git-recap 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- git_recap/providers/azure_fetcher.py +44 -37
- git_recap/providers/base_fetcher.py +9 -1
- git_recap/providers/github_fetcher.py +11 -6
- git_recap/providers/gitlab_fetcher.py +11 -7
- {git_recap-0.1.0.dist-info → git_recap-0.1.1.dist-info}/METADATA +1 -1
- git_recap-0.1.1.dist-info/RECORD +13 -0
- git_recap-0.1.0.dist-info/RECORD +0 -13
- {git_recap-0.1.0.dist-info → git_recap-0.1.1.dist-info}/WHEEL +0 -0
- {git_recap-0.1.0.dist-info → git_recap-0.1.1.dist-info}/licenses/LICENSE +0 -0
- {git_recap-0.1.0.dist-info → git_recap-0.1.1.dist-info}/top_level.txt +0 -0
|
@@ -13,10 +13,23 @@ class AzureFetcher(BaseFetcher):
|
|
|
13
13
|
self.connection = Connection(base_url=self.organization_url, creds=credentials)
|
|
14
14
|
self.core_client = self.connection.clients.get_core_client()
|
|
15
15
|
self.git_client = self.connection.clients.get_git_client()
|
|
16
|
-
|
|
16
|
+
self.repos = self.get_repos()
|
|
17
|
+
# Azure DevOps doesn't provide an affiliation filter;
|
|
18
|
+
# we'll iterate over all repos in each project.
|
|
17
19
|
if authors is None:
|
|
18
20
|
self.authors = []
|
|
19
|
-
|
|
21
|
+
|
|
22
|
+
def get_repos(self):
|
|
23
|
+
projects = self.core_client.get_projects().value
|
|
24
|
+
# Get all repositories in each project
|
|
25
|
+
repos = [self.git_client.get_repositories(project.id) for project in projects]
|
|
26
|
+
return repos
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def repos_names(self)->List[str]:
|
|
30
|
+
"to be implemented later"
|
|
31
|
+
...
|
|
32
|
+
|
|
20
33
|
def _filter_by_date(self, date_obj: datetime) -> bool:
|
|
21
34
|
if self.start_date and date_obj < self.start_date:
|
|
22
35
|
return False
|
|
@@ -32,39 +45,35 @@ class AzureFetcher(BaseFetcher):
|
|
|
32
45
|
def fetch_commits(self) -> List[Dict[str, Any]]:
|
|
33
46
|
entries = []
|
|
34
47
|
processed_commits = set()
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
for
|
|
39
|
-
|
|
48
|
+
for repo in self.repos:
|
|
49
|
+
if self.repo_filter and repo.name not in self.repo_filter:
|
|
50
|
+
continue
|
|
51
|
+
for author in self.authors:
|
|
52
|
+
try:
|
|
53
|
+
commits = self.git_client.get_commits(
|
|
54
|
+
project=repo.id,
|
|
55
|
+
repository_id=repo.id,
|
|
56
|
+
search_criteria={"author": author}
|
|
57
|
+
)
|
|
58
|
+
except Exception:
|
|
40
59
|
continue
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
sha
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"type": "commit",
|
|
59
|
-
"repo": repo.name,
|
|
60
|
-
"message": commit.comment.strip(),
|
|
61
|
-
"timestamp": commit_date,
|
|
62
|
-
"sha": sha,
|
|
63
|
-
}
|
|
64
|
-
entries.append(entry)
|
|
65
|
-
processed_commits.add(sha)
|
|
66
|
-
if self._stop_fetching(commit_date):
|
|
67
|
-
break
|
|
60
|
+
for commit in commits:
|
|
61
|
+
# Azure DevOps returns a commit with an 'author' property.
|
|
62
|
+
commit_date = commit.author.date # assumed datetime
|
|
63
|
+
if self._filter_by_date(commit_date):
|
|
64
|
+
sha = commit.commit_id
|
|
65
|
+
if sha not in processed_commits:
|
|
66
|
+
entry = {
|
|
67
|
+
"type": "commit",
|
|
68
|
+
"repo": repo.name,
|
|
69
|
+
"message": commit.comment.strip(),
|
|
70
|
+
"timestamp": commit_date,
|
|
71
|
+
"sha": sha,
|
|
72
|
+
}
|
|
73
|
+
entries.append(entry)
|
|
74
|
+
processed_commits.add(sha)
|
|
75
|
+
if self._stop_fetching(commit_date):
|
|
76
|
+
break
|
|
68
77
|
return entries
|
|
69
78
|
|
|
70
79
|
def fetch_pull_requests(self) -> List[Dict[str, Any]]:
|
|
@@ -130,9 +139,8 @@ class AzureFetcher(BaseFetcher):
|
|
|
130
139
|
|
|
131
140
|
def fetch_issues(self) -> List[Dict[str, Any]]:
|
|
132
141
|
entries = []
|
|
133
|
-
# Azure DevOps issues are typically tracked as Work Items.
|
|
134
142
|
wit_client = self.connection.clients.get_work_item_tracking_client()
|
|
135
|
-
# Query work items for each author
|
|
143
|
+
# Query work items for each author using a simplified WIQL query.
|
|
136
144
|
for author in self.authors:
|
|
137
145
|
wiql = f"SELECT [System.Id], [System.Title], [System.CreatedDate] FROM WorkItems WHERE [System.AssignedTo] CONTAINS '{author}'"
|
|
138
146
|
try:
|
|
@@ -141,7 +149,6 @@ class AzureFetcher(BaseFetcher):
|
|
|
141
149
|
continue
|
|
142
150
|
for item_ref in query_result:
|
|
143
151
|
work_item = wit_client.get_work_item(item_ref.id)
|
|
144
|
-
# The created date is a string; convert it to a datetime.
|
|
145
152
|
created_date = datetime.fromisoformat(work_item.fields["System.CreatedDate"])
|
|
146
153
|
if self._filter_by_date(created_date):
|
|
147
154
|
entry = {
|
|
@@ -25,6 +25,11 @@ class BaseFetcher(ABC):
|
|
|
25
25
|
self.repo_filter = repo_filter or []
|
|
26
26
|
self.limit = -1
|
|
27
27
|
self.authors = [] if authors is None else authors
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def repos_names(self)->List[str]:
|
|
32
|
+
pass
|
|
28
33
|
|
|
29
34
|
@abstractmethod
|
|
30
35
|
def fetch_commits(self) -> List[str]:
|
|
@@ -46,7 +51,10 @@ class BaseFetcher(ABC):
|
|
|
46
51
|
"""
|
|
47
52
|
commit_entries = self.fetch_commits()
|
|
48
53
|
pr_entries = self.fetch_pull_requests()
|
|
49
|
-
|
|
54
|
+
try:
|
|
55
|
+
issue_entries = self.fetch_issues()
|
|
56
|
+
except Exception as e:
|
|
57
|
+
issue_entries = []
|
|
50
58
|
|
|
51
59
|
all_entries = pr_entries + commit_entries + issue_entries
|
|
52
60
|
|
|
@@ -7,9 +7,14 @@ class GitHubFetcher(BaseFetcher):
|
|
|
7
7
|
def __init__(self, pat: str, start_date=None, end_date=None, repo_filter=None, authors=None):
|
|
8
8
|
super().__init__(pat, start_date, end_date, repo_filter, authors)
|
|
9
9
|
self.github = Github(self.pat)
|
|
10
|
-
self.user = self.github.get_user()
|
|
10
|
+
self.user = self.github.get_user()
|
|
11
|
+
self.repos = self.user.get_repos(affiliation="owner,collaborator,organization_member")
|
|
11
12
|
self.authors.append(self.user.login)
|
|
12
13
|
|
|
14
|
+
@property
|
|
15
|
+
def repos_names(self)->List[str]:
|
|
16
|
+
return [repo.name for repo in self.repos]
|
|
17
|
+
|
|
13
18
|
def _stop_fetching(self, date_obj: datetime) -> bool:
|
|
14
19
|
if self.start_date and date_obj < self.start_date:
|
|
15
20
|
return True
|
|
@@ -25,8 +30,7 @@ class GitHubFetcher(BaseFetcher):
|
|
|
25
30
|
def fetch_commits(self) -> List[Dict[str, Any]]:
|
|
26
31
|
entries = []
|
|
27
32
|
processed_commits = set()
|
|
28
|
-
|
|
29
|
-
for repo in repos:
|
|
33
|
+
for repo in self.repos:
|
|
30
34
|
if self.repo_filter and repo.name not in self.repo_filter:
|
|
31
35
|
continue
|
|
32
36
|
for author in self.authors:
|
|
@@ -53,13 +57,13 @@ class GitHubFetcher(BaseFetcher):
|
|
|
53
57
|
entries = []
|
|
54
58
|
# Maintain a local set to skip duplicate commits already captured in a PR.
|
|
55
59
|
processed_pr_commits = set()
|
|
56
|
-
repos
|
|
57
|
-
for repo in repos:
|
|
60
|
+
# Retrieve repos where you're owner, a collaborator, or an organization member.
|
|
61
|
+
for repo in self.repos:
|
|
58
62
|
if self.repo_filter and repo.name not in self.repo_filter:
|
|
59
63
|
continue
|
|
60
64
|
pulls = repo.get_pulls(state='all')
|
|
61
65
|
for i, pr in enumerate(pulls, start=1):
|
|
62
|
-
if pr.user.login not in
|
|
66
|
+
if pr.user.login not in self.authors:
|
|
63
67
|
continue
|
|
64
68
|
pr_date = pr.updated_at # alternatively, use pr.created_at
|
|
65
69
|
if not self._filter_by_date(pr_date):
|
|
@@ -97,6 +101,7 @@ class GitHubFetcher(BaseFetcher):
|
|
|
97
101
|
break
|
|
98
102
|
return entries
|
|
99
103
|
|
|
104
|
+
|
|
100
105
|
def fetch_issues(self) -> List[Dict[str, Any]]:
|
|
101
106
|
entries = []
|
|
102
107
|
issues = self.user.get_issues()
|
|
@@ -8,12 +8,19 @@ class GitLabFetcher(BaseFetcher):
|
|
|
8
8
|
super().__init__(pat, start_date, end_date, repo_filter, authors)
|
|
9
9
|
self.gl = gitlab.Gitlab(url, private_token=self.pat)
|
|
10
10
|
self.gl.auth()
|
|
11
|
+
# Instead of only owned projects, retrieve projects where you're a member.
|
|
12
|
+
self.projects = self.gl.projects.list(membership=True, all=True)
|
|
11
13
|
# Default to the authenticated user's username if no authors are provided.
|
|
12
14
|
if authors is None:
|
|
13
15
|
self.authors = [self.gl.user.username]
|
|
14
16
|
else:
|
|
15
17
|
self.authors = authors
|
|
16
18
|
|
|
19
|
+
@property
|
|
20
|
+
def repos_names(self)->List[str]:
|
|
21
|
+
"to be implemented later"
|
|
22
|
+
return [project.name for project in self.projects]
|
|
23
|
+
|
|
17
24
|
def _filter_by_date(self, date_str: str) -> bool:
|
|
18
25
|
date_obj = datetime.fromisoformat(date_str)
|
|
19
26
|
if self.start_date and date_obj < self.start_date:
|
|
@@ -31,8 +38,7 @@ class GitLabFetcher(BaseFetcher):
|
|
|
31
38
|
def fetch_commits(self) -> List[Dict[str, Any]]:
|
|
32
39
|
entries = []
|
|
33
40
|
processed_commits = set()
|
|
34
|
-
|
|
35
|
-
for project in projects:
|
|
41
|
+
for project in self.projects:
|
|
36
42
|
if self.repo_filter and project.name not in self.repo_filter:
|
|
37
43
|
continue
|
|
38
44
|
for author in self.authors:
|
|
@@ -59,11 +65,10 @@ class GitLabFetcher(BaseFetcher):
|
|
|
59
65
|
def fetch_pull_requests(self) -> List[Dict[str, Any]]:
|
|
60
66
|
entries = []
|
|
61
67
|
processed_pr_commits = set()
|
|
62
|
-
|
|
63
|
-
for project in projects:
|
|
68
|
+
for project in self.projects:
|
|
64
69
|
if self.repo_filter and project.name not in self.repo_filter:
|
|
65
70
|
continue
|
|
66
|
-
# Fetch merge requests (
|
|
71
|
+
# Fetch merge requests (GitLab's pull requests)
|
|
67
72
|
merge_requests = project.mergerequests.list(state='all', all=True)
|
|
68
73
|
for mr in merge_requests:
|
|
69
74
|
if mr.author['username'] not in self.authors:
|
|
@@ -105,8 +110,7 @@ class GitLabFetcher(BaseFetcher):
|
|
|
105
110
|
|
|
106
111
|
def fetch_issues(self) -> List[Dict[str, Any]]:
|
|
107
112
|
entries = []
|
|
108
|
-
|
|
109
|
-
for project in projects:
|
|
113
|
+
for project in self.projects:
|
|
110
114
|
if self.repo_filter and project.name not in self.repo_filter:
|
|
111
115
|
continue
|
|
112
116
|
issues = project.issues.list(assignee_id=self.gl.user.id)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
git_recap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
git_recap/fetcher.py,sha256=oRlenzd9OsiBkCtpUgSZGlUoUnVpdkolUZhFOF1USBs,2677
|
|
3
|
+
git_recap/utils.py,sha256=mAGZ6bNklBmE-qpA6RG17_Bka8WRXzjLRAAGS9p3QXk,3646
|
|
4
|
+
git_recap/providers/__init__.py,sha256=amk4xSzKOkjW8RaorBSJoiDnjiyFHoz8jFrgFM6Wx_o,256
|
|
5
|
+
git_recap/providers/azure_fetcher.py,sha256=PfPEeWTtx1Fqa1jClFPKqXhY5Yvs8MyEkSJkLAl4QvQ,7241
|
|
6
|
+
git_recap/providers/base_fetcher.py,sha256=SS35FP8B9_RwnU6CnMbcV9g5_YRWp7v-S7pwbt402Ng,3067
|
|
7
|
+
git_recap/providers/github_fetcher.py,sha256=rDb00RVy2NMpwv8s8Ry1-UjS6wf1Sh2Ndl0xKOZXMxU,4998
|
|
8
|
+
git_recap/providers/gitlab_fetcher.py,sha256=ch1V9O-MPv5_nyg9yLs6EC9e-yqsFXBXMm6xVevWzuQ,5396
|
|
9
|
+
git_recap-0.1.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
10
|
+
git_recap-0.1.1.dist-info/METADATA,sha256=wdnWHDjNfHu9DxxQXKMsmb7pkvWHKN1Wt14MNdppxMQ,4721
|
|
11
|
+
git_recap-0.1.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
12
|
+
git_recap-0.1.1.dist-info/top_level.txt,sha256=1JUKd3WPB8c3LcD1deIW-1UTmYzA0zJqwugAz72YZ_o,10
|
|
13
|
+
git_recap-0.1.1.dist-info/RECORD,,
|
git_recap-0.1.0.dist-info/RECORD
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
git_recap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
git_recap/fetcher.py,sha256=oRlenzd9OsiBkCtpUgSZGlUoUnVpdkolUZhFOF1USBs,2677
|
|
3
|
-
git_recap/utils.py,sha256=mAGZ6bNklBmE-qpA6RG17_Bka8WRXzjLRAAGS9p3QXk,3646
|
|
4
|
-
git_recap/providers/__init__.py,sha256=amk4xSzKOkjW8RaorBSJoiDnjiyFHoz8jFrgFM6Wx_o,256
|
|
5
|
-
git_recap/providers/azure_fetcher.py,sha256=KMqXdIKYGp_EC2BHKhqBIfdXJ7nENx6OSSdqtySa618,7270
|
|
6
|
-
git_recap/providers/base_fetcher.py,sha256=d8QuadaVaGxksbImrnAV10k21i21OSxVopOQYDbuIcA,2902
|
|
7
|
-
git_recap/providers/github_fetcher.py,sha256=yq5MF4VoZww5-yKxyKb270X1EZsRqtDc9P6XKBVs6s0,4770
|
|
8
|
-
git_recap/providers/gitlab_fetcher.py,sha256=2ivuBdVJVgT7qGqTakkMYkkOHd2veyh54in6ttr59FM,5284
|
|
9
|
-
git_recap-0.1.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
10
|
-
git_recap-0.1.0.dist-info/METADATA,sha256=a1A8NFSnAhjd6haIP5SpzzNbIdKjUSpS92OLJNrS6b8,4721
|
|
11
|
-
git_recap-0.1.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
12
|
-
git_recap-0.1.0.dist-info/top_level.txt,sha256=1JUKd3WPB8c3LcD1deIW-1UTmYzA0zJqwugAz72YZ_o,10
|
|
13
|
-
git_recap-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|