Skip to content

Commit d6e9c09

Browse files
authored
Merge pull request #123 from moevm/100-get_connected_pulls
100-get_connected_pulls
2 parents ced7025 + 4e2c5af commit d6e9c09

File tree

2 files changed

+135
-86
lines changed

2 files changed

+135
-86
lines changed

issues_parser.py

Lines changed: 133 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from datetime import datetime
44
from time import sleep
55
from typing import Generator
6-
76
import pytz
87
import requests
98

@@ -43,80 +42,134 @@ class IssueDataWithComment(IssueData):
4342
comment_author_email: str = ''
4443

4544

46-
def get_connected_pulls(issue_number, repo_owner, repo_name, token):
47-
# TODO как-то заменить
48-
return
49-
access_token = token
50-
repo_owner = repo_owner.login
51-
# Формирование запроса GraphQL
52-
query = """
53-
{
54-
repository(owner: "%s", name: "%s") {
55-
issue(number: %d) {
56-
timelineItems(first: 50, itemTypes:[CONNECTED_EVENT,CROSS_REFERENCED_EVENT]) {
57-
filteredCount
58-
nodes {
59-
... on ConnectedEvent {
60-
ConnectedEvent: subject {
61-
... on PullRequest {
62-
number
63-
title
64-
url
45+
def get_connected_pulls(
46+
token: str,
47+
issue_number: int,
48+
repo_owner: str,
49+
repo_name: str,
50+
base_url: str | None = None
51+
) -> str:
52+
53+
if base_url: # Forgejo
54+
headers = {
55+
"Authorization": f"token {token}",
56+
"Accept": "application/json"
57+
}
58+
59+
connected_prs = set()
60+
api_base = f"{base_url}/api/v1/repos/{repo_owner}/{repo_name}"
61+
62+
try:
63+
comments_response = requests.get(
64+
f"{api_base}/issues/{issue_number}/comments",
65+
headers=headers
66+
)
67+
comments_response.raise_for_status()
68+
69+
for comment in comments_response.json():
70+
body = comment.get("body", "")
71+
if not body:
72+
continue
73+
74+
for word in body.split():
75+
clean_word = word.strip(".,:;!?()[]{}")
76+
if len(clean_word) > 1 and clean_word[1:].isdigit():
77+
if clean_word.startswith('#'):
78+
pr_num = clean_word[1:]
79+
connected_prs.add(f"{base_url}/{repo_owner}/{repo_name}/pulls/{pr_num}")
80+
elif clean_word.startswith('!'):
81+
pr_num = clean_word[1:]
82+
connected_prs.add(f"{base_url}/{repo_owner}/{repo_name}/pulls/{pr_num}")
83+
84+
prs_response = requests.get(
85+
f"{api_base}/pulls?state=all",
86+
headers=headers
87+
)
88+
prs_response.raise_for_status()
89+
90+
for pr in prs_response.json():
91+
if f"#{issue_number}" in pr.get("body", ""):
92+
connected_prs.add(pr.get("html_url"))
93+
94+
except requests.exceptions.RequestException as e:
95+
print(f"[Warning] Failed to fetch connected PRs: {str(e)}")
96+
return 'Empty field'
97+
98+
return ';'.join(sorted(connected_prs)) if connected_prs else 'Empty field'
99+
100+
else: # PyGithub
101+
repo_owner = repo_owner.login
102+
# Формирование запроса GraphQL
103+
query = """
104+
{
105+
repository(owner: "%s", name: "%s") {
106+
issue(number: %d) {
107+
timelineItems(first: 50, itemTypes:[CONNECTED_EVENT,CROSS_REFERENCED_EVENT]) {
108+
filteredCount
109+
nodes {
110+
... on ConnectedEvent {
111+
ConnectedEvent: subject {
112+
... on PullRequest {
113+
number
114+
title
115+
url
116+
}
117+
}
65118
}
66-
}
67-
}
68-
... on CrossReferencedEvent {
69-
CrossReferencedEvent: source {
70-
... on PullRequest {
71-
number
72-
title
73-
url
119+
... on CrossReferencedEvent {
120+
CrossReferencedEvent: source {
121+
... on PullRequest {
122+
number
123+
title
124+
url
125+
}
126+
}
74127
}
75128
}
76129
}
77130
}
78131
}
132+
}""" % (
133+
repo_owner,
134+
repo_name,
135+
issue_number,
136+
)
137+
138+
# Формирование заголовков запроса
139+
headers = {
140+
"Authorization": f"Bearer {token}",
141+
"Content-Type": "application/json",
79142
}
80-
}
81-
}""" % (
82-
repo_owner,
83-
repo_name,
84-
issue_number,
85-
)
86-
87-
# Формирование заголовков запроса
88-
headers = {
89-
"Authorization": f"Bearer {access_token}",
90-
"Content-Type": "application/json",
91-
}
92-
93-
# Отправка запроса GraphQL
94-
response = requests.post(
95-
"https://api.github.com/graphql",
96-
headers=headers,
97-
data=json.dumps({"query": query}),
98-
)
99-
response_data = response.json()
100-
# Обработка полученных данных
101-
pull_request_data = response_data["data"]["repository"]["issue"]
102-
list_url = []
103-
if pull_request_data is not None:
104-
issues_data = pull_request_data["timelineItems"]["nodes"]
105-
for pulls in issues_data:
106-
if (
107-
pulls.get("CrossReferencedEvent") is not None
108-
and pulls.get("CrossReferencedEvent").get("url") not in list_url
109-
):
110-
list_url.append(pulls.get("CrossReferencedEvent").get("url"))
111-
if (
112-
pulls.get("ConnectedEvent") is not None
113-
and pulls.get("ConnectedEvent").get("url") not in list_url
114-
):
115-
list_url.append(pulls.get("ConnectedEvent").get("url"))
116-
if list_url == []:
117-
return 'Empty field'
118-
else:
119-
return ';'.join(list_url)
143+
144+
# Отправка запроса GraphQL
145+
response = requests.post(
146+
"https://api.github.com/graphql",
147+
headers=headers,
148+
data=json.dumps({"query": query}),
149+
)
150+
response_data = response.json()
151+
# Обработка полученных данных
152+
pull_request_data = response_data["data"]["repository"]["issue"]
153+
list_url = []
154+
if pull_request_data is not None:
155+
issues_data = pull_request_data["timelineItems"]["nodes"]
156+
for pulls in issues_data:
157+
if (
158+
pulls.get("CrossReferencedEvent") is not None
159+
and pulls.get("CrossReferencedEvent").get("url") is not None
160+
and pulls.get("CrossReferencedEvent").get("url") not in list_url
161+
):
162+
list_url.append(pulls.get("CrossReferencedEvent").get("url"))
163+
if (
164+
pulls.get("ConnectedEvent") is not None
165+
and pulls.get("ConnectedEvent").get("url") is not None
166+
and pulls.get("ConnectedEvent").get("url") not in list_url
167+
):
168+
list_url.append(pulls.get("ConnectedEvent").get("url"))
169+
if list_url == []:
170+
return 'Empty field'
171+
else:
172+
return ';'.join(list_url)
120173
return 'Empty field'
121174

122175

@@ -154,7 +207,7 @@ def get_info(obj, attr):
154207
closer_email=issue.closed_by.email if issue.closed_by else None,
155208
assignee_story=get_assignee_story(issue),
156209
connected_pull_requests=(
157-
get_connected_pulls(issue._id, repository.owner, repository.name, token)
210+
get_connected_pulls(token, issue._id, repository.owner, repository.name)
158211
if issue._id is not None
159212
else EMPTY_FIELD
160213
),
@@ -199,18 +252,14 @@ def log_issues(
199252
logger.log_to_csv(csv_name, list(info.keys()))
200253

201254
for client, repo, token in binded_repos:
202-
try:
203-
logger.log_title(repo.name)
204-
log_repository_issues(client, repo, csv_name, token, start, finish)
205-
if fork_flag:
206-
forked_repos = client.get_forks(repo)
207-
for forked_repo in forked_repos:
208-
logger.log_title(f"FORKED: {forked_repo.name}")
209-
log_repository_issues(
210-
client, forked_repo, csv_name, token, start, finish
211-
)
212-
sleep(TIMEDELTA)
213-
sleep(TIMEDELTA)
214-
except Exception as e:
215-
print("log_issues exception:", e)
216-
exit(1)
255+
logger.log_title(repo.name)
256+
log_repository_issues(client, repo, csv_name, token, start, finish)
257+
if fork_flag:
258+
forked_repos = client.get_forks(repo)
259+
for forked_repo in forked_repos:
260+
logger.log_title(f"FORKED: {forked_repo.name}")
261+
log_repository_issues(
262+
client, forked_repo, csv_name, token, start, finish
263+
)
264+
sleep(TIMEDELTA)
265+
sleep(TIMEDELTA)

utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ def log_title(title: str, title_len: int = TITLE_LEN):
2121
@staticmethod
2222
def log_to_csv(csv_name: str, field_names: tuple[str], row: dict | None = None):
2323
if isinstance(row, dict):
24-
with open(csv_name, 'a', newline='') as file:
24+
with open(csv_name, 'a', encoding='utf-8', newline='') as file:
2525
writer = csv.DictWriter(file, fieldnames=field_names)
2626
writer.writerow(row)
2727
elif row is None:
28-
with open(csv_name, 'w', newline='') as file:
28+
with open(csv_name, 'w', encoding='utf-8', newline='') as file:
2929
writer = csv.writer(file)
3030
writer.writerow(field_names)
3131
else:

0 commit comments

Comments
 (0)