Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/pip/pydantic-2.9.0
Browse files Browse the repository at this point in the history
  • Loading branch information
A-Ashiq committed Sep 6, 2024
2 parents 191798e + 0a52814 commit ad20950
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 10 deletions.
1 change: 1 addition & 0 deletions metrics/api/serializers/charts.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,4 @@ class EncodedChartResponseSerializer(serializers.Serializer):
)
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)
3 changes: 3 additions & 0 deletions metrics/api/serializers/help_texts.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@
CHARTS_ALT_TEXT: str = """
The description text which summarizes the chart and the data that it represents.
"""
CHARTS_FIGURE_OUTPUT: str = """
The `plotly` figure object output with overlaid settings specific for interactive charts.
"""
CHART_USE_MARKERS: str = """
Boolean switch to decide whether to draw markers on individual data points.
"""
Expand Down
22 changes: 20 additions & 2 deletions metrics/domain/charts/chart_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,21 @@ def is_date_type_x_axis(self) -> bool:

def get_x_axis_config(self) -> dict[str, str | bool | DICT_OF_STR_ONLY]:
base_x_axis_config = {
"showgrid": False,
"zeroline": False,
"showspikes": True,
"spikecolor": "#b1b4b6",
"spikedash": "2px",
"spikethickness": 1,
"spikemode": "toaxis+across+marker",
"spikesnap": "cursor",
"showgrid": True,
"showline": False,
"zeroline": False,
"fixedrange": True,
"gridcolor": "rgba(0,0,0,0.05)",
"ticks": "outside",
"tickson": "boundaries",
"type": "date",
"tickcolor": "rgba(0,0,0,0)",
"dtick": "M1",
"tickformat": "%b %Y",
"tickfont": self.get_tick_font_config(),
Expand All @@ -56,15 +65,24 @@ def get_x_axis_config(self) -> dict[str, str | bool | DICT_OF_STR_ONLY]:

def get_y_axis_config(self) -> dict[str, bool | DICT_OF_STR_ONLY]:
return {
"ticks": "outside",
"tickson": "boundaries",
"tickcolor": "rgba(0,0,0,0)",
"showgrid": False,
"showticklabels": True,
"fixedrange": True,
"gridcolor": "#000",
"tickfont": self.get_tick_font_config(),
}

def get_base_chart_config(self):
return {
"paper_bgcolor": colour_scheme.RGBAColours.WHITE.stringified,
"plot_bgcolor": colour_scheme.RGBAColours.WHITE.stringified,
"hoverlabel": {
"align": "left",
"bgcolor": "white",
},
"margin": {
"l": 0,
"r": 0,
Expand Down
14 changes: 13 additions & 1 deletion metrics/interfaces/charts/access.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ class ChartOutput:
figure: plotly.graph_objects.Figure
description: str

@property
def interactive_chart_figure_output(self) -> dict:
self._add_settings_for_interactive_charts()
return self.figure.to_dict()

def _add_settings_for_interactive_charts(self):
font_css_variable = "var(--font-primary), arial, sans-serif"
self.figure.layout.xaxis.tickfont.update(family=font_css_variable)
self.figure.layout.yaxis.tickfont.update(family=font_css_variable)
self.figure.layout.xaxis.showline = True


class ChartsInterface:
def __init__(
Expand Down Expand Up @@ -340,7 +351,7 @@ def calculate_change_in_metric_value(*, values, metric_name) -> int | float:

return calculations.change_between_each_half(values=values)

def get_encoded_chart(self, *, chart_output: ChartOutput) -> dict[str, str]:
def get_encoded_chart(self, *, chart_output: ChartOutput) -> dict[str, str | dict]:
"""Creates a dict containing a timestamp for the last data point + encoded string for the chart figure.
Args:
Expand All @@ -360,6 +371,7 @@ def get_encoded_chart(self, *, chart_output: ChartOutput) -> dict[str, str]:
"last_updated": self._latest_date,
"chart": self.encode_figure(figure=chart_output.figure),
"alt_text": chart_output.description,
"figure": chart_output.interactive_chart_figure_output,
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def test_main_bar_plot(self, mocked_plot_data: mock.Mock):
# ---X Axis checks---
x_axis = figure.layout.xaxis

assert not x_axis.showgrid
assert x_axis.showgrid
assert not x_axis.zeroline
assert not x_axis.showline

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def test_weekly_hospital_admissions_rate_main_plot(self, fake_plot_data: PlotDat
# ___Sep___Oct___Nov___ as opposed to _Sep_Sep_Oct_Oct_Oct_Nov_Nov_
assert x_axis.dtick == "M1"

assert not x_axis.showgrid
assert x_axis.showgrid

# Tick marks should be on the boundary drawn going outwards of the main frame
assert x_axis.ticks == "outside"
Expand Down
1 change: 1 addition & 0 deletions tests/unit/metrics/api/serializers/test_charts.py
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,7 @@ def valid_payload(self) -> dict[str, str]:
"last_updated": "has_value",
"chart": "has_value",
"alt_text": "has_value",
"figure": {},
}

def test_has_payload(self):
Expand Down
35 changes: 31 additions & 4 deletions tests/unit/metrics/domain/charts/test_chart_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,18 @@ def test_get_x_axes_setting_for_date_based_x_axis(

# Then
expected_x_axis_config = {
"showgrid": False,
"zeroline": False,
"showspikes": True,
"spikecolor": "#b1b4b6",
"spikedash": "2px",
"spikethickness": 1,
"spikemode": "toaxis+across+marker",
"spikesnap": "cursor",
"showline": False,
"fixedrange": True,
"gridcolor": "rgba(0,0,0,0.05)",
"tickcolor": "rgba(0,0,0,0)",
"showgrid": True,
"zeroline": False,
"ticks": "outside",
"tickson": "boundaries",
"type": "date",
Expand Down Expand Up @@ -96,9 +105,18 @@ def test_get_x_axes_setting_for_text_based_x_axis(

# Then
expected_x_axis_config = {
"showgrid": False,
"zeroline": False,
"showspikes": True,
"spikecolor": "#b1b4b6",
"spikedash": "2px",
"spikethickness": 1,
"spikemode": "toaxis+across+marker",
"spikesnap": "cursor",
"showline": False,
"showgrid": True,
"zeroline": False,
"fixedrange": True,
"gridcolor": "rgba(0,0,0,0.05)",
"tickcolor": "rgba(0,0,0,0)",
"ticks": "outside",
"tickson": "boundaries",
"type": "-",
Expand All @@ -124,6 +142,11 @@ def test_get_y_axes_setting(self, fake_chart_settings: ChartSettings):
expected_y_axis_config = {
"showgrid": False,
"showticklabels": True,
"fixedrange": True,
"gridcolor": "#000",
"ticks": "outside",
"tickson": "boundaries",
"tickcolor": "rgba(0,0,0,0)",
"tickfont": chart_settings.get_tick_font_config(),
}
assert y_axis_config == expected_y_axis_config
Expand Down Expand Up @@ -157,6 +180,10 @@ def test_get_base_chart_config(
"b": 0,
"t": 0,
},
"hoverlabel": {
"align": "left",
"bgcolor": "white",
},
"autosize": False,
"xaxis": mocked_get_x_axis_config.return_value,
"yaxis": mocked_get_y_axis_config.return_value,
Expand Down
74 changes: 73 additions & 1 deletion tests/unit/metrics/interfaces/charts/test_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import plotly.graph_objects
import pytest
from plotly.graph_objs import Figure

from metrics.data.managers.core_models.headline import CoreHeadlineManager
from metrics.data.managers.core_models.time_series import CoreTimeSeriesManager
Expand Down Expand Up @@ -553,7 +554,9 @@ def test_get_encoded_chart_returns_description(
"""
Given a mocked figure
When `get_encoded_chart()` is called from an instance of the `ChartsInterface`
Then the `chart` field is populated via a call to the `encode_figure()` method
Then the `alt_text` field is populated
via a call to the `description()` property
on the `ChartOutput` object
Patches:
`mocked_create_optimized_svg`: To remove the side effect of
Expand All @@ -576,6 +579,42 @@ def test_get_encoded_chart_returns_description(
# Then
assert encoded_chart["alt_text"] == chart_output.description

@mock.patch.object(ChartsInterface, "encode_figure")
def test_get_encoded_chart_returns_figure_output_for_interactive_charts(
self,
mocked_encode_figure: mock.MagicMock,
fake_chart_plot_parameters: PlotParameters,
):
"""
Given a mocked `ChartOutput` object
When `get_encoded_chart()` is called
from an instance of the `ChartsInterface`
Then the `figure` field is populated
via a call to the `interactive_chart_figure_output()` property
on the `ChartOutput` object
Patches:
`mocked_create_optimized_svg`: To remove the side effect of
creating a svg and optimizing it
"""
# Given
mocked_chart_output = mock.Mock()
mocked_plots_collection = mock.MagicMock()
mocked_plots_collection.plots = [fake_chart_plot_parameters]
charts_interface = ChartsInterface(chart_plots=mocked_plots_collection)

# When
encoded_chart: dict[str, str | dict] = charts_interface.get_encoded_chart(
chart_output=mocked_chart_output
)

# Then
assert (
encoded_chart["figure"]
== mocked_chart_output.interactive_chart_figure_output
)


class TestGenerateChartAsFile:
def test_raises_error_for_invalid_topic_and_metric_selection(
Expand Down Expand Up @@ -739,3 +778,36 @@ def test_invalid_format_passed_to_encode_figure(
# When / Then
with pytest.raises(InvalidFileFormatError):
charts_interface.encode_figure(figure=figure)


class TestChartOutput:
def test_interactive_chart_figure_output_sets_correct_settings(self):
"""
Given a `plotly` Figure object
When the `interactive_chart_figure_output()` is called
from an instance of `ChartOutput`
Then the returned dict output representation
contains the correct settings for interactive charts
"""
# Given
fake_figure = Figure()
chart_output = ChartOutput(
figure=fake_figure,
description="abc",
)

# When
interactive_chart_output = chart_output.interactive_chart_figure_output

# Then
expected_font_family = "var(--font-primary), arial, sans-serif"
assert (
interactive_chart_output["layout"]["xaxis"]["tickfont"]["family"]
== expected_font_family
)
assert interactive_chart_output["layout"]["xaxis"]["showline"] is True

assert (
interactive_chart_output["layout"]["yaxis"]["tickfont"]["family"]
== expected_font_family
)

0 comments on commit ad20950

Please sign in to comment.