Skip to content
Snippets Groups Projects
Commit ddbc8beb authored by Christopher Bohn's avatar Christopher Bohn :thinking:
Browse files

Added static typing to classes that interface directly with Gitlab API

parent d0fab743
Branches
No related tags found
No related merge requests found
from typing import ClassVar, Dict, Iterable, List, Union
from canvasapi import Canvas
from canvasapi.assignment import Assignment, AssignmentGroup
from canvasapi.course import Course
from canvasapi.group import Group, GroupCategory
from canvasapi.quiz import QuizSubmission, QuizSubmissionQuestion
from canvasapi.submission import Submission
from canvasapi.user import User
from config import Config
from typing import Iterable, List, Union, Dict
from config import Config
class CanvasSession:
__instance = None
__instance: ClassVar[Canvas] = None
@staticmethod
def get_session() -> Canvas:
......@@ -23,6 +25,8 @@ class CanvasSession:
class CanvasUser:
canvas_user: User
# @overload
# def __init__(self, user: int): # by NUID
# super().__init__()
......@@ -123,6 +127,8 @@ class CanvasUser:
class CanvasUserGroup:
canvas_user_group: Group
def __init__(self, group: Group):
super().__init__()
self.canvas_user_group = group
......@@ -206,6 +212,8 @@ class CanvasUserGroup:
class CanvasUserGroupSet: # aka, group_category
canvas_group_category: GroupCategory
def __init__(self, group_category: GroupCategory):
super().__init__()
self.canvas_group_category = group_category
......@@ -286,6 +294,8 @@ class CanvasUserGroupSet: # aka, group_category
class CanvasAssignment:
canvas_assignment: Assignment
def __init__(self, assignment: Assignment):
super().__init__()
self.canvas_assignment = assignment
......@@ -574,7 +584,8 @@ class CanvasAssignment:
class CanvasAssignmentGroup:
# from canvasapi.assignment import Assignment
canvas_assignment_group: AssignmentGroup
def __init__(self, group: AssignmentGroup):
super().__init__()
self.canvas_assignment_group = group
......@@ -621,6 +632,8 @@ class CanvasAssignmentGroup:
class CanvasCourse:
canvas_course: Course
def __init__(self, course_id: int):
self.canvas_course = CanvasSession.get_session().get_course(course_id)
......
from datetime import datetime
from typing import ClassVar, Dict, Iterable, List, Set, Union
import gitlab
from gitlab import Gitlab, MAINTAINER_ACCESS
from gitlab.v4.objects import Issue, Project, ProjectCommit, User
from config import Config
class GitlabSession:
__instance = None
__instance: ClassVar[Gitlab] = None
@staticmethod
def get_session():
def get_session() -> Gitlab:
if GitlabSession.__instance is None:
GitlabSession.__instance = gitlab.Gitlab(Config.gitlab_url, private_token=Config.gitlab_api_key)
GitlabSession.__instance = Gitlab(Config.gitlab_url, private_token=Config.gitlab_api_key)
return GitlabSession.__instance
class GitlabUser:
def __init__(self, user):
git_user: User
def __init__(self, user: Union[int, str, User]):
"""
Creates a User object, populating the backing git_user instance with the appropriate gitlab.User object
:param user: must be either a gitlab.User object, an integer representing the user ID, or a string containing
......@@ -30,34 +34,36 @@ class GitlabUser:
else:
self.git_user = user
def get_user_id(self):
def get_user_id(self) -> int:
return self.git_user.id
def get_name(self):
def get_name(self) -> str:
return self.git_user.name
def get_username(self):
def get_username(self) -> str:
return self.git_user.username
def get_site(self):
def get_site(self) -> str:
return self.git_user.web_url
def __repr__(self):
def __repr__(self) -> str:
username = self.get_username()
return f'@{username}'
def __eq__(self, other):
if isinstance(other, GitlabUser):
def __eq__(self, other: "GitlabUser") -> bool:
# if isinstance(other, GitlabUser):
return self.get_username() == other.get_username()
else:
return False
# else:
# return False
def __ne__(self, other):
def __ne__(self, other: "GitlabUser") -> bool:
return not self.__eq__(other)
class GitlabIssue:
def __init__(self, issue):
git_issue: Issue
def __init__(self, issue: Issue):
"""
Creates an Issue object, populating the backing git_issue instance with the appropriate gitlab.Issue object
:param issue: must be a gitlab.Issue object
......@@ -65,60 +71,60 @@ class GitlabIssue:
super().__init__()
self.git_issue = issue
def get_universal_issue_id(self):
def get_universal_issue_id(self) -> int:
"""
:return: universally-unique identifier
"""
return self.git_issue.id
def get_project_issue_id(self):
def get_project_issue_id(self) -> int:
"""
:return: project-unique identifier
"""
return self.git_issue.iid
def get_title(self):
def get_title(self) -> str:
"""
:return: issue's title
"""
return self.git_issue.title
def get_description(self):
def get_description(self) -> str:
"""
:return: issue's description
"""
return self.git_issue.description
def get_state(self):
def get_state(self) -> str:
"""
:return: opened or closed
"""
return self.git_issue.state
def get_created_at(self):
def get_created_at(self) -> datetime:
"""
:return: an "aware" datetime object representing the creation date/time
"""
created_at = self.git_issue.created_at
created_at: str = self.git_issue.created_at
if created_at[-1] in ('z', 'Z'): # Didn't encounter this problem with created_at
created_at = created_at[:-1] + '+00:00'
return datetime.fromisoformat(created_at)
def get_updated_at(self):
def get_updated_at(self) -> datetime:
"""
:return: an "aware" datetime object representing the last date/time the issue was updated
"""
updated_at = self.git_issue.updated_at
updated_at: str = self.git_issue.updated_at
if updated_at[-1] in ('z', 'Z'): # Didn't encounter this problem with updated_at
updated_at = updated_at[:-1] + '+00:00'
return datetime.fromisoformat(updated_at)
def get_closed_at(self):
def get_closed_at(self) -> Union[datetime, None]:
"""
:return: an "aware" datetime object representing the last date/time the issue was closed, or None if the issue
is open
"""
closed_at = self.git_issue.closed_at
closed_at: str = self.git_issue.closed_at
if closed_at is None:
return None
else:
......@@ -126,20 +132,20 @@ class GitlabIssue:
closed_at = closed_at[:-1] + '+00:00'
return datetime.fromisoformat(closed_at)
def get_labels(self):
def get_labels(self) -> Set[str]:
"""
:return: set of label names
"""
return set(self.git_issue.labels)
# return self.git_issue.labels.list(all=True)
def get_page(self):
def get_page(self) -> str:
"""
:return: HTTPS URL to issue's page
"""
return self.git_issue.web_url
def __repr__(self):
def __repr__(self) -> str:
issue_number = self.get_project_issue_id()
title = self.get_title()
return f'{issue_number}. {title}'
......@@ -171,34 +177,36 @@ class GitlabIssue:
class GitlabCommit:
gitlab_commit: ProjectCommit
# noinspection PyShadowingNames
def __init__(self, commit):
def __init__(self, commit: ProjectCommit):
super().__init__()
self.gitlab_commit = commit
def get_author(self):
def get_author(self) -> Dict[str, str]:
return {'name': self.gitlab_commit.author_name, 'email': self.gitlab_commit.author_email}
def get_timestamp(self):
def get_timestamp(self) -> str: # TODO: is a string (vs a datetime) really what we want?
return self.gitlab_commit.created_at
def get_message(self):
def get_message(self) -> str:
return self.gitlab_commit.message
def is_merge(self):
def is_merge(self) -> bool:
return len(self.gitlab_commit.parent_ids) > 1
# noinspection PyShadowingNames
def get_diffs(self):
diffs = []
gitlab_diffs = self.gitlab_commit.diff()
def get_diffs(self) -> List[Dict[str, Union[str, int]]]:
diffs: List[Dict[str, Union[str, int]]] = []
gitlab_diffs: List[Dict[str, str]] = self.gitlab_commit.diff()
for diff in gitlab_diffs:
diffs.append({'file': diff['new_path'], 'text': diff['diff'],
'+': diff['diff'].count('\n+'), '-': diff['diff'].count('\n-')})
return diffs
# noinspection PyShadowingNames
def get_diff_size(self):
def get_diff_size(self) -> int:
insertions = 0
deletions = 0
if not self.is_merge():
......@@ -229,7 +237,9 @@ class GitlabCommit:
class GitlabProject:
def __init__(self, project):
git_project: Project
def __init__(self, project: Union[int, str, Project]):
"""
Creates a Project object, populating the backing git_project instance with the appropriate gitlab.Project object
:param project: must be either a gitlab.Project object, an integer representing the project ID, or a string
......@@ -245,143 +255,144 @@ class GitlabProject:
self.git_project = GitlabSession.get_session().projects.get(project.id)
@staticmethod
def get_projects_by_group(group):
def get_projects_by_group(group: Union[int, str]) -> List["GitlabProject"]:
"""
:param group: must be either an integer representing the group ID, or a string containing the group's namespace
:return: list of projects in the specified group
"""
if isinstance(group, int): # by group id
gitlab_projects = GitlabSession.get_session().groups.get(group).projects.list(all=True)
gitlab_projects: Iterable[Project] = GitlabSession.get_session().groups.get(group).projects.list(all=True)
else: # isinstance(group, str): # by path
gitlab_projects = GitlabSession.get_session().groups.get(group).projects.list(all=True)
projects = []
projects: List[GitlabProject] = []
for project in gitlab_projects:
projects.append(GitlabProject(project))
return projects
@staticmethod
def get_projects_by_keyword(search_term):
gitlab_projects = GitlabSession.get_session().projects.list(search=search_term, all=True)
projects = []
def get_projects_by_keyword(search_term: str) -> List["GitlabProject"]:
gitlab_projects: Iterable[Project] = GitlabSession.get_session().projects.list(search=search_term, all=True)
projects: List[GitlabProject] = []
for project in gitlab_projects:
projects.append(GitlabProject(project))
return projects
@staticmethod
def create_project(project_name):
def create_project(project_name: str) -> "GitlabProject":
return GitlabProject(GitlabSession.get_session().projects.create({'name': project_name}))
@staticmethod
def create_project_in_group(group_name, project_name):
def create_project_in_group(group_name: str, project_name: str) -> "GitlabProject":
group_id = GitlabSession.get_session().groups.get(group_name).id
return GitlabProject(GitlabSession.get_session().projects.create({'name': project_name,
'namespace_id': group_id}))
def get_project_id(self):
def get_project_id(self) -> int:
return self.git_project.id
def get_name(self):
def get_name(self) -> str:
"""
:return: project name without namespace
"""
return self.git_project.name
def get_name_with_namespace(self):
def get_name_with_namespace(self) -> str:
"""
:return: project name with namespace, spaces around slashes
"""
return self.git_project.name_with_namespace
def get_path(self):
def get_path(self) -> str:
"""
:return: path without namespace (may differ from name if name has spaces)
"""
return self.git_project.path
def get_path_with_namespace(self):
def get_path_with_namespace(self) -> str:
"""
:return: path with namespace, no spaces around slashes
"""
return self.git_project.path_with_namespace
def get_cloning_url(self):
def get_cloning_url(self) -> str:
"""
:return: SSH URL to clone repository
"""
return self.git_project.ssh_url_to_repo
def get_site(self):
def get_site(self) -> str:
"""
:return: HTTPS URL to git site
"""
return self.git_project.web_url
def get_readme_url(self):
def get_readme_url(self) -> str:
"""
:return: HTTPS URL to README.md
"""
return self.git_project.readme_url
def get_visibility(self):
def get_visibility(self) -> str:
"""
:return: 'private', etc.
"""
return self.git_project.visibility
def get_creator(self):
def get_creator(self) -> GitlabUser:
"""
:return: User object backed by the gitlab.User object representing the user who created the repo
"""
return GitlabUser(self.git_project.creator_id)
def get_created_at(self):
def get_created_at(self) -> datetime:
"""
:return: an "aware" datetime object representing the creation date/time
"""
created_at = self.git_project.created_at
created_at: str = self.git_project.created_at
if created_at[-1] in ('z', 'Z'):
created_at = created_at[:-1] + '+00:00'
return datetime.fromisoformat(created_at)
def get_users(self):
def get_users(self) -> List[GitlabUser]:
"""
:return: List of User objects representing the project's members (not including inherited members)
"""
gitlab_users = self.git_project.members.list(all=True)
users = []
gitlab_users: Iterable[User] = self.git_project.members.list(all=True)
users: List[GitlabUser] = []
for user in gitlab_users:
users.append(GitlabUser(user))
return users
def get_all_users(self):
def get_all_users(self) -> List[GitlabUser]:
"""
:return: List of User objects representing all of the project's members (including inherited members)
"""
gitlab_users = self.git_project.members.all(all=True)
users = []
gitlab_users: Iterable[User] = self.git_project.members.all(all=True)
users: List[GitlabUser] = []
for user in gitlab_users:
users.append(GitlabUser(user))
return users
def add_user_as_maintainer(self, user):
self.git_project.members.create({'user_id': user.get_user_id(), 'access_level': gitlab.MAINTAINER_ACCESS})
def add_user_as_maintainer(self, user: GitlabUser) -> None:
self.git_project.members.create({'user_id': user.get_user_id(), 'access_level': MAINTAINER_ACCESS})
def get_issues(self):
def get_issues(self) -> List[GitlabIssue]:
"""
:return: List of Issue objects representing project's issues, sorted by creation date
"""
gitlab_issues = self.git_project.issues.list(order_by='created_at', sort='asc', all=True)
issues = []
gitlab_issues: Iterable[Issue] = self.git_project.issues.list(order_by='created_at', sort='asc', all=True)
issues: List[GitlabIssue] = []
for issue in gitlab_issues:
issues.append(GitlabIssue(issue))
return issues
def create_issue(self, title, description):
gitlab_issue = self.git_project.issues.create({'title': title, 'description': description})
def create_issue(self, title: str, description: str) -> GitlabIssue:
gitlab_issue: Issue = self.git_project.issues.create({'title': title, 'description': description})
return GitlabIssue(gitlab_issue)
# noinspection PyShadowingNames
def get_commits(self, branch_name='', after_date='1970-01-01', before_date='9999-12-31'):
def get_commits(self, branch_name: str = '', after_date: str = '1970-01-01', before_date: str = '9999-12-31') -> \
List[GitlabCommit]:
"""
:param branch_name: the branch to retrieve commits from; if an empty string (default) then retrieves commits
from all branches <-- NO, RETRIEVES FROM DEFAULT BRANCH; WILL NEED TO FIX THAT #TODO
......@@ -391,7 +402,7 @@ class GitlabProject:
having no latest-bound
:return: List of Commit objects representing the project's commits that meet the specified constraints
"""
filters = {}
filters: Dict[str, str] = {}
if branch_name != '':
filters['ref_name'] = branch_name
if after_date != '1970-01-01':
......@@ -399,21 +410,21 @@ class GitlabProject:
if before_date != '9999-12-31':
filters['until'] = before_date
if len(filters) == 0:
gitlab_commits = self.git_project.commits.list(all=True)
gitlab_commits: Iterable[ProjectCommit] = self.git_project.commits.list(all=True)
else:
gitlab_commits = self.git_project.commits.list(all=True, query_parameters=filters)
commits = []
commits: List[GitlabCommit] = []
for commit in gitlab_commits:
commits.append(GitlabCommit(commit))
return commits
def get_labels(self):
def get_labels(self) -> Set[str]:
"""
:return: set of label names
"""
return set(map(lambda label: label.name, self.git_project.labels.list()))
def __repr__(self):
def __repr__(self) -> str:
return self.get_name_with_namespace()
# other git_project fields:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment