11from collections import defaultdict
22
3- from django .db .models import Count , Exists , OuterRef , Sum
3+ from django .db .models import Case , Count , DecimalField , Exists , OuterRef , Sum , When
4+ from django .db .models .functions import Coalesce
45
56from conferences .models .conference import Conference
67from countries import countries
@@ -26,10 +27,12 @@ def calculate(self, conference_id):
2627 """
2728 statuses = Grant .Status .choices
2829 conference = Conference .objects .get (id = conference_id )
29- filtered_grants = Grant .objects .for_conference (conference )
30+ filtered_grants = Grant .objects .for_conference (conference ).annotate (
31+ effective_status = Coalesce ("pending_status" , "status" )
32+ )
3033
3134 grants_by_country = filtered_grants .values (
32- "departure_country" , "pending_status "
35+ "departure_country" , "effective_status "
3336 ).annotate (total = Count ("id" ))
3437
3538 (
@@ -99,6 +102,7 @@ def _aggregate_data_by_country(self, grants_by_country, statuses):
99102 totals_per_continent = {}
100103
101104 for data in grants_by_country :
105+ effective_status : str = data ["effective_status" ]
102106 country = countries .get (code = data ["departure_country" ])
103107 continent = country .continent .name if country else "Unknown"
104108 country_name = f"{ country .name } { country .emoji } " if country else "Unknown"
@@ -108,13 +112,13 @@ def _aggregate_data_by_country(self, grants_by_country, statuses):
108112 if key not in summary :
109113 summary [key ] = {status [0 ]: 0 for status in statuses }
110114
111- summary [key ][data [ "pending_status" ] ] += data ["total" ]
112- status_totals [data [ "pending_status" ] ] += data ["total" ]
115+ summary [key ][effective_status ] += data ["total" ]
116+ status_totals [effective_status ] += data ["total" ]
113117
114118 # Update continent totals
115119 if continent not in totals_per_continent :
116120 totals_per_continent [continent ] = {status [0 ]: 0 for status in statuses }
117- totals_per_continent [continent ][data [ "pending_status" ] ] += data ["total" ]
121+ totals_per_continent [continent ][effective_status ] += data ["total" ]
118122
119123 return summary , status_totals , totals_per_continent
120124
@@ -123,83 +127,106 @@ def _aggregate_data_by_country_type(self, filtered_grants, statuses):
123127 Aggregates grant data by country type and status.
124128 """
125129 country_type_data = filtered_grants .values (
126- "country_type" , "pending_status "
130+ "country_type" , "effective_status "
127131 ).annotate (total = Count ("id" ))
128132 country_type_summary = defaultdict (
129133 lambda : {status [0 ]: 0 for status in statuses }
130134 )
131135
132136 for data in country_type_data :
133137 country_type = data ["country_type" ]
134- pending_status = data ["pending_status " ]
138+ effective_status : str = data ["effective_status " ]
135139 total = data ["total" ]
136- country_type_summary [country_type ][pending_status ] += total
140+ country_type_summary [country_type ][effective_status ] += total
137141
138142 return dict (country_type_summary )
139143
140144 def _aggregate_data_by_gender (self , filtered_grants , statuses ):
141145 """
142146 Aggregates grant data by gender and status.
143147 """
144- gender_data = filtered_grants .values ("gender" , "pending_status " ).annotate (
148+ gender_data = filtered_grants .values ("gender" , "effective_status " ).annotate (
145149 total = Count ("id" )
146150 )
147151 gender_summary = defaultdict (lambda : {status [0 ]: 0 for status in statuses })
148152
149153 for data in gender_data :
150154 gender = data ["gender" ] if data ["gender" ] else ""
151- pending_status = data ["pending_status " ]
155+ effective_status : str = data ["effective_status " ]
152156 total = data ["total" ]
153- gender_summary [gender ][pending_status ] += total
157+ gender_summary [gender ][effective_status ] += total
154158
155159 return dict (gender_summary )
156160
157161 def _aggregate_financial_data_by_status (self , filtered_grants , statuses ):
158162 """
159- Aggregates financial data (total amounts) by grant status.
163+ Aggregates financial data (total amounts) by grant status
164+ using conditional aggregation in a single query.
160165 """
161- financial_summary = {status [0 ]: 0 for status in statuses }
162- overall_total = 0
163-
164- for status in statuses :
165- grants_for_status = filtered_grants .filter (pending_status = status [0 ])
166- reimbursements = GrantReimbursement .objects .filter (
167- grant__in = grants_for_status
166+ reimbursements = GrantReimbursement .objects .filter (
167+ grant__in = filtered_grants
168+ ).annotate (effective_status = Coalesce ("grant__pending_status" , "grant__status" ))
169+
170+ aggregations : dict [str , Sum ] = {
171+ status_value : Sum (
172+ Case (
173+ When (effective_status = status_value , then = "granted_amount" ),
174+ default = 0 ,
175+ output_field = DecimalField (),
176+ )
168177 )
169- total = reimbursements .aggregate (total = Sum ("granted_amount" ))["total" ] or 0
170- financial_summary [status [0 ]] = total
171- if status [0 ] in self .BUDGET_STATUSES :
172- overall_total += total
178+ for status_value , _ in statuses
179+ }
180+ result = reimbursements .aggregate (** aggregations )
181+
182+ financial_summary : dict [str , int ] = {
183+ status_value : result [status_value ] or 0 for status_value , _ in statuses
184+ }
185+ overall_total : int = sum (
186+ amount
187+ for status_value , amount in financial_summary .items ()
188+ if status_value in self .BUDGET_STATUSES
189+ )
173190
174191 return financial_summary , overall_total
175192
176193 def _aggregate_data_by_reimbursement_category (self , filtered_grants , statuses ):
177194 """
178195 Aggregates grant data by reimbursement category and status.
179196 """
180- category_summary = defaultdict (lambda : {status [0 ]: 0 for status in statuses })
181- reimbursements = GrantReimbursement .objects .filter (grant__in = filtered_grants )
182- for r in reimbursements :
183- category = r .category .category
184- status = r .grant .pending_status
185- category_summary [category ][status ] += 1
197+ category_data = (
198+ GrantReimbursement .objects .filter (grant__in = filtered_grants )
199+ .annotate (
200+ effective_status = Coalesce ("grant__pending_status" , "grant__status" )
201+ )
202+ .values ("category__category" , "effective_status" )
203+ .annotate (total = Count ("id" ))
204+ )
205+ category_summary : dict [str , dict [str , int ]] = defaultdict (
206+ lambda : {status [0 ]: 0 for status in statuses }
207+ )
208+ for data in category_data :
209+ category : str = data ["category__category" ]
210+ effective_status : str = data ["effective_status" ]
211+ total : int = data ["total" ]
212+ category_summary [category ][effective_status ] += total
186213 return dict (category_summary )
187214
188215 def _aggregate_data_by_grant_type (self , filtered_grants , statuses ):
189216 """
190217 Aggregates grant data by grant_type and status.
191218 """
192219 grant_type_data = filtered_grants .values (
193- "grant_type" , "pending_status "
220+ "grant_type" , "effective_status "
194221 ).annotate (total = Count ("id" ))
195222 grant_type_summary = defaultdict (lambda : {status [0 ]: 0 for status in statuses })
196223
197224 for data in grant_type_data :
198225 grant_types = data ["grant_type" ]
199- pending_status = data ["pending_status " ]
226+ effective_status : str = data ["effective_status " ]
200227 total = data ["total" ]
201228 for grant_type in grant_types :
202- grant_type_summary [grant_type ][pending_status ] += total
229+ grant_type_summary [grant_type ][effective_status ] += total
203230
204231 return dict (grant_type_summary )
205232
@@ -224,13 +251,13 @@ def _aggregate_data_by_speaker_status(self, filtered_grants, statuses):
224251
225252 proposed_speaker_data = (
226253 filtered_grants .filter (is_proposed_speaker = True )
227- .values ("pending_status " )
254+ .values ("effective_status " )
228255 .annotate (total = Count ("id" ))
229256 )
230257
231258 confirmed_speaker_data = (
232259 filtered_grants .filter (is_confirmed_speaker = True )
233- .values ("pending_status " )
260+ .values ("effective_status " )
234261 .annotate (total = Count ("id" ))
235262 )
236263
@@ -239,14 +266,16 @@ def _aggregate_data_by_speaker_status(self, filtered_grants, statuses):
239266 )
240267
241268 for data in proposed_speaker_data :
242- pending_status = data ["pending_status " ]
269+ effective_status : str = data ["effective_status " ]
243270 total = data ["total" ]
244- speaker_status_summary ["proposed_speaker" ][pending_status ] += total
271+ speaker_status_summary ["proposed_speaker" ][effective_status ] += total
245272
246273 for data in confirmed_speaker_data :
247- pending_status = data ["pending_status " ]
274+ effective_status_confirmed : str = data ["effective_status " ]
248275 total = data ["total" ]
249- speaker_status_summary ["confirmed_speaker" ][pending_status ] += total
276+ speaker_status_summary ["confirmed_speaker" ][effective_status_confirmed ] += (
277+ total
278+ )
250279
251280 return dict (speaker_status_summary )
252281
@@ -263,13 +292,13 @@ def _aggregate_data_by_requested_needs_summary(self, filtered_grants, statuses):
263292 for field in requested_needs_summary .keys ():
264293 field_data = (
265294 filtered_grants .filter (** {field : True })
266- .values ("pending_status " )
295+ .values ("effective_status " )
267296 .annotate (total = Count ("id" ))
268297 )
269298 for data in field_data :
270- pending_status = data ["pending_status " ]
299+ effective_status : str = data ["effective_status " ]
271300 total = data ["total" ]
272- requested_needs_summary [field ][pending_status ] += total
301+ requested_needs_summary [field ][effective_status ] += total
273302
274303 return requested_needs_summary
275304
@@ -278,14 +307,14 @@ def _aggregate_data_by_occupation(self, filtered_grants, statuses):
278307 Aggregates grant data by occupation and status.
279308 """
280309 occupation_data = filtered_grants .values (
281- "occupation" , "pending_status "
310+ "occupation" , "effective_status "
282311 ).annotate (total = Count ("id" ))
283312 occupation_summary = defaultdict (lambda : {status [0 ]: 0 for status in statuses })
284313
285314 for data in occupation_data :
286315 occupation = data ["occupation" ]
287- pending_status = data ["pending_status " ]
316+ effective_status : str = data ["effective_status " ]
288317 total = data ["total" ]
289- occupation_summary [occupation ][pending_status ] += total
318+ occupation_summary [occupation ][effective_status ] += total
290319
291320 return dict (occupation_summary )
0 commit comments