1+ from collections import defaultdict
12from typing import Optional
23
3- from django .db .models import Prefetch , Q , QuerySet
4+ from django .db .models import Count , Prefetch , Q , QuerySet
45from django .db .models .functions import Lower , Substr
6+ from graphql import GraphQLResolveInfo
57
68from core .models import Commit , Pull , Repository
79from graphql_api .types .enums import CommitStatus
@@ -23,35 +25,51 @@ def pull_commits(pull: Pull) -> QuerySet[Commit]:
2325 return Commit .objects .filter (id__in = subquery ).defer ("_report" )
2426
2527
28+ def load_commit_statuses (
29+ commit_ids : list [int ],
30+ ) -> dict [int , dict [CommitReport .ReportType , str ]]:
31+ qs = (
32+ CommitReport .objects .filter (commit__in = commit_ids )
33+ .values_list ("commit_id" , "report_type" , "sessions__state" )
34+ .annotate (sessions_count = Count ("sessions" ))
35+ )
36+
37+ grouped : dict [tuple [int , CommitReport .ReportType ], dict [str , int ]] = defaultdict (
38+ dict
39+ )
40+ for id , report_type , state , count in qs :
41+ # The above query generates a `LEFT OUTER JOIN` with a proper `GROUP BY`.
42+ # However, it is also yielding rows with a `NULL` state in case a report does not have any uploads.
43+ if not report_type or not state :
44+ continue
45+ grouped [(id , report_type )][state ] = count
46+
47+ results : dict [int , dict [CommitReport .ReportType , str ]] = {
48+ id : {} for id in commit_ids
49+ }
50+ for (id , report_type ), states in grouped .items ():
51+ status = CommitStatus .COMPLETED .value
52+ if states .get ("error" , 0 ) > 0 :
53+ status = CommitStatus .ERROR .value
54+ elif states .get ("uploaded" , 0 ) > 0 :
55+ status = CommitStatus .PENDING .value
56+
57+ results [id ][report_type ] = status
58+
59+ return results
60+
61+
2662def commit_status (
27- commit : Commit , report_type : CommitReport .ReportType
28- ) -> Optional [CommitStatus ]:
29- report = CommitReport .objects .filter (report_type = report_type , commit = commit ).first ()
30- if not report :
31- return None
32-
33- sessions = report .sessions .all ()
34- if not sessions :
35- return None
36-
37- # Only care about these 3 states, ignoring fully and partially overwritten
38- upload_states = [
39- s .state for s in sessions if s .state in ["processed" , "uploaded" , "error" ]
40- ]
41-
42- has_error , has_pending = False , False
43- for state in upload_states :
44- if state == "error" :
45- has_error = True
46- if state == "uploaded" :
47- has_pending = True
48-
49- # Prioritize returning error over pending
50- if has_error :
51- return CommitStatus .ERROR .value
52- if has_pending :
53- return CommitStatus .PENDING .value
54- return CommitStatus .COMPLETED .value
63+ info : GraphQLResolveInfo , commit : Commit , report_type : CommitReport .ReportType
64+ ) -> str | None :
65+ commit_statuses = info .context .setdefault ("commit_statuses" , {})
66+ commit_status = commit_statuses .get (commit .id )
67+ if commit_status is None :
68+ updated_statuses = load_commit_statuses ([commit .id ])
69+ commit_statuses .update (updated_statuses )
70+ commit_status = updated_statuses [commit .id ]
71+
72+ return commit_status .get (report_type )
5573
5674
5775def repo_commits (
@@ -100,11 +118,17 @@ def repo_commits(
100118 coverage_status = filters .get ("coverage_status" )
101119
102120 if coverage_status :
121+ # FIXME(swatinem):
122+ # This filter here is insane, it resolves *all* the results in the unbounded queryset,
123+ # just to check the status, and to then add it as another restricting filter.
124+ # I’m pretty sure this will completely break the server if anyone actually uses this filter, lol.
125+ commit_ids = [commit .id for commit in queryset ]
126+ commit_statuses = load_commit_statuses (commit_ids )
127+
103128 to_be_included = [
104- commit .id
105- for commit in queryset
106- if commit_status (commit , CommitReport .ReportType .COVERAGE )
107- in coverage_status
129+ id
130+ for id , statuses in commit_statuses .items ()
131+ if statuses .get (CommitReport .ReportType .COVERAGE ) in coverage_status
108132 ]
109133 queryset = queryset .filter (id__in = to_be_included )
110134
0 commit comments