Skip to content

Commit aa82a2a

Browse files
f
1 parent 86c8cb8 commit aa82a2a

File tree

6 files changed

+168
-132
lines changed

6 files changed

+168
-132
lines changed

.editorconfig

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# EditorConfig is awesome: https://editorconfig.org
2+
3+
# top-most EditorConfig file
4+
root = true
5+
6+
# Unix-style newlines with a newline ending every file
7+
[*]
8+
end_of_line = lf
9+
insert_final_newline = true
10+
11+
# Matches multiple files with brace expansion notation
12+
# Set default charset
13+
[*.{js,py}]
14+
charset = utf-8
15+
16+
# 4 space indentation
17+
[*.py]
18+
indent_style = space
19+
indent_size = 4
20+
21+
# Tab indentation (no size specified)
22+
[Makefile]
23+
indent_style = tab
24+
25+
# Indentation override for all JS under lib directory
26+
[lib/**.js]
27+
indent_style = space
28+
indent_size = 2
29+
30+
# Matches the exact files either package.json or .travis.yml
31+
[{package.json,.travis.yml}]
32+
indent_style = space
33+
indent_size = 2

src/ForgejoRepoAPI.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def get_commits(self, repo: Repository, files: bool = True) -> list[Commit]:
7474
Commit(
7575
_id=c.sha,
7676
message=c.commit.message,
77-
author=self.get_user_data(c.author),
77+
author=self.get_user_data(c.author) if c.author else None,
7878
date=isodate.parse_datetime(c.commit.author.date),
7979
files=(
8080
[f.filename for f in getattr(c, "files", [])] if files else None

src/GitHubRepoAPI.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def __init__(self, client: Github):
2424
self.client = self._client_validation(client)
2525

2626
@staticmethod
27-
@log_exceptions(default_return=None, message="Github: Connect: user could not be authenticated. Please try again.")
27+
@log_exceptions(default_return=None, message="Github: Connect: user could not be authenticated. Please try again.", print_stacktrace=False)
2828
def _client_validation(client: Github) -> Github | None:
2929
client.get_user().login
3030
return client

src/commits_parser.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def log_repository_commits(
3333
for branch in client.get_branches(repository):
3434
branches.append(branch.name)
3535
case None:
36-
branches.append(repository.default_branch)
36+
branches.append(repository.default_branch.name)
3737
case _:
3838
branches.append(branch)
3939

@@ -51,9 +51,9 @@ def log_repository_commits(
5151
changed_files = changed_files[:GOOGLE_MAX_CELL_LEN]
5252
commit_data = CommitData(
5353
repository_name=repository.name,
54-
author_name=commit.author.username,
55-
author_login=commit.author.login or EMPTY_FIELD,
56-
author_email=commit.author.email or EMPTY_FIELD,
54+
author_name=commit.author.username if commit.author else EMPTY_FIELD,
55+
author_login=commit.author.login if commit.author else EMPTY_FIELD,
56+
author_email=commit.author.email if commit.author else EMPTY_FIELD,
5757
date_and_time=commit.date.astimezone(pytz.timezone(TIMEZONE)).isoformat(),
5858
changed_files=changed_files,
5959
commit_id=commit._id,

src/git_logger.py

Lines changed: 126 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,126 @@
1-
from time import sleep
2-
import logging
3-
import traceback
4-
5-
import requests
6-
7-
from src.GitHubRepoAPI import GitHubRepoAPI
8-
from src.interface_wrapper import (
9-
RepositoryFactory,
10-
IRepositoryAPI
11-
)
12-
from src.constants import (
13-
TIMEDELTA,
14-
)
15-
16-
17-
def login(token, base_url):
18-
try:
19-
client = RepositoryFactory.create_api(token, base_url)
20-
return client
21-
except Exception as e:
22-
logging.error(e)
23-
logging.error(traceback.format_exc())
24-
return None
25-
26-
27-
def get_tokens_from_file(tokens_path: str) -> list[str]:
28-
with open(tokens_path, 'r') as file:
29-
tokens = [token for token in file.read().split('\n') if token]
30-
31-
return tokens
32-
33-
34-
def get_repos_from_file(repos_path: str) -> list[str]:
35-
with open(repos_path, 'r') as file:
36-
list_repos = [x for x in file.read().split('\n') if x]
37-
38-
return list_repos
39-
40-
41-
class Clients:
42-
def __init__(self, tokens: list[str], base_url: str | None = None):
43-
self.clients = []
44-
self.token_map = {}
45-
46-
for token in tokens:
47-
client = login(token, base_url)
48-
if client:
49-
self.clients.append(client)
50-
self.token_map[client] = token
51-
52-
if not self.clients:
53-
raise Exception("No valid tokens for either GitHub or Forgejo")
54-
55-
def _get_next_client(self) -> tuple[IRepositoryAPI, str]:
56-
client = None
57-
max_remaining_limit = -1
58-
59-
for c in self.clients:
60-
remaining, _ = c.get_rate_limiting()
61-
if remaining > max_remaining_limit:
62-
client = c
63-
max_remaining_limit = remaining
64-
sleep(TIMEDELTA)
65-
66-
if client is None:
67-
raise Exception("No git clients available")
68-
return client, self.token_map[client]
69-
70-
def get_next_client(self) -> tuple[IRepositoryAPI, str]:
71-
return self._get_next_client()
72-
73-
74-
def get_next_binded_repo(clients: Clients, repositories: list[str]):
75-
for repo_name in repositories:
76-
try:
77-
client, token = clients.get_next_client()
78-
repo = client.get_repository(repo_name)
79-
except Exception as err:
80-
print(f'git_logger.get_next_binded_repo(): error {err}')
81-
print(f'git_logger.get_next_binded_repo(): failed to load repository "{repo_name}"')
82-
else:
83-
yield client, repo, token
84-
85-
86-
def get_assignee_story(git_object, client, token, repository):
87-
assignee_result = ""
88-
89-
try:
90-
repo_owner = repository.owner.login
91-
repo_name = repository.name
92-
issue_index = git_object._id # Для pull request и issue одинаково
93-
94-
base_url = client.get_base_url().rstrip('/')
95-
96-
url = f"{base_url}/repos/{repo_owner}/{repo_name}/issues/{issue_index}/timeline"
97-
headers = {
98-
"Authorization": f"Bearer {token}" if client is GitHubRepoAPI else f"token {token}",
99-
"Accept": "application/json"
100-
}
101-
102-
response = requests.get(url, headers=headers)
103-
if response.status_code != 200:
104-
raise Exception(f"Failed to fetch issue timeline: {response.status_code}, {response.text}")
105-
106-
events = response.json()
107-
108-
for event in events:
109-
if event.get('event') in ["assigned", "unassigned"]:
110-
date = event.get('created_at')
111-
assigner = event.get('actor', {}).get('login', 'unknown')
112-
assignee = event.get('assignee', {}).get('login', 'unknown')
113-
114-
assignee_result += f"{date}: {assigner} -"
115-
if event['event'] == "unassigned":
116-
assignee_result += "/"
117-
assignee_result += f"> {assignee}; "
118-
119-
sleep(TIMEDELTA)
120-
121-
except Exception as e:
122-
print(f"get_assignee_story(): error {e}")
123-
124-
return assignee_result
1+
from time import sleep
2+
import logging
3+
import traceback
4+
5+
import requests
6+
7+
from src.GitHubRepoAPI import GitHubRepoAPI
8+
from src.interface_wrapper import (
9+
RepositoryFactory,
10+
IRepositoryAPI
11+
)
12+
from src.constants import (
13+
TIMEDELTA,
14+
)
15+
16+
17+
def login(token, base_url):
18+
try:
19+
client = RepositoryFactory.create_api(token, base_url)
20+
return client
21+
except Exception as e:
22+
logging.error(e)
23+
logging.error(traceback.format_exc())
24+
return None
25+
26+
27+
def get_tokens_from_file(tokens_path: str) -> list[str]:
28+
with open(tokens_path, 'r') as file:
29+
tokens = [token for token in file.read().split('\n') if token]
30+
31+
return tokens
32+
33+
34+
def get_repos_from_file(repos_path: str) -> list[str]:
35+
with open(repos_path, 'r') as file:
36+
list_repos = [x for x in file.read().split('\n') if x]
37+
38+
return list_repos
39+
40+
41+
class Clients:
42+
def __init__(self, tokens: list[str], base_url: str | None = None):
43+
self.clients = []
44+
self.token_map = {}
45+
46+
for token in tokens:
47+
client = login(token, base_url)
48+
if client:
49+
self.clients.append(client)
50+
self.token_map[client] = token
51+
52+
if not self.clients:
53+
if base_url:
54+
raise Exception("No valid tokens for either GitHub or Forgejo")
55+
raise Exception("Make sure that base_url is provided")
56+
57+
def _get_next_client(self) -> tuple[IRepositoryAPI, str]:
58+
client = None
59+
max_remaining_limit = -1
60+
61+
for c in self.clients:
62+
remaining, _ = c.get_rate_limiting()
63+
if remaining > max_remaining_limit:
64+
client = c
65+
max_remaining_limit = remaining
66+
sleep(TIMEDELTA)
67+
68+
if client is None:
69+
raise Exception("No git clients available")
70+
return client, self.token_map[client]
71+
72+
def get_next_client(self) -> tuple[IRepositoryAPI, str]:
73+
return self._get_next_client()
74+
75+
76+
def get_next_binded_repo(clients: Clients, repositories: list[str]):
77+
for repo_name in repositories:
78+
try:
79+
client, token = clients.get_next_client()
80+
repo = client.get_repository(repo_name)
81+
except Exception as err:
82+
print(f'git_logger.get_next_binded_repo(): error {err}')
83+
print(f'git_logger.get_next_binded_repo(): failed to load repository "{repo_name}"')
84+
else:
85+
yield client, repo, token
86+
87+
88+
def get_assignee_story(git_object, client, token, repository):
89+
assignee_result = ""
90+
91+
try:
92+
repo_owner = repository.owner.login
93+
repo_name = repository.name
94+
issue_index = git_object._id # Для pull request и issue одинаково
95+
96+
base_url = client.get_base_url().rstrip('/')
97+
98+
url = f"{base_url}/repos/{repo_owner}/{repo_name}/issues/{issue_index}/timeline"
99+
headers = {
100+
"Authorization": f"Bearer {token}" if client is GitHubRepoAPI else f"token {token}",
101+
"Accept": "application/json"
102+
}
103+
104+
response = requests.get(url, headers=headers)
105+
if response.status_code != 200:
106+
raise Exception(f"Failed to fetch issue timeline: {response.status_code}, {response.text}")
107+
108+
events = response.json()
109+
110+
for event in events:
111+
if event.get('event') in ["assigned", "unassigned"]:
112+
date = event.get('created_at')
113+
assigner = event.get('actor', {}).get('login', 'unknown')
114+
assignee = event.get('assignee', {}).get('login', 'unknown')
115+
116+
assignee_result += f"{date}: {assigner} -"
117+
if event['event'] == "unassigned":
118+
assignee_result += "/"
119+
assignee_result += f"> {assignee}; "
120+
121+
sleep(TIMEDELTA)
122+
123+
except Exception as e:
124+
print(f"get_assignee_story(): error {e}")
125+
126+
return assignee_result

src/utils.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def parse_time(datetime_str) -> datetime:
7777
return start_datetime.astimezone(pytz.timezone(TIMEZONE))
7878

7979

80-
def log_exceptions(default_return=None, message=""):
80+
def log_exceptions(default_return=None, message="", print_stacktrace=True):
8181
"""
8282
Декоратор обработки ошибок для методов класса.
8383
Логирует ошибки и возвращает default_return при исключении.
@@ -91,7 +91,8 @@ def wrapper(*args, **kwargs):
9191
class_name = args[0].__class__.__name__ if args else ""
9292
logging.error(f"{class_name=}")
9393
logging.error(f"{message} {func.__name__}: {e}")
94-
logging.error(traceback.format_exc())
94+
if print_stacktrace:
95+
logging.error(traceback.format_exc())
9596
return default_return
9697
return wrapper
9798
return decorator

0 commit comments

Comments
 (0)