Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
S
scripts
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container registry
Model registry
Operate
Environments
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
CSCE 361
scripts
Commits
ddbc8beb
Commit
ddbc8beb
authored
Jan 15, 2020
by
Christopher Bohn
Browse files
Options
Downloads
Patches
Plain Diff
Added static typing to classes that interface directly with Gitlab API
parent
d0fab743
Branches
Branches containing commit
No related tags found
No related merge requests found
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
api/canvas_classes.py
+17
-4
17 additions, 4 deletions
api/canvas_classes.py
api/gitlab_classes.py
+91
-80
91 additions, 80 deletions
api/gitlab_classes.py
with
108 additions
and
84 deletions
api/canvas_classes.py
+
17
−
4
View file @
ddbc8beb
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
typin
g
import
Iterable
,
List
,
Union
,
Dict
from
confi
g
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
)
...
...
This diff is collapsed.
Click to expand it.
api/gitlab_classes.py
+
91
−
80
View file @
ddbc8beb
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:
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment