Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions metrics/api/serializers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from .charts import ChartsSerializer
from .dual_category_tables import (
DualCategoryTablesSerializer,
DualCategoryTablesResponseSerializer,
)
from .headlines import HeadlinesQuerySerializer, CoreHeadlineSerializer
from .trends import TrendsQuerySerializer, TrendsResponseSerializer
from .downloads import (
Expand Down
1 change: 0 additions & 1 deletion metrics/api/serializers/charts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
ChartPlotSerializer,
ChartPlotsListSerializer,
ChartsResponseSerializer,
EncodedChartResponseSerializer,
EncodedChartsRequestSerializer,
ChartsSerializer,
)
20 changes: 20 additions & 0 deletions metrics/api/serializers/charts/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@


class BaseChartsSerializer(serializers.Serializer):
"""Base serializer for chart request payloads, containing common fields across different chart types."""

file_format = serializers.ChoiceField(
choices=FILE_FORMAT_CHOICES,
help_text=help_texts.CHART_FILE_FORMAT_FIELD,
Expand Down Expand Up @@ -94,3 +96,21 @@ class BaseChartsSerializer(serializers.Serializer):
allow_null=True,
default="",
)


class ChartPreviewQueryParamsSerializer(serializers.Serializer):
"""Serializer for query parameters when requesting a chart preview."""

preview = serializers.BooleanField(required=False)


class EncodedChartResponseSerializer(serializers.Serializer):
"""Serializer for the response of an encoded chart generation, containing the encoded chart and related metadata."""

last_updated = serializers.CharField(
help_text=help_texts.ENCODED_CHARTS_LAST_UPDATED,
allow_blank=True,
)
chart = serializers.CharField(help_text=help_texts.ENCODED_CHARTS_RESPONSE)
alt_text = serializers.CharField(help_text=help_texts.CHARTS_ALT_TEXT)
figure = serializers.DictField(help_text=help_texts.CHARTS_FIGURE_OUTPUT)
118 changes: 95 additions & 23 deletions metrics/api/serializers/charts/dual_category_charts.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@
DEFAULT_CHART_WIDTH,
DEFAULT_X_AXIS,
DEFAULT_Y_AXIS,
ChartAxisFields,
ChartTypes,
DataSourceFileType,
DEFAULT_Y_AXIS_MINIMUM_VAlUE,
extract_metric_group_from_metric,
)
from metrics.domain.models.charts.dual_category_charts import (
DualCategoryChartRequestParams,
)
from metrics.domain.models.charts import DualCategoryChartRequestParams


class DualCategoryChartSegmentSerializer(serializers.Serializer):
Expand All @@ -33,59 +38,125 @@ class DualCategoryChartSegmentSerializer(serializers.Serializer):
)


class StaticFieldsSerializer(PlotSerializer):
theme = serializers.CharField(
required=True,
allow_blank=True,
allow_null=True,
)
sub_theme = serializers.CharField(
required=True,
allow_blank=True,
allow_null=True,
)


Comment thread
aidan marked this conversation as resolved.
class DualCategoryChartSerializer(BaseChartsSerializer):
chart_type = serializers.ChoiceField(
help_text=help_texts.CHART_TYPE_FIELD,
choices=ChartTypes.selectable_choices(),
choices=ChartTypes.dual_category_chart_options(),
required=True,
)
primary_field_values = serializers.ListField(
child=serializers.CharField(),
help_text="List of primary field values for this segment",
required=True,
allow_empty=False,
required=False,
allow_empty=True,
)

secondary_category = serializers.CharField(
help_text="Secondary category field for the chart",
required=True,
)

static_fields = StaticFieldsSerializer()
static_fields = PlotSerializer()

segments = serializers.ListField(
child=DualCategoryChartSegmentSerializer(),
help_text="Segments for the dual category chart",
required=True,
)

@classmethod
def validate(cls, attrs: dict) -> dict:
"""Validate primary_field_values based on the selected x-axis."""
x_axis = attrs.get("x_axis") or DEFAULT_X_AXIS
primary_field_values = attrs.get("primary_field_values") or []
metric = attrs["static_fields"]["metric"]
metric_group = extract_metric_group_from_metric(metric=metric)
is_timeseries_data = DataSourceFileType[metric_group].is_timeseries

if is_timeseries_data:
if primary_field_values:
raise serializers.ValidationError(
{
"primary_field_values": (
"This field should not be provided for timeseries data."
)
}
)
if x_axis != ChartAxisFields.date.name:
raise serializers.ValidationError(
{
"x_axis": (
"This field should be set to 'date' for timeseries data."
)
}
)

