Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 133 additions & 84 deletions issues_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from datetime import datetime
from time import sleep
from typing import Generator

import pytz
import requests

Expand Down Expand Up @@ -43,80 +42,134 @@ class IssueDataWithComment(IssueData):
comment_author_email: str = ''


def get_connected_pulls(issue_number, repo_owner, repo_name, token):
# TODO как-то заменить
return
access_token = token
repo_owner = repo_owner.login
# Формирование запроса GraphQL
query = """
{
repository(owner: "%s", name: "%s") {
issue(number: %d) {
timelineItems(first: 50, itemTypes:[CONNECTED_EVENT,CROSS_REFERENCED_EVENT]) {
filteredCount
nodes {
... on ConnectedEvent {
ConnectedEvent: subject {
... on PullRequest {
number
title
url
def get_connected_pulls(
token: str,
issue_number: int,
repo_owner: str,
repo_name: str,
base_url: str | None = None
) -> str:

if base_url: # Forgejo
headers = {
"Authorization": f"token {token}",
"Accept": "application/json"
}

connected_prs = set()
api_base = f"{base_url}/api/v1/repos/{repo_owner}/{repo_name}"

try:
comments_response = requests.get(
f"{api_base}/issues/{issue_number}/comments",
headers=headers
)
comments_response.raise_for_status()

for comment in comments_response.json():
body = comment.get("body", "")
if not body:
continue

for word in body.split():
clean_word = word.strip(".,:;!?()[]{}")
if len(clean_word) > 1 and clean_word[1:].isdigit():
if clean_word.startswith('#'):
pr_num = clean_word[1:]
connected_prs.add(f"{base_url}/{repo_owner}/{repo_name}/pulls/{pr_num}")
elif clean_word.startswith('!'):
pr_num = clean_word[1:]
connected_prs.add(f"{base_url}/{repo_owner}/{repo_name}/pulls/{pr_num}")

prs_response = requests.get(
f"{api_base}/pulls?state=all",
headers=headers
)
prs_response.raise_for_status()

for pr in prs_response.json():
if f"#{issue_number}" in pr.get("body", ""):
connected_prs.add(pr.get("html_url"))

except requests.exceptions.RequestException as e:
print(f"[Warning] Failed to fetch connected PRs: {str(e)}")
return 'Empty field'

return ';'.join(sorted(connected_prs)) if connected_prs else 'Empty field'

else: # PyGithub
repo_owner = repo_owner.login
# Формирование запроса GraphQL
query = """
{
repository(owner: "%s", name: "%s") {
issue(number: %d) {
timelineItems(first: 50, itemTypes:[CONNECTED_EVENT,CROSS_REFERENCED_EVENT]) {
filteredCount
nodes {
... on ConnectedEvent {
ConnectedEvent: subject {
... on PullRequest {
number
title
url
}
}
}
}
}
... on CrossReferencedEvent {
CrossReferencedEvent: source {
... on PullRequest {
number
title
url
... on CrossReferencedEvent {
CrossReferencedEvent: source {
... on PullRequest {
number
title
url
}
}
}
}
}
}
}
}""" % (
repo_owner,
repo_name,
issue_number,
)

# Формирование заголовков запроса
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
}
}
}""" % (
repo_owner,
repo_name,
issue_number,
)

# Формирование заголовков запроса
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
}

# Отправка запроса GraphQL
response = requests.post(
"https://api.github.com/graphql",
headers=headers,
data=json.dumps({"query": query}),
)
response_data = response.json()
# Обработка полученных данных
pull_request_data = response_data["data"]["repository"]["issue"]
list_url = []
if pull_request_data is not None:
issues_data = pull_request_data["timelineItems"]["nodes"]
for pulls in issues_data:
if (
pulls.get("CrossReferencedEvent") is not None
and pulls.get("CrossReferencedEvent").get("url") not in list_url
):
list_url.append(pulls.get("CrossReferencedEvent").get("url"))
if (
pulls.get("ConnectedEvent") is not None
and pulls.get("ConnectedEvent").get("url") not in list_url
):
list_url.append(pulls.get("ConnectedEvent").get("url"))
if list_url == []:
return 'Empty field'
else:
return ';'.join(list_url)

# Отправка запроса GraphQL
response = requests.post(
"https://api.github.com/graphql",
headers=headers,
data=json.dumps({"query": query}),
)
response_data = response.json()
# Обработка полученных данных
pull_request_data = response_data["data"]["repository"]["issue"]
list_url = []
if pull_request_data is not None:
issues_data = pull_request_data["timelineItems"]["nodes"]
for pulls in issues_data:
if (
pulls.get("CrossReferencedEvent") is not None
and pulls.get("CrossReferencedEvent").get("url") is not None
and pulls.get("CrossReferencedEvent").get("url") not in list_url
):
list_url.append(pulls.get("CrossReferencedEvent").get("url"))
if (
pulls.get("ConnectedEvent") is not None
and pulls.get("ConnectedEvent").get("url") is not None
and pulls.get("ConnectedEvent").get("url") not in list_url
):
list_url.append(pulls.get("ConnectedEvent").get("url"))
if list_url == []:
return 'Empty field'
else:
return ';'.join(list_url)
return 'Empty field'


Expand Down Expand Up @@ -154,7 +207,7 @@ def get_info(obj, attr):
closer_email=issue.closed_by.email if issue.closed_by else None,
assignee_story=get_assignee_story(issue),
connected_pull_requests=(
get_connected_pulls(issue._id, repository.owner, repository.name, token)
get_connected_pulls(token, issue._id, repository.owner, repository.name)
if issue._id is not None
else EMPTY_FIELD
),
Expand Down Expand Up @@ -199,18 +252,14 @@ def log_issues(
logger.log_to_csv(csv_name, list(info.keys()))

for client, repo, token in binded_repos:
try:
logger.log_title(repo.name)
log_repository_issues(client, repo, csv_name, token, start, finish)
if fork_flag:
forked_repos = client.get_forks(repo)
for forked_repo in forked_repos:
logger.log_title(f"FORKED: {forked_repo.name}")
log_repository_issues(
client, forked_repo, csv_name, token, start, finish
)
sleep(TIMEDELTA)
sleep(TIMEDELTA)
except Exception as e:
print("log_issues exception:", e)
exit(1)
logger.log_title(repo.name)
log_repository_issues(client, repo, csv_name, token, start, finish)
if fork_flag:
forked_repos = client.get_forks(repo)
for forked_repo in forked_repos:
logger.log_title(f"FORKED: {forked_repo.name}")
log_repository_issues(
client, forked_repo, csv_name, token, start, finish
)
sleep(TIMEDELTA)
sleep(TIMEDELTA)
4 changes: 2 additions & 2 deletions utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ def log_title(title: str, title_len: int = TITLE_LEN):
@staticmethod
def log_to_csv(csv_name: str, field_names: tuple[str], row: dict | None = None):
if isinstance(row, dict):
with open(csv_name, 'a', newline='') as file:
with open(csv_name, 'a', encoding='utf-8', newline='') as file:
writer = csv.DictWriter(file, fieldnames=field_names)
writer.writerow(row)
elif row is None:
with open(csv_name, 'w', newline='') as file:
with open(csv_name, 'w', encoding='utf-8', newline='') as file:
writer = csv.writer(file)
writer.writerow(field_names)
else:
Expand Down