4
4
5
5
from django .core .management .base import BaseCommand
6
6
from django .db import transaction
7
+ from django .utils import timezone
7
8
from github .GithubException import GithubException
8
9
9
10
from apps .github .auth import get_github_client
@@ -23,8 +24,13 @@ class Command(BaseCommand):
23
24
ALLOWED_GITHUB_HOSTS = {"github.com" , "www.github.com" }
24
25
REPO_PATH_PARTS = 2
25
26
26
- def _extract_repo_full_name (self , repo_url ):
27
- parsed = urlparse (repo_url or "" )
27
+ def _extract_repo_full_name (self , repository ):
28
+ """Extract repository full name from Repository model or URL string."""
29
+ if hasattr (repository , "path" ):
30
+ return repository .path
31
+
32
+ repo_url = str (repository ) if repository else ""
33
+ parsed = urlparse (repo_url )
28
34
if parsed .netloc in self .ALLOWED_GITHUB_HOSTS :
29
35
parts = parsed .path .strip ("/" ).split ("/" )
30
36
if len (parts ) >= self .REPO_PATH_PARTS :
@@ -45,18 +51,19 @@ def _get_status(self, issue, assignee):
45
51
def _get_last_assigned_date (self , repo , issue_number , assignee_login ):
46
52
"""Find the most recent 'assigned' event for a specific user using PyGithub."""
47
53
try :
48
- issue = repo .get_issue (number = issue_number )
49
-
50
- events_list = list (issue .get_events ())
51
- events_list .reverse ()
52
-
53
- for event in events_list :
54
+ gh_issue = repo .get_issue (number = issue_number )
55
+ last_dt = None
56
+ for event in gh_issue .get_events ():
54
57
if (
55
58
event .event == "assigned"
56
59
and event .assignee
57
60
and event .assignee .login == assignee_login
58
61
):
59
- return event .created_at
62
+ last_dt = event .created_at
63
+
64
+ if last_dt and timezone .is_naive (last_dt ):
65
+ return timezone .make_aware (last_dt , timezone .utc )
66
+ return last_dt # noqa: TRY300
60
67
61
68
except GithubException as e :
62
69
self .stderr .write (
@@ -69,15 +76,14 @@ def _build_repo_label_to_issue_map(self):
69
76
"""Build a map from (repository_id, normalized_label_name) to a set of issue IDs."""
70
77
self .stdout .write ("Building a repository-aware map of labels to issues..." )
71
78
repo_label_to_issue_ids = {}
72
-
73
- issues_query = Issue .objects .select_related ("repository" ).prefetch_related ("labels" )
74
- for issue in issues_query :
75
- if not issue .repository_id :
76
- continue
77
- for label in issue .labels .all ():
78
- normalized_label = normalize_name (label .name )
79
- key = (issue .repository_id , normalized_label )
80
- repo_label_to_issue_ids .setdefault (key , set ()).add (issue .id )
79
+ rows = (
80
+ Issue .objects .filter (labels__isnull = False , repository__isnull = False )
81
+ .values_list ("id" , "repository_id" , "labels__name" )
82
+ .iterator (chunk_size = 5000 )
83
+ )
84
+ for issue_id , repo_id , label_name in rows :
85
+ key = (repo_id , normalize_name (label_name ))
86
+ repo_label_to_issue_ids .setdefault (key , set ()).add (issue_id )
81
87
82
88
self .stdout .write (
83
89
f"Map built. Found issues for { len (repo_label_to_issue_ids )} unique repo-label pairs."
@@ -120,11 +126,26 @@ def _process_module(
120
126
)
121
127
122
128
for issue in issues :
123
- new_assignee = issue .assignees .first ()
124
- assigned_date = None
125
- if new_assignee and issue .repository :
126
- repo_full_name = self ._extract_repo_full_name (str (issue .repository .url ))
129
+ assignee = issue .assignees .first ()
130
+ if not assignee :
131
+ continue
132
+
133
+ status = self ._get_status (issue , assignee )
134
+ task , created = Task .objects .get_or_create (
135
+ issue = issue ,
136
+ assignee = assignee ,
137
+ defaults = {"module" : module , "status" : status },
138
+ )
127
139
140
+ updates = {}
141
+ if task .module != module :
142
+ updates ["module" ] = module
143
+ if task .status != status :
144
+ updates ["status" ] = status
145
+
146
+ # Only fetch assigned_at when needed.
147
+ if (created or task .assigned_at is None ) and issue .repository :
148
+ repo_full_name = self ._extract_repo_full_name (issue .repository )
128
149
if repo_full_name :
129
150
if repo_full_name not in repo_cache :
130
151
try :
@@ -136,49 +157,30 @@ def _process_module(
136
157
)
137
158
)
138
159
repo_cache [repo_full_name ] = None
139
-
140
- repo = repo_cache [repo_full_name ]
160
+ repo = repo_cache .get (repo_full_name )
141
161
if repo :
142
162
assigned_date = self ._get_last_assigned_date (
143
163
repo = repo ,
144
164
issue_number = issue .number ,
145
- assignee_login = new_assignee .login ,
165
+ assignee_login = assignee .login ,
146
166
)
147
-
148
- if new_assignee :
149
- status = self ._get_status (issue , new_assignee )
150
-
151
- task , created = Task .objects .get_or_create (
152
- issue = issue ,
153
- assignee = new_assignee ,
154
- defaults = {
155
- "module" : module ,
156
- "status" : status ,
157
- "assigned_at" : assigned_date ,
158
- },
167
+ if assigned_date :
168
+ updates ["assigned_at" ] = assigned_date
169
+
170
+ if created :
171
+ num_tasks_created += 1
172
+ self .stdout .write (
173
+ self .style .SUCCESS (
174
+ f"Task created for user '{ assignee .login } ' on issue "
175
+ f"{ issue .repository .name } #{ issue .number } "
176
+ f"in module '{ module .name } '"
177
+ )
159
178
)
160
179
161
- if created :
162
- num_tasks_created += 1
163
- self .stdout .write (
164
- self .style .SUCCESS (
165
- f"Task created for user '{ new_assignee .login } ' on issue "
166
- f"{ issue .repository .name } #{ issue .number } "
167
- f"in module '{ module .name } ' "
168
- f"(assigned on { assigned_date } )"
169
- )
170
- )
171
- else :
172
- updates = {}
173
- if task .module != module :
174
- updates ["module" ] = module
175
- if task .status != status :
176
- updates ["status" ] = status
177
-
178
- if updates :
179
- for field , value in updates .items ():
180
- setattr (task , field , value )
181
- task .save (update_fields = list (updates .keys ()))
180
+ if updates :
181
+ for field , value in updates .items ():
182
+ setattr (task , field , value )
183
+ task .save (update_fields = list (updates .keys ()))
182
184
183
185
num_linked = len (matched_issue_ids )
184
186
if num_linked > 0 :
0 commit comments