elif not is_timeseries_data and not primary_field_values:
raise serializers.ValidationError(
{"primary_field_values": ("This field is required for headline data.")}
)

return attrs

def to_models(self, request: Request) -> DualCategoryChartRequestParams:
x_axis = self.data.get("x_axis") or DEFAULT_X_AXIS
y_axis = self.data.get("y_axis") or DEFAULT_Y_AXIS

for plot in self.data["segments"]:
plot["x_axis"] = x_axis
plot["y_axis"] = y_axis
primary_field_values = self.data.get("primary_field_values") or []
secondary_category = self.data["secondary_category"]
static_fields: dict[str, str | int] = self.validated_data.pop("static_fields")

if static_fields["date_to"]:
static_fields["date_to"] = static_fields["date_to"].isoformat()

if static_fields["date_from"]:
static_fields["date_from"] = static_fields["date_from"].isoformat()

groups_plots = []
segments: list[dict] = self.data["segments"]

metric_group = extract_metric_group_from_metric(metric=static_fields["metric"])
is_timeseries_data = DataSourceFileType[metric_group].is_timeseries

# If timeseries data
if is_timeseries_data:
plots = [
{
"x_axis": x_axis,
"y_axis": y_axis,
"line_colour": segment["colour"],
**static_fields,
secondary_category: segment["secondary_field_value"],
"chart_type": self.data["chart_type"],
"label": segment["label"],
}
for segment in segments
]
groups_plots.extend(plots)

else:
for primary_field_value in primary_field_values:
plots = [
{
"x_axis": x_axis,
"y_axis": y_axis,
"line_colour": segment["colour"],
**static_fields,
x_axis: primary_field_value,
secondary_category: segment["secondary_field_value"],
"chart_type": self.data["chart_type"],
"label": segment["label"],
}
for segment in segments
]
groups_plots.extend(plots)

return DualCategoryChartRequestParams(
chart_type=self.data["chart_type"],
primary_field_values=self.data["primary_field_values"],
primary_field_values=primary_field_values,
secondary_category=self.data["secondary_category"],
static_fields=self.data["static_fields"],
segments=self.data["segments"],
plots=groups_plots,
file_format=self.data["file_format"],
chart_height=self.data["chart_height"] or DEFAULT_CHART_HEIGHT,
chart_width=self.data["chart_width"] or DEFAULT_CHART_WIDTH,
Expand All @@ -97,4 +168,5 @@ def to_models(self, request: Request) -> DualCategoryChartRequestParams:
or DEFAULT_Y_AXIS_MINIMUM_VAlUE,
y_axis_maximum_value=self.data["y_axis_maximum_value"],
request=request,
legend_title=self.data.get("legend_title", ""),
)
10 changes: 0 additions & 10 deletions metrics/api/serializers/charts/single_category_charts.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,3 @@ class EncodedChartsRequestSerializer(ChartsSerializer):
help_text=help_texts.ENCODED_CHARTS_FILE_FORMAT_FIELD,
default="svg",
)

Comment thread
tushortz marked this conversation as resolved.

class EncodedChartResponseSerializer(serializers.Serializer):
last_updated = serializers.CharField(
help_text=help_texts.ENCODED_CHARTS_LAST_UPDATED,
allow_blank=True,
)
chart = serializers.CharField(help_text=help_texts.ENCODED_CHARTS_RESPONSE)
alt_text = serializers.CharField(help_text=help_texts.CHARTS_ALT_TEXT)
figure = serializers.DictField(help_text=help_texts.CHARTS_FIGURE_OUTPUT)
4 changes: 0 additions & 4 deletions metrics/api/serializers/charts/subplot_charts.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,3 @@ def to_models(self, request: Request) -> SubplotChartRequestParameters:
subplots=self.validated_data["subplots"],
request=request,
)


class ChartPreviewQueryParamsSerializer(serializers.Serializer):
preview = serializers.BooleanField(required=False)
139 changes: 139 additions & 0 deletions metrics/api/serializers/dual_category_tables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import contextlib

from django.db.utils import OperationalError
from rest_framework import serializers
from rest_framework.request import Request

from metrics.api.serializers import help_texts
from metrics.api.serializers.plots import PlotSerializer
from metrics.domain.common.utils import (
DEFAULT_CHART_HEIGHT,
DEFAULT_CHART_WIDTH,
DEFAULT_X_AXIS,
DEFAULT_Y_AXIS,
ChartAxisFields,
)
from metrics.domain.models import ChartRequestParams


