1616from metrics .domain .models .charts .dual_category_charts import (
1717 DualCategoryChartRequestParams ,
1818)
19+ from tests .fakes .factories .metrics .metric_factory import FakeMetricFactory
20+ from tests .fakes .managers .metric_manager import FakeMetricManager
21+ from tests .fakes .managers .topic_manager import FakeTopicManager
22+
23+ HEADLINE_METRIC = "COVID-19_headline_vaccines_spring24Uptake"
24+ TIMESERIES_METRIC = "COVID-19_cases_rateRollingMean"
1925
2026
2127class TestDualCategoryChartSegmentSerializer :
@@ -201,26 +207,26 @@ def test_missing_label_is_still_deemed_valid(self):
201207class TestDualCategoryChartSerializer :
202208 # Success cases
203209 @pytest .mark .parametrize (
204- "x_axis,primary_field_values" ,
210+ "metric, x_axis,primary_field_values" ,
205211 [
206- ("age" , ["m" , "f" ]),
207- # Test that primary_field_values can be omitted when x_axis is date
208- ("date" , None ),
209- # Test defaulting to date when x_axis is not provided
210- (None , None ),
212+ (HEADLINE_METRIC , "age" , ["m" , "f" ]),
213+ (TIMESERIES_METRIC , "date" , None ),
214+ (TIMESERIES_METRIC , None , None ),
211215 ],
212216 )
213- def test_validation_with_valid_x_axis_and_primary_field_values_combination (
214- self , x_axis , primary_field_values
217+ def test_validation_with_valid_metric_and_primary_field_values_combination (
218+ self , metric : str , x_axis : str | None , primary_field_values : list [ str ] | None
215219 ):
216220 """
217- Given a payload containing varying x_axis and primary_field_values
221+ Given a payload containing a valid metric and primary_field_values combination
218222 passed to a `DualCategoryChartSerializer` object
219223 When `validate()` is called from the serializer
220- Then no ValidationError is raised and the data is returned as these are valid combination of fields
224+ Then no ValidationError is raised and the data is returned unchanged
221225 """
222226 # Given
223227 valid_payload = EXAMPLE_DUAL_CATEGORY_CHART_REQUEST_PAYLOAD .copy ()
228+ valid_payload ["static_fields" ] = valid_payload ["static_fields" ].copy ()
229+ valid_payload ["static_fields" ]["metric" ] = metric
224230 valid_payload ["x_axis" ] = x_axis
225231 valid_payload ["primary_field_values" ] = primary_field_values
226232
@@ -233,16 +239,15 @@ def test_validation_with_valid_x_axis_and_primary_field_values_combination(
233239 assert is_valid == valid_payload
234240
235241 # Failure cases
236- def test_validation_with_x_axis_and_primary_field_values_present (self ):
242+ def test_validation_with_primary_field_values_for_timeseries_data (self ):
237243 """
238- Given a payload containing x_axis and primary_field_values
244+ Given a payload containing primary_field_values for a timeseries metric
239245 passed to a `DualCategoryChartSerializer` object
240246 When `validate()` is called from the serializer
241- Then a `ValidationError` is raised as primary_field_values should not be provided when x_axis value is date
247+ Then a `ValidationError` is raised as primary_field_values should not be provided
242248 """
243249 # Given
244250 invalid_payload = EXAMPLE_DUAL_CATEGORY_CHART_REQUEST_PAYLOAD .copy ()
245- invalid_payload ["x_axis" ] = "date"
246251 invalid_payload ["primary_field_values" ] = ["m" , "f" ]
247252
248253 serializer = DualCategoryChartSerializer ()
@@ -252,22 +257,43 @@ def test_validation_with_x_axis_and_primary_field_values_present(self):
252257 serializer .validate (attrs = invalid_payload )
253258
254259 assert exc_info .value .detail ["primary_field_values" ] == (
255- "This field should not be provided when x_axis is 'date'."
260+ "This field should not be provided for timeseries data."
261+ )
262+
263+ def test_validation_with_non_date_x_axis_for_timeseries_data (self ):
264+ """
265+ Given a payload containing a non-date x_axis for a timeseries metric
266+ passed to a `DualCategoryChartSerializer` object
267+ When `validate()` is called from the serializer
268+ Then a `ValidationError` is raised as x_axis must be date for timeseries data
269+ """
270+ # Given
271+ invalid_payload = EXAMPLE_DUAL_CATEGORY_CHART_REQUEST_PAYLOAD .copy ()
272+ invalid_payload ["x_axis" ] = "age"
273+
274+ serializer = DualCategoryChartSerializer ()
275+
276+ # When / Then
277+ with pytest .raises (ValidationError ) as exc_info :
278+ serializer .validate (attrs = invalid_payload )
279+
280+ assert exc_info .value .detail ["x_axis" ] == (
281+ "This field should be set to 'date' for timeseries data."
256282 )
257283
258- def test_validation_with_primary_field_values_missing_when_x_axis_is_not_date (self ):
284+ def test_validation_with_primary_field_values_missing_for_headline_data (self ):
259285 """
260- Given a payload containing x_axis but no primary_field_values
286+ Given a payload containing a headline metric but no primary_field_values
261287 passed to a `DualCategoryChartSerializer` object
262288 When `validate()` is called from the serializer
263- Then a `ValidationError` is raised as primary_field_values should be provided when x_axis value is not date
289+ Then a `ValidationError` is raised as primary_field_values are required
264290 """
265291 # Given
266292 invalid_payload = EXAMPLE_DUAL_CATEGORY_CHART_REQUEST_PAYLOAD .copy ()
293+ invalid_payload ["static_fields" ] = invalid_payload ["static_fields" ].copy ()
294+ invalid_payload ["static_fields" ]["metric" ] = HEADLINE_METRIC
267295 invalid_payload ["x_axis" ] = "age"
268- invalid_payload .pop (
269- "primary_field_values"
270- ) # Remove primary_field_values to simulate it being missing
296+ invalid_payload .pop ("primary_field_values" , None )
271297
272298 serializer = DualCategoryChartSerializer ()
273299
@@ -276,7 +302,7 @@ def test_validation_with_primary_field_values_missing_when_x_axis_is_not_date(se
276302 serializer .validate (attrs = invalid_payload )
277303
278304 assert exc_info .value .detail ["primary_field_values" ] == (
279- "This field is required when x_axis is not 'date' ."
305+ "This field is required for headline data ."
280306 )
281307
282308 def test_to_models_builds_timeseries_plots_when_x_axis_is_date (
@@ -330,3 +356,59 @@ def test_to_models_builds_timeseries_plots_when_x_axis_is_date(
330356 assert plot .metric == static_fields ["metric" ]
331357 assert plot .date_from == static_fields ["date_from" ]
332358 assert plot .date_to == static_fields ["date_to" ]
359+
360+ def test_to_models_builds_headline_plots_with_primary_field_values (self ):
361+ """
362+ Given a valid payload with a headline metric and primary field values
363+ When `to_models()` is called from the serializer
364+ Then one plot per segment is returned for each primary field value
365+ """
366+ # Given
367+ fake_metric = FakeMetricFactory .build_example_metric (
368+ metric_name = HEADLINE_METRIC ,
369+ metric_group_name = "headline" ,
370+ )
371+ fake_topic = fake_metric .metric_group .topic
372+ metric_manager = FakeMetricManager ([fake_metric ])
373+ topic_manager = FakeTopicManager ([fake_topic ])
374+ valid_payload = EXAMPLE_DUAL_CATEGORY_CHART_REQUEST_PAYLOAD .copy ()
375+ valid_payload ["static_fields" ] = valid_payload ["static_fields" ].copy ()
376+ valid_payload ["static_fields" ]["topic" ] = fake_topic .name
377+ valid_payload ["static_fields" ]["metric" ] = HEADLINE_METRIC
378+ valid_payload ["x_axis" ] = ChartAxisFields .sex .name
379+ valid_payload ["primary_field_values" ] = ["m" , "f" ]
380+
381+ serializer_context = {
382+ "topic_manager" : topic_manager ,
383+ "metric_manager" : metric_manager ,
384+ }
385+ serializer = DualCategoryChartSerializer (
386+ data = valid_payload ,
387+ context = serializer_context ,
388+ )
389+ serializer .fields ["static_fields" ] = PlotSerializer (context = serializer_context )
390+ serializer .is_valid (raise_exception = True )
391+
392+ # When
393+ result : DualCategoryChartRequestParams = serializer .to_models (request = None )
394+
395+ # Then
396+ segments = valid_payload ["segments" ]
397+ secondary_category = valid_payload ["secondary_category" ]
398+ x_axis = valid_payload ["x_axis" ]
399+
400+ assert result .primary_field_values == ["m" , "f" ]
401+ assert len (result .plots ) == len (segments ) * len (
402+ valid_payload ["primary_field_values" ]
403+ )
404+
405+ for index , plot in enumerate (result .plots ):
406+ segment_index = index % len (segments )
407+ primary_index = index // len (segments )
408+ segment = segments [segment_index ]
409+ primary_field_value = valid_payload ["primary_field_values" ][primary_index ]
410+
411+ assert plot .x_axis == x_axis
412+ assert getattr (plot , x_axis ) == primary_field_value
413+ assert getattr (plot , secondary_category ) == segment ["secondary_field_value" ]
414+ assert plot .line_colour == segment ["colour" ]
0 commit comments