Skip to content

Commit

Permalink
feat: invoke UI changes to reports and create new URL path to handle …
Browse files Browse the repository at this point in the history
…reports with multiple tabs (#1134)
  • Loading branch information
nboyse authored Jan 18, 2024
1 parent 628b6d3 commit e1e87cd
Show file tree
Hide file tree
Showing 12 changed files with 110 additions and 26 deletions.
7 changes: 6 additions & 1 deletion common/static/common/scss/_button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@ button.no-background {
cursor: pointer;
overflow: hidden;
outline: none;
}
}

.report-button-inline {
float:right;
margin-top:-70px
}
6 changes: 6 additions & 0 deletions reports/jinja2/generics/table.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,33 @@
</ul>
<div class="govuk-tabs__panel" id="{{ report.tab_name | replace(' ', '_') | lower }}">
<h2 class="govuk-heading-l">{{ report.tab_name }}</h2>
<a id="exportToCSVLink{{report.tab_name | replace(' ', '_') | lower}}" class="govuk-button report-button-inline" href="{{ url("reports:export_report_to_csv", kwargs={"report_slug": report.slug() }) }}">Export to CSV</a>
{{ govukTable({
"head": report.headers(),
"rows": report.rows()
}) }}
</div>
<div class="govuk-tabs__panel govuk-tabs__panel--hidden" id="{{ report.tab_name2 | replace(' ', '_') | lower }}">
<div>
<h2 class="govuk-heading-l">{{ report.tab_name2 }}</h2>
<a id="exportToCSVLink{{report.tab_name2 | replace(' ', '_') | lower}}" class="govuk-button report-button-inline" href="{{ url("reports:export_report_with_tabs_to_csv", kwargs={"report_slug": report.slug(), "current_tab": report.tab_name2 | replace(' ', '_') | lower }) }}">Export to CSV</a>
</div>
{{ govukTable({
"head": report.headers2(),
"rows": report.rows2()
}) }}
</div>
<div class="govuk-tabs__panel govuk-tabs__panel--hidden" id="{{ report.tab_name3 | replace(' ', '_') | lower }}">
<h2 class="govuk-heading-l">{{ report.tab_name3 }}</h2>
<a id="exportToCSVLink{{report.tab_name3 | replace(' ', '_') | lower}}" class="govuk-button report-button-inline" href="{{ url("reports:export_report_with_tabs_to_csv", kwargs={"report_slug": report.slug(), "current_tab": report.tab_name3 | replace(' ', '_') | lower }) }}">Export to CSV</a>
{{ govukTable({
"head": report.headers3(),
"rows": report.rows3()
}) }}
</div>
<div class="govuk-tabs__panel govuk-tabs__panel--hidden" id="{{ report.tab_name4 | replace(' ', '_') | lower }}">
<h2 class="govuk-heading-l">{{ report.tab_name4 }}</h2>
<a id="exportToCSVLink{{report.tab_name4 | replace(' ', '_') | lower}}" class="govuk-button report-button-inline" href="{{ url("reports:export_report_with_tabs_to_csv", kwargs={"report_slug": report.slug(), "current_tab": report.tab_name4 | replace(' ', '_') | lower }) }}">Export to CSV</a>
{{ govukTable({
"head": report.headers4(),
"rows": report.rows4()
Expand Down
1 change: 0 additions & 1 deletion reports/jinja2/reports/report_chart_timescale.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@
<h1 class="govuk-heading-xl govuk-!-margin-bottom-3">
Report: {{ report.name }}
</h1>
<span class="govuk-caption-xl govuk-!-margin-bottom-9">Timescale chart</span>
<h2 class="govuk-body">
{{ report.description|safe }}
</h2>
Expand Down
7 changes: 4 additions & 3 deletions reports/jinja2/reports/report_table.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
<h1 class="govuk-heading-xl govuk-!-margin-bottom-3">
Report: {{ report.name }}
</h1>
<a class="govuk-button" href="{{ url("reports:export_report_to_csv", kwargs={"report_slug": report.slug()}) }}">Export to CSV</a>
<span class="govuk-caption-xl govuk-!-margin-bottom-9">Table</span>
{% if not report.tab_name2 %}
<a id="exportToCSVLink{{report.slug()}}" class="govuk-button" style="float:right;" href="{{ url("reports:export_report_to_csv", kwargs={"report_slug": report.slug()}) }}">Export to CSV</a>
{% endif %}
<h2 class="govuk-body">
{{ report.description|safe }}
</h2>
<div>
<div class="govuk-!-margin-top-9">
{% include "generics/table.jinja" %}
</div>
{% endblock %}
2 changes: 1 addition & 1 deletion reports/reports/cds_approved.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

class Report(ReportBaseChart):
name = "CDS approvals in the last 12 months"
description = "This report shows the count of approved (published) workbaskets in the last 12 months per day"
description = "This chart shows the count of approved (published) workbaskets in the last 12 months per day"
chart_type = "line"
report_template = "chart_timescale"
days_in_past = 365
Expand Down
2 changes: 1 addition & 1 deletion reports/reports/cds_approved_7_day_avg.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
class Report(ReportBaseChart):
name = "CDS approvals (7 day average) in the last 12 months"
description = (
"This report shows the 7 day average of approved (published) "
"This chart shows the 7 day average of approved (published) "
"workbaskets in the last 12 months per day"
)
chart_type = "line"
Expand Down
4 changes: 2 additions & 2 deletions reports/reports/cds_rejections.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
class Report(ReportBaseChart):
name = "CDS rejections in the last 12 months"
description = (
"This report shows the count of rejected (errored) workbaskets in the last 12 months per day. "
"This chart shows the count of rejected (errored) workbaskets in the last 12 months per day. "
"<br><br>"
"Note: workbaskets that are rejected, tend to be removed, so this report is for demonstration "
"Note: workbaskets that are rejected, tend to be removed, so this chart is for demonstration "
"purposes only at this point. there remains some work to do to confidently track and collect "
"rejection information in TAP suitable for reporting purposes."
)
Expand Down
31 changes: 20 additions & 11 deletions reports/reports/expiring_quotas_with_no_definition_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
class Report(ReportBaseTable):
name = "Quotas Expiring Soon"
enabled = True
description = "Quotas with definition/sub-quota/blocking/suspension periods about to expire and no future definition period"
description = "Quotas with definition, sub-quota, blocking or suspension periods about to expire and no future definition period."
tabular_reports = True
tab_name = "Definitions"
tab_name2 = "Sub-quota associations"
Expand Down Expand Up @@ -54,18 +54,27 @@ def query(self):
return quotas_without_future_definition

def find_quota_definitions_expiring_soon(self):
filter_query = Q(
valid_between__isnull=False,
valid_between__endswith__lte=self.future_time,
) & Q(valid_between__endswith__gte=self.current_time) | Q(
valid_between__endswith=None
expiring_quotas = QuotaDefinition.objects.latest_approved().filter(
Q(
valid_between__isnull=False,
valid_between__endswith__lte=self.future_time,
)
& Q(valid_between__endswith__gte=self.current_time)
| Q(valid_between__endswith=None)
)

quotas_expiring_soon = QuotaDefinition.objects.latest_approved().filter(
filter_query
)
# Filter out quota definitions with associated future definitions
filtered_quotas = []
for quota in expiring_quotas:
future_definitions = QuotaOrderNumber.objects.latest_approved().filter(
order_number=quota.order_number,
valid_between__startswith__gt=quota.valid_between.upper,
)

return list(quotas_expiring_soon)
if not future_definitions.exists():
filtered_quotas.append(quota)

return filtered_quotas

def find_quotas_without_future_definition(self, expiring_quotas):
matching_data = set()
Expand Down Expand Up @@ -186,7 +195,6 @@ def find_quota_suspension_without_future_definition(self, expiring_quotas):
for quota_definition in expiring_quotas:
future_definitions = QuotaSuspension.objects.latest_approved().filter(
quota_definition=quota_definition,
# valid_between__startswith__gt=quota_definition.valid_between.upper,
)

if future_definitions.exists():
Expand Down Expand Up @@ -240,4 +248,5 @@ def query4(self):
quota_suspension_without_future_definition = (
self.find_quota_suspension_without_future_definition(expiring_quotas)
)

return quota_suspension_without_future_definition
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ def test_find_quota_suspension_without_future_definition(self, quota_order_numbe
assert expiring_quota_definition in result
assert suspension in expiring_quota_definition.quotasuspension_set.all()

def test_rows_no_data(self):
report = Report()

result = report.rows()

assert len(result) == 1
assert result[0][0]["text"] == "There is no data for this report at present"

def test_rows2_no_data(self, quota_order_number):
report = Report()

Expand Down
13 changes: 12 additions & 1 deletion reports/tests/test_report_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from reports.utils import get_reports
from reports.views import export_report_to_csv
from reports.reports.expiring_quotas_with_no_definition_period import Report

pytestmark = pytest.mark.django_db

Expand Down Expand Up @@ -41,7 +42,7 @@ def test_all_report_unauthorised(self, client_name, http_status, request):

def test_export_report_to_csv(self, request):
request = RequestFactory().get("/")
report_slug = "cds_rejections_in_the_last_12_months"
report_slug = "blank_goods_nomenclature_descriptions"

response = export_report_to_csv(request, report_slug)

Expand All @@ -51,3 +52,13 @@ def test_export_report_to_csv(self, request):
response["Content-Disposition"]
== f'attachment; filename="{report_slug}_report.csv"'
)

def test_export_report_invalid_tab(self):
request = RequestFactory().get("/")
report_slug = Report.slug()
invalid_tab = "Invalid tab"

with pytest.raises(
ValueError, match=f"Invalid current_tab value: {invalid_tab}"
):
export_report_to_csv(request, report_slug, current_tab=invalid_tab)
7 changes: 6 additions & 1 deletion reports/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@
urlpatterns = [
path("reports/", views.index, name="index"),
path(
"reports/<str:report_slug>/export-csv/",
"reports/<str:report_slug>/",
views.export_report_to_csv,
name="export_report_to_csv",
),
path(
"reports/<str:report_slug>/<str:current_tab>/export-csv/",
views.export_report_to_csv,
name="export_report_with_tabs_to_csv",
),
]

for report in utils.get_reports():
Expand Down
48 changes: 44 additions & 4 deletions reports/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,60 @@ def report(request):
)


def export_report_to_csv(request, report_slug):
def export_report_to_csv(request, report_slug, current_tab=None):
report_class = utils.get_report_by_slug(report_slug)
report_instance = report_class()

response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = f'attachment; filename="{report_slug}_report.csv"'

if current_tab:
response[
"Content-Disposition"
] = f'attachment; filename="{report_slug + "_for_" + current_tab}_report.csv"'
formatted_current_tab = current_tab.capitalize().replace("_", " ")

# Define a dictionary to map current_tab values to methods
tab_mapping = {
report_instance.tab_name: (
report_instance.headers(),
report_instance.rows(),
),
report_instance.tab_name2: (
report_instance.headers2(),
report_instance.rows2(),
),
report_instance.tab_name3: (
report_instance.headers3(),
report_instance.rows3(),
),
report_instance.tab_name4: (
report_instance.headers4(),
report_instance.rows4(),
),
}

# Use the dictionary to get the methods based on current_tab
methods = tab_mapping.get(formatted_current_tab)

if methods:
headers, rows = methods
else:
# Raise an exception if current_tab doesn't match any expected values
raise ValueError(f"Invalid current_tab value: {formatted_current_tab}")
else:
response[
"Content-Disposition"
] = f'attachment; filename="{report_slug}_report.csv"'
headers = report_instance.headers()
rows = report_instance.rows()

writer = csv.writer(response)

# Check if the report is a table or a chart
if hasattr(report_instance, "headers"):
# For table reports
writer.writerow([header["text"] for header in report_instance.headers()])
for row in report_instance.rows():
writer.writerow([header["text"] for header in headers])
for row in rows:
writer.writerow([column["text"] for column in row])
else:
# For chart reports
Expand Down

0 comments on commit e1e87cd

Please sign in to comment.