33from helpers .constants import GENDERS
44from countries import countries
55from .models import Grant
6+ from django .db .models import Exists , OuterRef
7+ from submissions .models import Submission
8+ from schedule .models import ScheduleItem
69
710
811class GrantSummary :
@@ -31,13 +34,37 @@ def calculate(self, conference_id):
3134 status_totals ,
3235 totals_per_continent ,
3336 ) = self ._aggregate_data_by_country (grants_by_country , statuses )
37+ sorted_country_stats = dict (
38+ sorted (country_stats .items (), key = lambda x : (x [0 ][0 ], x [0 ][2 ]))
39+ )
40+ country_type_summary = self ._aggregate_data_by_country_type (
41+ filtered_grants , statuses
42+ )
3443 gender_stats = self ._aggregate_data_by_gender (filtered_grants , statuses )
3544 financial_summary , total_amount = self ._aggregate_financial_data_by_status (
3645 filtered_grants , statuses
3746 )
38-
39- sorted_country_stats = dict (
40- sorted (country_stats .items (), key = lambda x : (x [0 ][0 ], x [0 ][2 ]))
47+ grant_type_summary = self ._aggregate_data_by_grant_type (
48+ filtered_grants , statuses
49+ )
50+ speaker_status_summary = self ._aggregate_data_by_speaker_status (
51+ filtered_grants , statuses
52+ )
53+ approved_type_summary = self ._aggregate_data_by_approved_type (
54+ filtered_grants , statuses
55+ )
56+ requested_needs_summary = self ._aggregate_data_by_requested_needs_summary (
57+ filtered_grants , statuses
58+ )
59+ approved_types = {
60+ approved_type .value : approved_type .label
61+ for approved_type in Grant .ApprovedType
62+ }
63+ country_types = {
64+ country_type .value : country_type .label for country_type in Grant .CountryType
65+ }
66+ occupation_summary = self ._aggregate_data_by_occupation (
67+ filtered_grants , statuses
4168 )
4269
4370 return dict (
@@ -52,6 +79,15 @@ def calculate(self, conference_id):
5279 status_totals = status_totals ,
5380 totals_per_continent = totals_per_continent ,
5481 gender_stats = gender_stats ,
82+ preselected_statuses = ["approved" , "confirmed" ],
83+ grant_type_summary = grant_type_summary ,
84+ speaker_status_summary = speaker_status_summary ,
85+ approved_type_summary = approved_type_summary ,
86+ approved_types = approved_types ,
87+ requested_needs_summary = requested_needs_summary ,
88+ country_type_summary = country_type_summary ,
89+ country_types = country_types ,
90+ occupation_summary = occupation_summary ,
5591 )
5692
5793 def _aggregate_data_by_country (self , grants_by_country , statuses ):
@@ -83,6 +119,26 @@ def _aggregate_data_by_country(self, grants_by_country, statuses):
83119
84120 return summary , status_totals , totals_per_continent
85121
122+ def _aggregate_data_by_country_type (self , filtered_grants , statuses ):
123+ """
124+ Aggregates grant data by country type and status.
125+ """
126+ country_type_data = filtered_grants .values ("country_type" , "status" ).annotate (
127+ total = Count ("id" )
128+ )
129+ country_type_summary = {
130+ country_type : {status [0 ]: 0 for status in statuses }
131+ for country_type in Grant .CountryType .values
132+ }
133+
134+ for data in country_type_data :
135+ country_type = data ["country_type" ]
136+ status = data ["status" ]
137+ total = data ["total" ]
138+ country_type_summary [country_type ][status ] += total
139+
140+ return country_type_summary
141+
86142 def _aggregate_data_by_gender (self , filtered_grants , statuses ):
87143 """
88144 Aggregates grant data by gender and status.
@@ -124,3 +180,138 @@ def _aggregate_financial_data_by_status(self, filtered_grants, statuses):
124180 overall_total += total_amount
125181
126182 return financial_summary , overall_total
183+
184+ def _aggregate_data_by_grant_type (self , filtered_grants , statuses ):
185+ """
186+ Aggregates grant data by grant_type and status.
187+ """
188+ grant_type_data = filtered_grants .values ("grant_type" , "status" ).annotate (
189+ total = Count ("id" )
190+ )
191+ grant_type_summary = {
192+ grant_type : {status [0 ]: 0 for status in statuses }
193+ for grant_type in Grant .GrantType .values
194+ }
195+
196+ for data in grant_type_data :
197+ grant_types = data ["grant_type" ]
198+ status = data ["status" ]
199+ total = data ["total" ]
200+ for grant_type in grant_types :
201+ grant_type_summary [grant_type ][status ] += total
202+
203+ return grant_type_summary
204+
205+ def _aggregate_data_by_speaker_status (self , filtered_grants , statuses ):
206+ """
207+ Aggregates grant data by speaker status (proposed and confirmed) and grant status.
208+ """
209+ filtered_grants = filtered_grants .annotate (
210+ is_proposed_speaker = Exists (
211+ Submission .objects .non_cancelled ().filter (
212+ conference_id = OuterRef ("conference_id" ),
213+ speaker_id = OuterRef ("user_id" ),
214+ )
215+ ),
216+ is_confirmed_speaker = Exists (
217+ ScheduleItem .objects .filter (
218+ conference_id = OuterRef ("conference_id" ),
219+ submission__speaker_id = OuterRef ("user_id" ),
220+ )
221+ ),
222+ )
223+
224+ proposed_speaker_data = (
225+ filtered_grants .filter (is_proposed_speaker = True )
226+ .values ("status" )
227+ .annotate (total = Count ("id" ))
228+ )
229+
230+ confirmed_speaker_data = (
231+ filtered_grants .filter (is_confirmed_speaker = True )
232+ .values ("status" )
233+ .annotate (total = Count ("id" ))
234+ )
235+
236+ speaker_status_summary = {
237+ "proposed_speaker" : {status [0 ]: 0 for status in statuses },
238+ "confirmed_speaker" : {status [0 ]: 0 for status in statuses },
239+ }
240+
241+ for data in proposed_speaker_data :
242+ status = data ["status" ]
243+ total = data ["total" ]
244+ speaker_status_summary ["proposed_speaker" ][status ] += total
245+
246+ for data in confirmed_speaker_data :
247+ status = data ["status" ]
248+ total = data ["total" ]
249+ speaker_status_summary ["confirmed_speaker" ][status ] += total
250+
251+ return speaker_status_summary
252+
253+ def _aggregate_data_by_approved_type (self , filtered_grants , statuses ):
254+ """
255+ Aggregates grant data by approved type and status.
256+ """
257+ approved_type_data = filtered_grants .values ("approved_type" , "status" ).annotate (
258+ total = Count ("id" )
259+ )
260+ approved_type_summary = {
261+ approved_type : {status [0 ]: 0 for status in statuses }
262+ for approved_type in Grant .ApprovedType .values
263+ }
264+ approved_type_summary [None ] = {
265+ status [0 ]: 0 for status in statuses
266+ } # For unspecified genders
267+
268+ for data in approved_type_data :
269+ approved_type = data ["approved_type" ]
270+ status = data ["status" ]
271+ total = data ["total" ]
272+ approved_type_summary [approved_type ][status ] += total
273+
274+ return approved_type_summary
275+
276+ def _aggregate_data_by_requested_needs_summary (self , filtered_grants , statuses ):
277+ """
278+ Aggregates grant data by boolean fields (needs_funds_for_travel, need_visa, need_accommodation) and status.
279+ """
280+ requested_needs_summary = {
281+ "needs_funds_for_travel" : {status [0 ]: 0 for status in statuses },
282+ "need_visa" : {status [0 ]: 0 for status in statuses },
283+ "need_accommodation" : {status [0 ]: 0 for status in statuses },
284+ }
285+
286+ for field in requested_needs_summary .keys ():
287+ field_data = (
288+ filtered_grants .filter (** {field : True })
289+ .values ("status" )
290+ .annotate (total = Count ("id" ))
291+ )
292+ for data in field_data :
293+ status = data ["status" ]
294+ total = data ["total" ]
295+ requested_needs_summary [field ][status ] += total
296+
297+ return requested_needs_summary
298+
299+ def _aggregate_data_by_occupation (self , filtered_grants , statuses ):
300+ """
301+ Aggregates grant data by occupation and status.
302+ """
303+ occupation_data = filtered_grants .values ("occupation" , "status" ).annotate (
304+ total = Count ("id" )
305+ )
306+ occupation_summary = {
307+ occupation : {status [0 ]: 0 for status in statuses }
308+ for occupation in Grant .Occupation .values
309+ }
310+
311+ for data in occupation_data :
312+ occupation = data ["occupation" ]
313+ status = data ["status" ]
314+ total = data ["total" ]
315+ occupation_summary [occupation ][status ] += total
316+
317+ return occupation_summary
0 commit comments