11import logging
2- from typing import List , Optional
32
43import sentry_sdk
5- from django .conf import settings
6- from django .db .models import Prefetch , Q
74from django .utils .functional import cached_property
85from shared .helpers .flag import Flag
96from shared .reports .readonly import ReadOnlyReport as SharedReadOnlyReport
107from shared .reports .resources import Report
11- from shared .reports .types import ReportFileSummary , ReportTotals
128from shared .storage .exceptions import FileNotInStorageError
13- from shared .utils .sessions import Session , SessionType
9+ from shared .utils .sessions import Session
1410
1511from core .models import Commit
16- from reports .models import AbstractTotals , CommitReport , ReportSession
1712from services .archive import ArchiveService
18- from utils .config import RUN_ENV
1913
2014log = logging .getLogger (__name__ )
2115
2216
2317class ReportMixin :
24- def file_reports (self ):
25- for f in self .files :
26- yield self .get (f )
27-
2818 @cached_property
29- def flags (self ):
19+ def flags (self ) -> dict :
3020 """returns dict(:name=<Flag>)"""
3121 flags_dict = {}
3222 for session in self .sessions .values ():
@@ -61,42 +51,20 @@ def build_report(chunks, files, sessions, totals, report_class=None):
6151
6252
6353@sentry_sdk .trace
64- def build_report_from_commit (commit : Commit , report_class = None ):
54+ def build_report_from_commit (commit : Commit , report_class = None ) -> Report | None :
6555 """
6656 Builds a `shared.reports.resources.Report` from a given commit.
67-
68- Chunks are fetched from archive storage and the rest of the data is sourced
69- from various `reports_*` tables in the database.
7057 """
7158
72- # TODO: this can be removed once confirmed working well on prod
73- new_report_builder_enabled = (
74- RUN_ENV == "DEV"
75- or RUN_ENV == "STAGING"
76- or RUN_ENV == "TESTING"
77- or commit .repository_id in settings .REPORT_BUILDER_REPO_IDS
78- )
79-
80- with sentry_sdk .start_span (description = "Fetch files/sessions/totals" ):
81- commit_report = fetch_commit_report (commit )
82- if commit_report and new_report_builder_enabled :
83- files = build_files (commit_report )
84- sessions = build_sessions (commit_report )
85- try :
86- totals = build_totals (commit_report .reportleveltotals )
87- except CommitReport .reportleveltotals .RelatedObjectDoesNotExist :
88- totals = None
89- else :
90- if not commit .report :
91- return None
59+ if not commit .report :
60+ return None
9261
93- files = commit .report ["files" ]
94- sessions = commit .report ["sessions" ]
95- totals = commit .totals
62+ files = commit .report ["files" ]
63+ sessions = commit .report ["sessions" ]
64+ totals = commit .totals
9665
9766 try :
98- with sentry_sdk .start_span (description = "Fetch chunks" ):
99- chunks = ArchiveService (commit .repository ).read_chunks (commit .commitid )
67+ chunks = ArchiveService (commit .repository ).read_chunks (commit .commitid )
10068 return build_report (chunks , files , sessions , totals , report_class = report_class )
10169 except FileNotInStorageError :
10270 log .warning (
@@ -109,130 +77,7 @@ def build_report_from_commit(commit: Commit, report_class=None):
10977 return None
11078
11179
112- def fetch_commit_report (commit : Commit ) -> Optional [CommitReport ]:
113- """
114- Fetch a single `CommitReport` for the given commit.
115- All the necessary report relations are prefetched.
116- """
117- return (
118- commit .reports .coverage_reports ()
119- .filter (code = None )
120- .prefetch_related (
121- Prefetch (
122- "sessions" ,
123- queryset = ReportSession .objects .prefetch_related ("flags" ).select_related (
124- "uploadleveltotals"
125- ),
126- ),
127- )
128- .select_related ("reportdetails" , "reportleveltotals" )
129- .first ()
130- )
131-
132-
133- def build_totals (totals : AbstractTotals ) -> ReportTotals :
134- """
135- Build a `shared.reports.types.ReportTotals` instance from one of the
136- various database totals records.
137- """
138- return ReportTotals (
139- files = totals .files ,
140- lines = totals .lines ,
141- hits = totals .hits ,
142- misses = totals .misses ,
143- partials = totals .partials ,
144- coverage = totals .coverage ,
145- branches = totals .branches ,
146- methods = totals .methods ,
147- )
148-
149-
150- def build_session (upload : ReportSession ) -> Session :
151- """
152- Build a `shared.utils.sessions.Session` from a database `reports_upload` record.
153- """
154- try :
155- upload_totals = build_totals (upload .uploadleveltotals )
156- except ReportSession .uploadleveltotals .RelatedObjectDoesNotExist :
157- # upload does not have any totals - maybe the processing failed
158- # or the upload was empty?
159- upload_totals = None
160- flags = [flag .flag_name for flag in upload .flags .all ()]
161-
162- return Session (
163- id = upload .id ,
164- totals = upload_totals ,
165- time = upload .created_at .timestamp ,
166- archive = upload .storage_path ,
167- flags = flags ,
168- provider = upload .provider ,
169- build = upload .build_code ,
170- job = upload .job_code ,
171- url = upload .build_url ,
172- state = upload .state ,
173- env = upload .env ,
174- name = upload .name ,
175- session_type = SessionType .get_from_string (upload .upload_type ),
176- session_extras = upload .upload_extras ,
177- )
178-
179-
180- def build_sessions (commit_report : CommitReport ) -> dict [int , Session ]:
181- """
182- Build mapping of report number -> session that can be passed to the report class.
183- Does not include CF sessions if there is also an upload session with the same
184- flag name.
185- """
186- sessions = {}
187-
188- carryforward_sessions = {}
189- uploaded_flags = set ()
190-
191- for upload in commit_report .sessions .filter (
192- Q (state = "complete" ) | Q (state = "processed" )
193- ):
194- session = build_session (upload )
195- if session .session_type == SessionType .carriedforward :
196- carryforward_sessions [upload .order_number ] = session
197- else :
198- sessions [upload .order_number ] = session
199- uploaded_flags |= set (session .flags )
200-
201- for sid , session in carryforward_sessions .items ():
202- # we only ever expect 1 flag for CF sessions
203- overlapping_flags = uploaded_flags & set (session .flags )
204-
205- if len (overlapping_flags ) == 0 :
206- # we can include this CF session since there are no direct uploads
207- # with the same flag name
208- sessions [sid ] = session
209-
210- return sessions
211-
212-
213- def build_files (commit_report : CommitReport ) -> dict [str , ReportFileSummary ]:
214- """
215- Construct a files dictionary in a format compatible with `shared.reports.resources.Report`
216- from data in the `reports_reportdetails.files_array` column in the database.
217- """
218- try :
219- report_details = commit_report .reportdetails
220- except CommitReport .reportdetails .RelatedObjectDoesNotExist :
221- # we don't expect this but something could have gone wrong in the worker
222- # we can't really recover here
223- return {}
224-
225- return {
226- file ["filename" ]: ReportFileSummary (
227- file_index = file ["file_index" ],
228- file_totals = ReportTotals (* file ["file_totals" ]),
229- diff_totals = file ["diff_totals" ],
230- )
231- for file in report_details .files_array
232- }
233-
234-
235- def files_belonging_to_flags (commit_report : Report , flags : List [str ]) -> List [str ]:
80+ def files_belonging_to_flags (commit_report : Report , flags : list [str ]) -> list [str ]:
23681 sessions_for_specific_flags = sessions_with_specific_flags (
23782 commit_report = commit_report , flags = flags
23883 )
@@ -244,7 +89,7 @@ def files_belonging_to_flags(commit_report: Report, flags: List[str]) -> List[st
24489
24590
24691def sessions_with_specific_flags (
247- commit_report : Report , flags : List [str ]
92+ commit_report : Report , flags : list [str ]
24893) -> dict [int , Session ]:
24994 sessions = [
25095 (sid , session )
@@ -254,7 +99,7 @@ def sessions_with_specific_flags(
25499 return dict (sessions )
255100
256101
257- def files_in_sessions (commit_report : Report , session_ids : List [int ]) -> List [str ]:
102+ def files_in_sessions (commit_report : Report , session_ids : list [int ]) -> list [str ]:
258103 files , session_ids = [], set (session_ids )
259104 for file in commit_report :
260105 found = False
0 commit comments