1010 DEFAULT_CHART_WIDTH ,
1111 DEFAULT_X_AXIS ,
1212 DEFAULT_Y_AXIS ,
13+ ChartAxisFields ,
1314 ChartTypes ,
15+ DataSourceFileType ,
1416 DEFAULT_Y_AXIS_MINIMUM_VAlUE ,
17+ extract_metric_group_from_metric ,
18+ )
19+ from metrics .domain .models .charts .dual_category_charts import (
20+ DualCategoryChartRequestParams ,
1521)
16- from metrics .domain .models .charts import DualCategoryChartRequestParams
1722
1823
1924class DualCategoryChartSegmentSerializer (serializers .Serializer ):
@@ -33,59 +38,122 @@ class DualCategoryChartSegmentSerializer(serializers.Serializer):
3338 )
3439
3540
36- class StaticFieldsSerializer (PlotSerializer ):
37- theme = serializers .CharField (
38- required = True ,
39- allow_blank = True ,
40- allow_null = True ,
41- )
42- sub_theme = serializers .CharField (
43- required = True ,
44- allow_blank = True ,
45- allow_null = True ,
46- )
47-
48-
4941class DualCategoryChartSerializer (BaseChartsSerializer ):
5042 chart_type = serializers .ChoiceField (
5143 help_text = help_texts .CHART_TYPE_FIELD ,
52- choices = ChartTypes .selectable_choices (),
44+ choices = ChartTypes .dual_category_chart_options (),
5345 required = True ,
5446 )
5547 primary_field_values = serializers .ListField (
5648 child = serializers .CharField (),
5749 help_text = "List of primary field values for this segment" ,
58- required = True ,
59- allow_empty = False ,
50+ required = False ,
51+ allow_empty = True ,
6052 )
6153
6254 secondary_category = serializers .CharField (
6355 help_text = "Secondary category field for the chart" ,
6456 required = True ,
6557 )
6658
67- static_fields = StaticFieldsSerializer ()
59+ static_fields = PlotSerializer ()
6860
6961 segments = serializers .ListField (
7062 child = DualCategoryChartSegmentSerializer (),
7163 help_text = "Segments for the dual category chart" ,
7264 required = True ,
7365 )
7466
67+ @classmethod
68+ def validate (cls , attrs : dict ) -> dict :
69+ """Validate primary_field_values based on the selected x-axis."""
70+ x_axis = attrs .get ("x_axis" ) or DEFAULT_X_AXIS
71+ primary_field_values = attrs .get ("primary_field_values" ) or []
72+ metric = attrs ["static_fields" ]["metric" ]
73+ metric_group = extract_metric_group_from_metric (metric = metric )
74+ is_timeseries_data = DataSourceFileType [metric_group ].is_timeseries
75+
76+ if is_timeseries_data :
77+ if primary_field_values :
78+ raise serializers .ValidationError (
79+ {
80+ "primary_field_values" : (
81+ "This field should not be provided for timeseries data."
82+ )
83+ }
84+ )
85+ if x_axis != ChartAxisFields .date .name :
86+ raise serializers .ValidationError (
87+ {
88+ "x_axis" : (
89+ "This field should be set to 'date' for timeseries data."
90+ )
91+ }
92+ )
93+
94+ elif not is_timeseries_data and not primary_field_values :
95+ raise serializers .ValidationError (
96+ {"primary_field_values" : ("This field is required for headline data." )}
97+ )
98+
99+ return attrs
100+
75101 def to_models (self , request : Request ) -> DualCategoryChartRequestParams :
76102 x_axis = self .data .get ("x_axis" ) or DEFAULT_X_AXIS
77103 y_axis = self .data .get ("y_axis" ) or DEFAULT_Y_AXIS
78104
79- for plot in self .data ["segments" ]:
80- plot ["x_axis" ] = x_axis
81- plot ["y_axis" ] = y_axis
105+ primary_field_values = self .data .get ("primary_field_values" ) or []
106+ secondary_category = self .data ["secondary_category" ]
107+ static_fields : dict [str , str | int ] = self .validated_data .pop ("static_fields" )
108+
109+ if static_fields ["date_to" ]:
110+ static_fields ["date_to" ] = static_fields ["date_to" ].isoformat ()
111+
112+ if static_fields ["date_from" ]:
113+ static_fields ["date_from" ] = static_fields ["date_from" ].isoformat ()
114+
115+ groups_plots = []
116+ segments : list [dict ] = self .data ["segments" ]
117+
118+ # If the x-axis is date (timeseries data)
119+ if x_axis == ChartAxisFields .date .name :
120+ plots = [
121+ {
122+ "x_axis" : x_axis ,
123+ "y_axis" : y_axis ,
124+ "line_colour" : segment ["colour" ],
125+ ** static_fields ,
126+ secondary_category : segment ["secondary_field_value" ],
127+ "chart_type" : self .data ["chart_type" ],
128+ "label" : segment ["label" ],
129+ }
130+ for segment in segments
131+ ]
132+ groups_plots .extend (plots )
133+
134+ else :
135+ for primary_field_value in primary_field_values :
136+ plots = [
137+ {
138+ "x_axis" : x_axis ,
139+ "y_axis" : y_axis ,
140+ "line_colour" : segment ["colour" ],
141+ ** static_fields ,
142+ x_axis : primary_field_value ,
143+ secondary_category : segment ["secondary_field_value" ],
144+ "chart_type" : self .data ["chart_type" ],
145+ "label" : segment ["label" ],
146+ }
147+ for segment in segments
148+ ]
149+ groups_plots .extend (plots )
82150
83151 return DualCategoryChartRequestParams (
84152 chart_type = self .data ["chart_type" ],
85- primary_field_values = self . data [ " primary_field_values" ] ,
153+ primary_field_values = primary_field_values ,
86154 secondary_category = self .data ["secondary_category" ],
87155 static_fields = self .data ["static_fields" ],
88- segments = self . data [ "segments" ] ,
156+ plots = groups_plots ,
89157 file_format = self .data ["file_format" ],
90158 chart_height = self .data ["chart_height" ] or DEFAULT_CHART_HEIGHT ,
91159 chart_width = self .data ["chart_width" ] or DEFAULT_CHART_WIDTH ,
@@ -97,4 +165,5 @@ def to_models(self, request: Request) -> DualCategoryChartRequestParams:
97165 or DEFAULT_Y_AXIS_MINIMUM_VAlUE ,
98166 y_axis_maximum_value = self .data ["y_axis_maximum_value" ],
99167 request = request ,
168+ legend_title = self .data .get ("legend_title" , "" ),
100169 )
0 commit comments