git-recap 0.1.3__tar.gz → 0.1.5__tar.gz
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-0.1.3 → git_recap-0.1.5}/PKG-INFO +9 -1
- {git_recap-0.1.3 → git_recap-0.1.5}/README.md +9 -1
- {git_recap-0.1.3 → git_recap-0.1.5}/git_recap/providers/azure_fetcher.py +158 -5
- git_recap-0.1.5/git_recap/providers/base_fetcher.py +209 -0
- git_recap-0.1.5/git_recap/providers/github_fetcher.py +408 -0
- {git_recap-0.1.3 → git_recap-0.1.5}/git_recap/providers/gitlab_fetcher.py +126 -6
- {git_recap-0.1.3 → git_recap-0.1.5}/git_recap/providers/url_fetcher.py +81 -13
- git_recap-0.1.5/git_recap/utils.py +147 -0
- {git_recap-0.1.3 → git_recap-0.1.5}/git_recap.egg-info/PKG-INFO +9 -1
- {git_recap-0.1.3 → git_recap-0.1.5}/setup.py +1 -1
- {git_recap-0.1.3 → git_recap-0.1.5}/tests/test_dummy_parser.py +13 -1
- git_recap-0.1.5/tests/test_parser.py +630 -0
- git_recap-0.1.3/git_recap/providers/base_fetcher.py +0 -87
- git_recap-0.1.3/git_recap/providers/github_fetcher.py +0 -120
- git_recap-0.1.3/git_recap/utils.py +0 -93
- git_recap-0.1.3/tests/test_parser.py +0 -52
- {git_recap-0.1.3 → git_recap-0.1.5}/LICENSE +0 -0
- {git_recap-0.1.3 → git_recap-0.1.5}/git_recap/__init__.py +0 -0
- {git_recap-0.1.3 → git_recap-0.1.5}/git_recap/fetcher.py +0 -0
- {git_recap-0.1.3 → git_recap-0.1.5}/git_recap/providers/__init__.py +0 -0
- {git_recap-0.1.3 → git_recap-0.1.5}/git_recap.egg-info/SOURCES.txt +0 -0
- {git_recap-0.1.3 → git_recap-0.1.5}/git_recap.egg-info/dependency_links.txt +0 -0
- {git_recap-0.1.3 → git_recap-0.1.5}/git_recap.egg-info/requires.txt +0 -0
- {git_recap-0.1.3 → git_recap-0.1.5}/git_recap.egg-info/top_level.txt +0 -0
- {git_recap-0.1.3 → git_recap-0.1.5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: git-recap
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: A modular Python tool that aggregates and formats user-authored messages from repositories.
|
|
5
5
|
Author: Bruno V.
|
|
6
6
|
Author-email: bruno.vitorino@tecnico.ulisboa.pt
|
|
@@ -21,8 +21,16 @@ Dynamic: license-file
|
|
|
21
21
|
Dynamic: requires-dist
|
|
22
22
|
Dynamic: summary
|
|
23
23
|
|
|
24
|
+
<a href="https://www.uneed.best/tool/gitrecap">
|
|
25
|
+
<img src="https://www.uneed.best/POTD2A.png" style="width: 250px;" alt="Uneed POTD2 Badge" />
|
|
26
|
+
</a>
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
24
30
|
# Git Recap
|
|
25
31
|
|
|
32
|
+
🎉 **Featured in Uneed's Latest Newsletter as a Staff Pick!** 🎉
|
|
33
|
+
|
|
26
34
|
Git Recap is a modular Python tool that aggregates and formats user-authored messages from repositories hosted on GitHub, Azure DevOps, and GitLab. It fetches commit messages, pull requests (along with their associated commits), and issues, then consolidates and sorts these events into a clear, chronological summary. This summary is output as a plain text string that can serve as context for large language models or other analysis tools.
|
|
27
35
|
|
|
28
36
|
## Features
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
<a href="https://www.uneed.best/tool/gitrecap">
|
|
2
|
+
<img src="https://www.uneed.best/POTD2A.png" style="width: 250px;" alt="Uneed POTD2 Badge" />
|
|
3
|
+
</a>
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
1
7
|
# Git Recap
|
|
2
8
|
|
|
9
|
+
🎉 **Featured in Uneed's Latest Newsletter as a Staff Pick!** 🎉
|
|
10
|
+
|
|
3
11
|
Git Recap is a modular Python tool that aggregates and formats user-authored messages from repositories hosted on GitHub, Azure DevOps, and GitLab. It fetches commit messages, pull requests (along with their associated commits), and issues, then consolidates and sorts these events into a clear, chronological summary. This summary is output as a plain text string that can serve as context for large language models or other analysis tools.
|
|
4
12
|
|
|
5
13
|
## Features
|
|
@@ -110,4 +118,4 @@ This project is licensed under the terms of the [MIT License](LICENSE).
|
|
|
110
118
|
|
|
111
119
|
- [PyGitHub](https://pygithub.readthedocs.io/en/stable/)
|
|
112
120
|
- [Azure DevOps Python API](https://github.com/microsoft/azure-devops-python-api)
|
|
113
|
-
- [python-gitlab](https://python-gitlab.readthedocs.io/)
|
|
121
|
+
- [python-gitlab](https://python-gitlab.readthedocs.io/)
|
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
from azure.devops.connection import Connection
|
|
2
2
|
from msrest.authentication import BasicAuthentication
|
|
3
3
|
from datetime import datetime
|
|
4
|
-
from typing import List, Dict, Any
|
|
4
|
+
from typing import List, Dict, Any, Optional
|
|
5
5
|
from git_recap.providers.base_fetcher import BaseFetcher
|
|
6
6
|
|
|
7
7
|
class AzureFetcher(BaseFetcher):
|
|
8
|
+
"""
|
|
9
|
+
Fetcher implementation for Azure DevOps repositories.
|
|
10
|
+
|
|
11
|
+
Supports fetching commits, pull requests, and issues.
|
|
12
|
+
Release fetching is not supported and will raise NotImplementedError.
|
|
13
|
+
"""
|
|
14
|
+
|
|
8
15
|
def __init__(self, pat: str, organization_url: str, start_date=None, end_date=None, repo_filter=None, authors=None):
|
|
9
|
-
|
|
16
|
+
"""
|
|
17
|
+
Initialize the AzureFetcher.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
pat (str): Personal Access Token for Azure DevOps.
|
|
21
|
+
organization_url (str): The Azure DevOps organization URL.
|
|
22
|
+
start_date (datetime, optional): Start date for filtering entries.
|
|
23
|
+
end_date (datetime, optional): End date for filtering entries.
|
|
24
|
+
repo_filter (List[str], optional): List of repository names to filter.
|
|
25
|
+
authors (List[str], optional): List of author identifiers (e.g., email or unique id).
|
|
26
|
+
"""
|
|
10
27
|
super().__init__(pat, start_date, end_date, repo_filter, authors)
|
|
11
28
|
self.organization_url = organization_url
|
|
12
29
|
credentials = BasicAuthentication('', self.pat)
|
|
@@ -20,17 +37,37 @@ class AzureFetcher(BaseFetcher):
|
|
|
20
37
|
self.authors = []
|
|
21
38
|
|
|
22
39
|
def get_repos(self):
|
|
40
|
+
"""
|
|
41
|
+
Retrieve all repositories in all projects for the organization.
|
|
42
|
+
Returns:
|
|
43
|
+
List of repository objects.
|
|
44
|
+
"""
|
|
23
45
|
projects = self.core_client.get_projects().value
|
|
24
46
|
# Get all repositories in each project
|
|
25
47
|
repos = [self.git_client.get_repositories(project.id) for project in projects]
|
|
26
48
|
return repos
|
|
27
49
|
|
|
28
50
|
@property
|
|
29
|
-
def repos_names(self)->List[str]:
|
|
30
|
-
"
|
|
51
|
+
def repos_names(self) -> List[str]:
|
|
52
|
+
"""
|
|
53
|
+
Return the list of repository names.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
List[str]: List of repository names.
|
|
57
|
+
"""
|
|
58
|
+
# To be implemented if needed for UI or listing.
|
|
31
59
|
...
|
|
32
60
|
|
|
33
61
|
def _filter_by_date(self, date_obj: datetime) -> bool:
|
|
62
|
+
"""
|
|
63
|
+
Check if a datetime object is within the configured date range.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
date_obj (datetime): The datetime to check.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
bool: True if within range, False otherwise.
|
|
70
|
+
"""
|
|
34
71
|
if self.start_date and date_obj < self.start_date:
|
|
35
72
|
return False
|
|
36
73
|
if self.end_date and date_obj > self.end_date:
|
|
@@ -38,11 +75,26 @@ class AzureFetcher(BaseFetcher):
|
|
|
38
75
|
return True
|
|
39
76
|
|
|
40
77
|
def _stop_fetching(self, date_obj: datetime) -> bool:
|
|
78
|
+
"""
|
|
79
|
+
Determine if fetching should stop based on the date.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
date_obj (datetime): The datetime to check.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
bool: True if should stop, False otherwise.
|
|
86
|
+
"""
|
|
41
87
|
if self.start_date and date_obj < self.start_date:
|
|
42
88
|
return True
|
|
43
89
|
return False
|
|
44
90
|
|
|
45
91
|
def fetch_commits(self) -> List[Dict[str, Any]]:
|
|
92
|
+
"""
|
|
93
|
+
Fetch commits for all repositories and authors.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
List[Dict[str, Any]]: List of commit entries.
|
|
97
|
+
"""
|
|
46
98
|
entries = []
|
|
47
99
|
processed_commits = set()
|
|
48
100
|
for repo in self.repos:
|
|
@@ -77,6 +129,12 @@ class AzureFetcher(BaseFetcher):
|
|
|
77
129
|
return entries
|
|
78
130
|
|
|
79
131
|
def fetch_pull_requests(self) -> List[Dict[str, Any]]:
|
|
132
|
+
"""
|
|
133
|
+
Fetch pull requests and their associated commits for all repositories and authors.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
List[Dict[str, Any]]: List of pull request and commit_from_pr entries.
|
|
137
|
+
"""
|
|
80
138
|
entries = []
|
|
81
139
|
processed_pr_commits = set()
|
|
82
140
|
projects = self.core_client.get_projects().value
|
|
@@ -138,6 +196,12 @@ class AzureFetcher(BaseFetcher):
|
|
|
138
196
|
return entries
|
|
139
197
|
|
|
140
198
|
def fetch_issues(self) -> List[Dict[str, Any]]:
|
|
199
|
+
"""
|
|
200
|
+
Fetch issues (work items) assigned to the configured authors.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
List[Dict[str, Any]]: List of issue entries.
|
|
204
|
+
"""
|
|
141
205
|
entries = []
|
|
142
206
|
wit_client = self.connection.clients.get_work_item_tracking_client()
|
|
143
207
|
# Query work items for each author using a simplified WIQL query.
|
|
@@ -160,4 +224,93 @@ class AzureFetcher(BaseFetcher):
|
|
|
160
224
|
entries.append(entry)
|
|
161
225
|
if self._stop_fetching(created_date):
|
|
162
226
|
break
|
|
163
|
-
return entries
|
|
227
|
+
return entries
|
|
228
|
+
|
|
229
|
+
def fetch_releases(self) -> List[Dict[str, Any]]:
|
|
230
|
+
"""
|
|
231
|
+
Fetch releases for Azure DevOps repositories.
|
|
232
|
+
|
|
233
|
+
Not implemented for Azure DevOps.
|
|
234
|
+
|
|
235
|
+
Raises:
|
|
236
|
+
NotImplementedError: Always, since release fetching is not supported for AzureFetcher.
|
|
237
|
+
"""
|
|
238
|
+
# If Azure DevOps release fetching is supported in the future, implement logic here.
|
|
239
|
+
raise NotImplementedError("Release fetching is not supported for Azure DevOps (AzureFetcher).")
|
|
240
|
+
|
|
241
|
+
def get_branches(self) -> List[str]:
|
|
242
|
+
"""
|
|
243
|
+
Get all branches in the repository.
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
List[str]: List of branch names.
|
|
247
|
+
|
|
248
|
+
Raises:
|
|
249
|
+
NotImplementedError: Always, since branch listing is not yet implemented for AzureFetcher.
|
|
250
|
+
"""
|
|
251
|
+
# TODO: Implement get_branches() for Azure DevOps support
|
|
252
|
+
# This would use: git_client.get_branches(repository_id, project)
|
|
253
|
+
# and extract branch names from the returned objects
|
|
254
|
+
raise NotImplementedError("Branch listing is not yet implemented for Azure DevOps (AzureFetcher).")
|
|
255
|
+
|
|
256
|
+
def get_valid_target_branches(self, source_branch: str) -> List[str]:
|
|
257
|
+
"""
|
|
258
|
+
Get branches that can receive a pull request from the source branch.
|
|
259
|
+
|
|
260
|
+
Validates that the source branch exists, filters out branches with existing
|
|
261
|
+
open PRs from source, excludes the source branch itself, and optionally
|
|
262
|
+
checks if source is ahead of target.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
source_branch (str): The source branch name.
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
List[str]: List of valid target branch names.
|
|
269
|
+
|
|
270
|
+
Raises:
|
|
271
|
+
NotImplementedError: Always, since PR target validation is not yet implemented for AzureFetcher.
|
|
272
|
+
"""
|
|
273
|
+
# TODO: Implement get_valid_target_branches() for Azure DevOps support
|
|
274
|
+
# This would require:
|
|
275
|
+
# 1. Verify source_branch exists using git_client.get_branch()
|
|
276
|
+
# 2. Get all branches using get_branches()
|
|
277
|
+
# 3. Filter out source branch
|
|
278
|
+
# 4. Check for existing pull requests using git_client.get_pull_requests()
|
|
279
|
+
# 5. Filter out branches with existing open PRs from source
|
|
280
|
+
# 6. Optionally check branch policies and protection rules
|
|
281
|
+
raise NotImplementedError("Pull request target branch validation is not yet implemented for Azure DevOps (AzureFetcher).")
|
|
282
|
+
|
|
283
|
+
def create_pull_request(
|
|
284
|
+
self,
|
|
285
|
+
head_branch: str,
|
|
286
|
+
base_branch: str,
|
|
287
|
+
title: str,
|
|
288
|
+
body: str,
|
|
289
|
+
draft: bool = False,
|
|
290
|
+
reviewers: Optional[List[str]] = None,
|
|
291
|
+
assignees: Optional[List[str]] = None,
|
|
292
|
+
labels: Optional[List[str]] = None
|
|
293
|
+
) -> Dict[str, Any]:
|
|
294
|
+
"""
|
|
295
|
+
Create a pull request between two branches with optional metadata.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
head_branch: Source branch for the PR.
|
|
299
|
+
base_branch: Target branch for the PR.
|
|
300
|
+
title: PR title.
|
|
301
|
+
body: PR description.
|
|
302
|
+
draft: Whether to create as draft PR (default: False).
|
|
303
|
+
reviewers: List of reviewer usernames (optional).
|
|
304
|
+
assignees: List of assignee usernames (optional).
|
|
305
|
+
labels: List of label names (optional).
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
Dict[str, Any]: Dictionary containing PR metadata (url, number, state, success) or error information.
|
|
309
|
+
|
|
310
|
+
Raises:
|
|
311
|
+
NotImplementedError: Always, since PR creation is not yet implemented for AzureFetcher.
|
|
312
|
+
"""
|
|
313
|
+
# TODO: Implement create_pull_request() for Azure DevOps support
|
|
314
|
+
# This would use: git_client.create_pull_request() with appropriate parameters
|
|
315
|
+
# Would need to handle reviewers, work item links (assignees), labels, and draft status
|
|
316
|
+
raise NotImplementedError("Pull request creation is not yet implemented for Azure DevOps (AzureFetcher).")
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
|
+
from typing import List, Optional, Dict, Any
|
|
4
|
+
|
|
5
|
+
class BaseFetcher(ABC):
|
|
6
|
+
def __init__(
|
|
7
|
+
self,
|
|
8
|
+
pat: str,
|
|
9
|
+
start_date: Optional[datetime] = None,
|
|
10
|
+
end_date: Optional[datetime] = None,
|
|
11
|
+
repo_filter: Optional[List[str]] = None,
|
|
12
|
+
authors: Optional[List[str]] = None
|
|
13
|
+
):
|
|
14
|
+
self.pat = pat
|
|
15
|
+
if start_date is not None:
|
|
16
|
+
self.start_date = start_date if start_date.tzinfo is not None else start_date.replace(tzinfo=timezone.utc)
|
|
17
|
+
else:
|
|
18
|
+
self.start_date = None
|
|
19
|
+
|
|
20
|
+
if end_date is not None:
|
|
21
|
+
self.end_date = end_date if end_date.tzinfo is not None else end_date.replace(tzinfo=timezone.utc)
|
|
22
|
+
else:
|
|
23
|
+
self.end_date = None
|
|
24
|
+
|
|
25
|
+
self.repo_filter = repo_filter or []
|
|
26
|
+
self.limit = -1
|
|
27
|
+
self.authors = [] if authors is None else authors
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def repos_names(self) -> List[str]:
|
|
32
|
+
"""
|
|
33
|
+
Return the list of repository names accessible to this fetcher.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
List[str]: List of repository names.
|
|
37
|
+
"""
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
@abstractmethod
|
|
41
|
+
def fetch_commits(self) -> List[Dict[str, Any]]:
|
|
42
|
+
"""
|
|
43
|
+
Fetch commit entries for the configured repositories and authors.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
List[Dict[str, Any]]: List of commit entries.
|
|
47
|
+
"""
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
def fetch_pull_requests(self) -> List[Dict[str, Any]]:
|
|
52
|
+
"""
|
|
53
|
+
Fetch pull request entries for the configured repositories and authors.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
List[Dict[str, Any]]: List of pull request entries.
|
|
57
|
+
"""
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
@abstractmethod
|
|
61
|
+
def fetch_issues(self) -> List[Dict[str, Any]]:
|
|
62
|
+
"""
|
|
63
|
+
Fetch issue entries for the configured repositories and authors.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
List[Dict[str, Any]]: List of issue entries.
|
|
67
|
+
"""
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
@abstractmethod
|
|
71
|
+
def fetch_releases(self) -> List[Dict[str, Any]]:
|
|
72
|
+
"""
|
|
73
|
+
Fetch releases for all repositories accessible to this fetcher.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
List[Dict[str, Any]]: List of releases, each as a structured dictionary.
|
|
77
|
+
The dictionary should include at least:
|
|
78
|
+
- tag_name: str
|
|
79
|
+
- name: str
|
|
80
|
+
- repo: str
|
|
81
|
+
- author: str
|
|
82
|
+
- published_at: datetime
|
|
83
|
+
- created_at: datetime
|
|
84
|
+
- draft: bool
|
|
85
|
+
- prerelease: bool
|
|
86
|
+
- body: str
|
|
87
|
+
- assets: List[Dict[str, Any]] (if available)
|
|
88
|
+
Raises:
|
|
89
|
+
NotImplementedError: If the provider does not support release fetching.
|
|
90
|
+
"""
|
|
91
|
+
raise NotImplementedError("Release fetching is not implemented for this provider.")
|
|
92
|
+
|
|
93
|
+
@abstractmethod
|
|
94
|
+
def get_branches(self) -> List[str]:
|
|
95
|
+
"""
|
|
96
|
+
Get all branches in the repository.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
List[str]: List of branch names.
|
|
100
|
+
|
|
101
|
+
Raises:
|
|
102
|
+
NotImplementedError: Subclasses must implement this method.
|
|
103
|
+
"""
|
|
104
|
+
raise NotImplementedError("Subclasses must implement get_branches() to return all repository branches")
|
|
105
|
+
|
|
106
|
+
@abstractmethod
|
|
107
|
+
def get_valid_target_branches(self, source_branch: str) -> List[str]:
|
|
108
|
+
"""
|
|
109
|
+
Get branches that can receive a pull request from the source branch.
|
|
110
|
+
|
|
111
|
+
Validates that the source branch exists, filters out branches with existing
|
|
112
|
+
open PRs from source, excludes the source branch itself, and optionally
|
|
113
|
+
checks if source is ahead of target.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
source_branch (str): The source branch name.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
List[str]: List of valid target branch names.
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
NotImplementedError: Subclasses must implement this method.
|
|
123
|
+
"""
|
|
124
|
+
raise NotImplementedError("Subclasses must implement get_valid_target_branches() to return valid PR target branches for the given source branch")
|
|
125
|
+
|
|
126
|
+
@abstractmethod
|
|
127
|
+
def create_pull_request(
|
|
128
|
+
self,
|
|
129
|
+
head_branch: str,
|
|
130
|
+
base_branch: str,
|
|
131
|
+
title: str,
|
|
132
|
+
body: str,
|
|
133
|
+
draft: bool = False,
|
|
134
|
+
reviewers: Optional[List[str]] = None,
|
|
135
|
+
assignees: Optional[List[str]] = None,
|
|
136
|
+
labels: Optional[List[str]] = None
|
|
137
|
+
) -> Dict[str, Any]:
|
|
138
|
+
"""
|
|
139
|
+
Create a pull request between two branches with optional metadata.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
head_branch: Source branch for the PR.
|
|
143
|
+
base_branch: Target branch for the PR.
|
|
144
|
+
title: PR title.
|
|
145
|
+
body: PR description.
|
|
146
|
+
draft: Whether to create as draft PR (default: False).
|
|
147
|
+
reviewers: List of reviewer usernames (optional).
|
|
148
|
+
assignees: List of assignee usernames (optional).
|
|
149
|
+
labels: List of label names (optional).
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Dict[str, Any]: Dictionary containing PR metadata (url, number, state, success) or error information.
|
|
153
|
+
|
|
154
|
+
Raises:
|
|
155
|
+
NotImplementedError: Subclasses must implement this method.
|
|
156
|
+
"""
|
|
157
|
+
raise NotImplementedError("Subclasses must implement create_pull_request() to create a pull request with the specified parameters")
|
|
158
|
+
|
|
159
|
+
def get_authored_messages(self) -> List[Dict[str, Any]]:
|
|
160
|
+
"""
|
|
161
|
+
Aggregates all commit, pull request, and issue entries into a single list,
|
|
162
|
+
ensuring no duplicate commits (based on SHA) are present, and then sorts
|
|
163
|
+
them in chronological order based on their timestamp.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
List[Dict[str, Any]]: Aggregated and sorted list of entries.
|
|
167
|
+
"""
|
|
168
|
+
commit_entries = self.fetch_commits()
|
|
169
|
+
pr_entries = self.fetch_pull_requests()
|
|
170
|
+
try:
|
|
171
|
+
issue_entries = self.fetch_issues()
|
|
172
|
+
except Exception:
|
|
173
|
+
issue_entries = []
|
|
174
|
+
|
|
175
|
+
all_entries = pr_entries + commit_entries + issue_entries
|
|
176
|
+
|
|
177
|
+
# For commit-related entries, remove duplicates (if any) based on SHA.
|
|
178
|
+
unique_entries = {}
|
|
179
|
+
for entry in all_entries:
|
|
180
|
+
if entry.get("type") in ["commit", "commit_from_pr"]:
|
|
181
|
+
sha = entry.get("sha")
|
|
182
|
+
if sha in unique_entries:
|
|
183
|
+
continue
|
|
184
|
+
unique_entries[sha] = entry
|
|
185
|
+
else:
|
|
186
|
+
# For pull requests and issues, we can create a unique key.
|
|
187
|
+
key = f"{entry['type']}_{entry['repo']}_{entry['timestamp']}"
|
|
188
|
+
unique_entries[key] = entry
|
|
189
|
+
|
|
190
|
+
final_entries = list(unique_entries.values())
|
|
191
|
+
# Sort all entries by their timestamp.
|
|
192
|
+
final_entries.sort(key=lambda x: x["timestamp"])
|
|
193
|
+
return self.convert_timestamps_to_str(final_entries)
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
def convert_timestamps_to_str(entries: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
197
|
+
"""
|
|
198
|
+
Converts the timestamp field from datetime to string format for each entry in the list.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
entries (List[Dict[str, Any]]): List of entries with possible datetime timestamps.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
List[Dict[str, Any]]: Entries with timestamps as ISO-formatted strings.
|
|
205
|
+
"""
|
|
206
|
+
for entry in entries:
|
|
207
|
+
if isinstance(entry.get("timestamp"), datetime):
|
|
208
|
+
entry["timestamp"] = entry["timestamp"].isoformat()
|
|
209
|
+
return entries
|