1717 AuthoringTool ,
1818)
1919
20- months = list (calendar .month_name )[1 :]
20+ MONTHS = list (calendar .month_name )[1 :]
2121
2222PERIODS = {
2323 "month" : {
2424 "annotate" : lambda qs : qs .annotate (period = ExtractMonth ("created" )),
25- "label" : lambda row : months [row ["period" ] - 1 ],
26- "full_set" : months ,
25+ "label" : lambda row : MONTHS [row ["period" ] - 1 ],
26+ "full_set" : MONTHS ,
2727 },
2828 "week" : {
2929 "annotate" : lambda qs : qs .annotate (period = ExtractWeek ("created" )),
5252 },
5353}
5454
55-
5655COLORS = {
5756 "primary" : "#79aec8" ,
5857 "success" : "#55efc4" ,
7372 "HEADER_SYNTAX" : "SYNTAX" ,
7473}
7574
76-
7775TASK_TYPES = {
7876 "SYNTAX" : ("Syntax" , COLORS ["success" ]),
7977 "SCHEMA" : ("Schema" , COLORS ["schema" ]),
@@ -120,7 +118,8 @@ def chart_response(title, labels, datasets):
120118 "datasets" : datasets ,
121119 },
122120 })
123-
121+
122+
124123def group_by_period (qs , period , agg_key , agg_expression , window = None ):
125124 cfg = PERIODS [period ]
126125 qs = cfg ["annotate" ](qs )
@@ -131,7 +130,7 @@ def group_by_period(qs, period, agg_key, agg_expression, window=None):
131130 qs = qs .filter (
132131 Q (period__in = [
133132 int (lbl [1 :]) if period in ["week" , "quarter" ]
134- else months .index (lbl ) + 1 if period == "month"
133+ else MONTHS .index (lbl ) + 1 if period == "month"
135134 else lbl
136135 for lbl in valid_labels
137136 ])
@@ -142,6 +141,7 @@ def group_by_period(qs, period, agg_key, agg_expression, window=None):
142141 .order_by ("period" )
143142 )
144143
144+
145145def fill_period_dict (grouped_qs , period , year , * , key , transform = lambda x : x , window = None ):
146146 data = dict_for_period (period , year = year , window = window )
147147 cfg = PERIODS [period ]
@@ -153,9 +153,11 @@ def fill_period_dict(grouped_qs, period, year, *, key, transform=lambda x: x, wi
153153 data [label ] = round (value , 2 )
154154 return data
155155
156+
156157def get_period (request ):
157158 return request .GET .get ("period" , "month" ).lower ()
158159
160+
159161def get_window (request ) -> int | None :
160162 try :
161163 w = int (request .GET .get ("window" , "" ))
@@ -201,7 +203,7 @@ def _rolling_labels(period: str, window: int, today: datetime.date | None = None
201203 month_cursor = today .replace (day = 1 )
202204 labels = []
203205 for _ in range (window ):
204- labels .insert (0 , months [month_cursor .month - 1 ])
206+ labels .insert (0 , MONTHS [month_cursor .month - 1 ])
205207 month_cursor = (month_cursor - datetime .timedelta (days = 1 )).replace (day = 1 )
206208 return labels
207209
@@ -390,7 +392,7 @@ def get_duration_per_task_chart(request, year):
390392 valid_labels = set (_rolling_labels (period , window ))
391393 label_values = [
392394 int (lbl [1 :]) if period in ["week" , "quarter" ]
393- else months .index (lbl ) + 1 if period == "month"
395+ else MONTHS .index (lbl ) + 1 if period == "month"
394396 else lbl
395397 for lbl in valid_labels
396398 ]
@@ -437,7 +439,6 @@ def get_duration_per_task_chart(request, year):
437439 )
438440
439441
440-
441442@staff_member_required
442443def get_processing_status_chart (request , year ):
443444 period = get_period (request )
@@ -629,6 +630,78 @@ def get_usage_by_vendor_chart(request, year):
629630 )
630631
631632
633+ @staff_member_required
634+ def get_usage_by_channel_chart (request , year ):
635+ """
636+ Distinct channels per period, split into WebUI vs API.
637+ """
638+ period = get_period (request )
639+ window = get_window (request )
640+
641+ if period == "total" :
642+ qs = ValidationRequest .objects .all ()
643+ total_uploaders = qs .values ("channel" ).distinct ().count ()
644+ api_uploaders = qs .filter (
645+ channel = ValidationRequest .Channel .API
646+ ).values ("id" ).distinct ().count ()
647+ webui_uploaders = total_uploaders - api_uploaders
648+
649+ return chart_response (
650+ title = "Channels (WebUI vs API, Total)" ,
651+ labels = ["WebUI" , "API" ],
652+ datasets = [{
653+ "label" : "Channels" ,
654+ "backgroundColor" : [COLORS ["primary" ], COLORS ["success" ]],
655+ "borderColor" : [COLORS ["primary" ], COLORS ["success" ]],
656+ "data" : [webui_uploaders , api_uploaders ],
657+ }]
658+ )
659+
660+ qs = ValidationRequest .objects .filter (created__year = year )
661+
662+ total_qs = group_by_period (
663+ qs ,
664+ period ,
665+ "total" ,
666+ Count ("id" , distinct = True ),
667+ window = window
668+ )
669+
670+ api_qs = group_by_period (
671+ qs .filter (channel = ValidationRequest .Channel .API ),
672+ period ,
673+ ValidationRequest .Channel .API ,
674+ Count ("id" , distinct = True ),
675+ window = window
676+ )
677+
678+ total_dict = fill_period_dict (total_qs , period , year , key = "total" , transform = int , window = window )
679+ api_dict = fill_period_dict (api_qs , period , year , key = "API" , transform = int , window = window )
680+ webui_dict = {lbl : total_dict [lbl ] - api_dict .get (lbl , 0 ) for lbl in total_dict }
681+ labels = list (total_dict .keys ())
682+
683+ return chart_response (
684+ title = f"Channels (WebUI vs API) in { year } " ,
685+ labels = labels ,
686+ datasets = [
687+ {
688+ "label" : "WebUI" ,
689+ "backgroundColor" : COLORS ["primary" ],
690+ "borderColor" : COLORS ["primary" ],
691+ "data" : [webui_dict [lbl ] for lbl in labels ],
692+ "stack" : "stack1" ,
693+ },
694+ {
695+ "label" : "API" ,
696+ "backgroundColor" : COLORS ["success" ],
697+ "borderColor" : COLORS ["success" ],
698+ "data" : [api_dict [lbl ] for lbl in labels ],
699+ "stack" : "stack1" ,
700+ },
701+ ],
702+ )
703+
704+
632705@staff_member_required
633706def get_models_by_vendor_chart (request , year ):
634707 """
@@ -676,7 +749,7 @@ def get_models_by_vendor_chart(request, year):
676749 valid_labels = set (_rolling_labels (period , window ))
677750 label_values = [
678751 int (lbl [1 :]) if period in ["week" , "quarter" ]
679- else months .index (lbl ) + 1 if period == "month"
752+ else MONTHS .index (lbl ) + 1 if period == "month"
680753 else lbl
681754 for lbl in valid_labels
682755 ]
@@ -812,7 +885,6 @@ def get_tools_count_chart(request, year):
812885 )
813886
814887
815-
816888@staff_member_required
817889def get_totals (request ):
818890 # Overall, non time-split totals
@@ -851,7 +923,7 @@ def get_uploads_per_2h_chart(request, year):
851923 valid_labels = set (_rolling_labels (period , window ))
852924 label_values = [
853925 int (lbl [1 :]) if period in ["week" , "quarter" ]
854- else months .index (lbl ) + 1 if period == "month"
926+ else MONTHS .index (lbl ) + 1 if period == "month"
855927 else lbl
856928 for lbl in valid_labels
857929 ]
@@ -883,6 +955,7 @@ def get_uploads_per_2h_chart(request, year):
883955 }],
884956 )
885957
958+
886959@staff_member_required
887960def get_queue_p95_chart (request , year ):
888961 period = get_period (request )
@@ -899,7 +972,7 @@ def get_queue_p95_chart(request, year):
899972 valid_labels = set (_rolling_labels (period , window ))
900973 label_values = [
901974 int (lbl [1 :]) if period in ["week" , "quarter" ]
902- else months .index (lbl ) + 1 if period == "month"
975+ else MONTHS .index (lbl ) + 1 if period == "month"
903976 else lbl
904977 for lbl in valid_labels
905978 ]
@@ -965,7 +1038,7 @@ def get_stuck_per_day_chart(request, year):
9651038 valid_labels = set (_rolling_labels (period , window ))
9661039 label_values = [
9671040 int (lbl [1 :]) if period in ["week" , "quarter" ]
968- else months .index (lbl ) + 1 if period == "month"
1041+ else MONTHS .index (lbl ) + 1 if period == "month"
9691042 else lbl
9701043 for lbl in valid_labels
9711044 ]
@@ -997,6 +1070,7 @@ def get_stuck_per_day_chart(request, year):
9971070 }],
9981071 )
9991072
1073+
10001074@staff_member_required
10011075def get_uploads_per_weekday_chart (request , year ):
10021076 period = get_period (request )
@@ -1013,7 +1087,7 @@ def get_uploads_per_weekday_chart(request, year):
10131087 valid_labels = set (_rolling_labels (period , window ))
10141088 label_values = [
10151089 int (lbl [1 :]) if period in ["week" , "quarter" ]
1016- else months .index (lbl ) + 1 if period == "month"
1090+ else MONTHS .index (lbl ) + 1 if period == "month"
10171091 else lbl
10181092 for lbl in valid_labels
10191093 ]
0 commit comments