class DualCategoryTableSegmentSerializer(serializers.Serializer):
colour = serializers.CharField(
required=False,
allow_blank=True,
allow_null=True,
default="",
help_text=help_texts.LABEL_FIELD,
)

secondary_field_value = serializers.CharField(
required=False,
allow_blank=True,
allow_null=True,
default="",
help_text=help_texts.LABEL_FIELD,
)

label = serializers.CharField(
required=False,
allow_blank=True,
allow_null=True,
default="",
help_text=help_texts.LABEL_FIELD,
)


class DualCategoryTableSegmentListSerializer(serializers.ListSerializer):
child = DualCategoryTableSegmentSerializer()


class DualCategoryTablesSerializer(serializers.Serializer):

segments = DualCategoryTableSegmentListSerializer()

static_fields = PlotSerializer()

x_axis = serializers.ChoiceField(
choices=ChartAxisFields.choices(),
required=False,
allow_blank=True,
allow_null=True,
help_text=help_texts.CHART_X_AXIS,
default=DEFAULT_X_AXIS,
)

y_axis = serializers.ChoiceField(
choices=ChartAxisFields.choices(),
required=False,
allow_blank=True,
allow_null=True,
help_text=help_texts.CHART_Y_AXIS,
default=DEFAULT_Y_AXIS,
)

primary_field_values = serializers.ListField(
child=serializers.CharField(),
help_text="List of primary field values for this segment",
required=True,
allow_empty=False,
)

secondary_category = serializers.CharField(
help_text="Secondary category field for the chart",
required=True,
)

def __init__(self, *args, **kwargs):
with contextlib.suppress(OperationalError):
super().__init__(*args, **kwargs)

def to_models(self, request: Request) -> ChartRequestParams:

groups_plots = []
primary_field_values = self.data.get("primary_field_values")
x_axis = self.data.get("x_axis") or DEFAULT_X_AXIS
y_axis = self.data.get("y_axis") or DEFAULT_Y_AXIS
static_fields = self.data.get("static_fields")
print(f"AIDAN static_fields {static_fields}")
topic = static_fields.get("topic")

Check warning on line 97 in metrics/api/serializers/dual_category_tables.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the unused local variable "topic".

See more on https://sonarcloud.io/project/issues?id=UKHSA-Internal_winter-pressures-api&issues=AZ62w0_qrnf4cV9QboYX&open=AZ62w0_qrnf4cV9QboYX&pullRequest=3222

for primary_field_value in primary_field_values:
for segment in self.data["segments"]:
plot = {
"y_axis": y_axis,
"x_axis": primary_field_value,
self.data.get("secondary_category"): segment[
"secondary_field_value"
],
**static_fields,
}
groups_plots.append(plot)
print(f"AIDAN: plots {groups_plots}")
return ChartRequestParams(
chart_height=DEFAULT_CHART_HEIGHT,
chart_width=DEFAULT_CHART_WIDTH,
file_format="svg",
plots=groups_plots,
request=request,
x_axis=x_axis,
y_axis=y_axis,
)


class DualCategoryTablesResponseValueSerializer(serializers.Serializer):
label = serializers.CharField()
value = serializers.CharField()
in_reporting_delay_period = serializers.BooleanField()
# Confidence intervals aren't implemented for dual category charts


class DualCategoryTablesResponseValuesListSerializer(serializers.ListSerializer):
child = DualCategoryTablesResponseValueSerializer()


class DualCategoryTablesResponsePlotsListSerializer(serializers.Serializer):
reference = serializers.CharField()
values = DualCategoryTablesResponseValuesListSerializer()


class DualCategoryTablesResponseSerializer(serializers.ListSerializer):
child = DualCategoryTablesResponsePlotsListSerializer()
2 changes: 2 additions & 0 deletions metrics/api/urls_construction.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
BulkDownloadsView,
ChartsView,
ColdAlertViewSet,
DualCategoryTablesView,
DownloadsView,
EncodedChartsView,
HeadlinesView,
Expand Down Expand Up @@ -206,6 +207,7 @@ def construct_public_api_urlpatterns(
re_path(f"^{API_PREFIX}maps/v1", MapsView.as_view()),
re_path(f"^{API_PREFIX}tables/v4", TablesView.as_view()),
re_path(f"^{API_PREFIX}tables/subplot/v1", TablesSubplotView.as_view()),
re_path(f"^{API_PREFIX}tables/dual-category/v1", DualCategoryTablesView.as_view()),
re_path(f"^{API_PREFIX}trends/v3", TrendsView.as_view()),
]

Expand Down
Loading
Loading