diff --git a/app/main/forms.py b/app/main/forms.py index 5683149..85240fa 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -1,7 +1,7 @@ from flask_wtf import FlaskForm -from govuk_frontend_wtf.wtforms_widgets import GovRadioInput, GovSelect, GovSubmitInput +from govuk_frontend_wtf.wtforms_widgets import GovRadioInput, GovSubmitInput from wtforms.fields import RadioField, SelectField, SubmitField -from wtforms.validators import AnyOf, InputRequired +from wtforms.validators import InputRequired class CookiesForm(FlaskForm): @@ -24,16 +24,15 @@ class CookiesForm(FlaskForm): class DownloadForm(FlaskForm): file_format = SelectField( - "File type", - widget=GovSelect(), - validators=[AnyOf(["json", "xlsx"])], + "Which format do you need?", + widget=GovRadioInput(), choices=[ - ("xlsx", "XSLX (Excel)"), + ("xlsx", "XLSX (Microsoft Excel)"), ("json", "JSON"), ], default=None, ) - download = SubmitField("Download", widget=GovSubmitInput()) + download = SubmitField("Confirm and request data", widget=GovSubmitInput()) class RetrieveForm(FlaskForm): diff --git a/app/main/routes.py b/app/main/routes.py index d46ca73..4636d67 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -69,65 +69,76 @@ def download(): ) if request.method == "POST": - file_format = form.file_format.data - if file_format not in ["json", "xlsx"]: - current_app.logger.error( - "Unexpected file format requested from /download: {file_format}", - extra=dict(file_format=file_format), + if form.validate_on_submit(): + file_format = form.file_format.data + orgs = request.form.getlist(FormNames.ORGS) + regions = request.form.getlist(FormNames.REGIONS) + funds = request.form.getlist(FormNames.FUNDS) + outcome_categories = request.form.getlist(FormNames.OUTCOMES) + from_quarter = request.form.get("from-quarter") + from_year = request.form.get("from-year") + to_quarter = request.form.get("to-quarter") + to_year = request.form.get("to-year") + + reporting_period_start = ( + financial_quarter_from_mapping(quarter=from_quarter, year=from_year) if to_quarter and to_year else None ) - return abort(500), f"Unknown file format: {file_format}" - - orgs = request.form.getlist(FormNames.ORGS) - regions = request.form.getlist(FormNames.REGIONS) - funds = request.form.getlist(FormNames.FUNDS) - outcome_categories = request.form.getlist(FormNames.OUTCOMES) - from_quarter = request.form.get("from-quarter") - from_year = request.form.get("from-year") - to_quarter = request.form.get("to-quarter") - to_year = request.form.get("to-year") - - reporting_period_start = ( - financial_quarter_from_mapping(quarter=from_quarter, year=from_year) if to_quarter and to_year else None - ) - reporting_period_end = ( - financial_quarter_to_mapping(quarter=to_quarter, year=to_year) if to_quarter and to_year else None - ) + reporting_period_end = ( + financial_quarter_to_mapping(quarter=to_quarter, year=to_year) if to_quarter and to_year else None + ) + + query_params = {"email_address": g.user.email, "file_format": file_format} + if orgs: + query_params["organisations"] = orgs + if regions: + query_params["regions"] = regions + if funds: + query_params["funds"] = funds + if outcome_categories: + query_params["outcome_categories"] = outcome_categories + if reporting_period_start: + query_params["rp_start"] = reporting_period_start + if reporting_period_end: + query_params["rp_end"] = reporting_period_end + + status_code = process_async_download(query_params) + + current_app.logger.info( + "Request for download by {user_id} with {query_params}", + extra={ + "user_id": g.account_id, + "email": g.user.email, + "query_params": query_params, + "request_type": "download", + }, + ) - query_params = {"email_address": g.user.email, "file_format": file_format} - if orgs: - query_params["organisations"] = orgs - if regions: - query_params["regions"] = regions - if funds: - query_params["funds"] = funds - if outcome_categories: - query_params["outcome_categories"] = outcome_categories - if reporting_period_start: - query_params["rp_start"] = reporting_period_start - if reporting_period_end: - query_params["rp_end"] = reporting_period_end - - status_code = process_async_download(query_params) - - current_app.logger.info( - "Request for download by {user_id} with {query_params}", - extra={ - "user_id": g.account_id, - "email": g.user.email, - "query_params": query_params, - "request_type": "download", - }, + if status_code == 204: + return redirect(url_for("main.request_received")) + else: + current_app.logger.error( + "Response status code from data-store/trigger_async_download: {content_type}", + extra=dict(content_type=status_code), + ) + return render_template("500.html") + + current_app.logger.error( + "Unexpected file format requested from /download: {file_format}", + extra=dict(file_format=form.file_format), ) + if form.file_format.errors: + form.file_format.errors = ["Select a file format"] - if status_code == 204: - return redirect(url_for("main.request_received")) - else: - current_app.logger.error( - "Response status code from data-store/trigger_async_download: {content_type}", - extra=dict(content_type=status_code), - ) - return render_template("500.html") + return render_template( + "download.html", + form=form, + funds=get_fund_checkboxes(), + regions=get_region_checkboxes(), + orgs=get_org_checkboxes(), + outcomes=get_outcome_checkboxes(), + returnsParams=get_returns(), + ) @bp.route("/request-received", methods=["GET"]) diff --git a/app/templates/main/download.html b/app/templates/main/download.html index d8f4933..9d4613e 100644 --- a/app/templates/main/download.html +++ b/app/templates/main/download.html @@ -5,6 +5,7 @@ {%- from 'govuk_frontend_jinja/components/select/macro.html' import govukSelect -%} {%- from 'govuk_frontend_jinja/components/button/macro.html' import govukButton -%} {%- from 'govuk_frontend_jinja/components/label/macro.html' import govukLabel -%} +{%- from 'govuk_frontend_jinja/components/error-summary/macro.html' import govukErrorSummary -%} {%- from "outcomeCheckboxes.html" import outcomeCheckboxItems -%} {%- from "checkboxes.html" import checkboxItems -%} {%- from "select.html" import selectItems -%} @@ -14,74 +15,102 @@ {% endblock beforeContent %} {% block content %} -

Download monitoring and evaluation data

+
+
+ {% if form.errors %} + {{ govukErrorSummary ({ + "titleText": "There is a problem", + "errorList": [ + { + "text": "Select a file format", + "href": "#"+form.file_format.id + }, -

Use the following filters to select the projects you need.

+ ] + }) + }} -

The filters you select will be applied at project level. You will be able to download this as a file to your desktop.

+ {% endif %} +

Get monitoring and evaluation data

+

Use the filters to select the data you need.

+

You will get all of the data from all projects and programmes if you do not select any filters.

+
-

- If you don't select any filters you will get all the data from all of the projects that are in the system. -

+ {{ form.csrf_token }} - - {{ form.csrf_token }} + {{ govukAccordion({ + "id": "accordion-download", + "headingLevel": 2, + "showAllSectionsText": "", + "items": [ + { + "heading": { + "text": "Filter by fund" + }, + "content": { + "html": checkboxItems(funds["name"], funds["items"]) + }, + }, + { + "heading": { + "text": "Filter by region" + }, + "content": { + "html": checkboxItems(regions["name"], regions["items"]) + } + }, + { + "heading": { + "text": "Filter by funded organisation" + }, + "content": { + "html": checkboxItems(orgs["name"], orgs["items"]) + } + }, + { + "heading": { + "text": "Filter by outcomes" + }, + "content": { + "html": checkboxItems(outcomes["name"], outcomes["items"]) + } + }, + { + "heading": { + "text": "Filter by returns period" + }, + "content": { + "html": selectItems(returnsParams) + } + } + ] + }) + }} +
+

We will:

+
    +
  • send you an email to the address you’ve signed in with
  • +
  • include a link in the email for you to download your data
  • +
+
+ {{ + form.file_format( + params={ + "fieldset": { + "legend": { + "text": form.file_format.label.text, + "classes": "govuk-!-font-weight-bold" + }, + } + } + ) + }} + {{ form.download }} +
+

+ Access the funding glossary +

+
+
- {{ govukAccordion({ - "id": "accordion-download", - "headingLevel": 2, - "showAllSectionsText": "", - "items": [ - { - "heading": { - "text": "Filter by fund" - }, - "content": { - "html": checkboxItems(funds["name"], funds["items"]) - }, - }, - { - "heading": { - "text": "Filter by region" - }, - "content": { - "html": checkboxItems(regions["name"], regions["items"]) - } - }, - { - "heading": { - "text": "Filter by funded organisation" - }, - "content": { - "html": checkboxItems(orgs["name"], orgs["items"]) - } - }, - { - "heading": { - "text": "Filter by outcomes" - }, - "content": { - "html": checkboxItems(outcomes["name"], outcomes["items"]) - } - }, - { - "heading": { - "text": "Filter by returns period" - }, - "content": { - "html": selectItems(returnsParams) - } - } - ] - }) - }} - - {{ form.file_format }} - {{ form.download }} - - - -

- Access the funding glossary -

{% endblock content %} diff --git a/tests/test_routes.py b/tests/test_routes.py index 3b98736..5630a05 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -76,23 +76,41 @@ def test_async_download_redirect_error(flask_test_client, mocked_failing_downloa assert b"Try again later." in response.data -def test_download_post_unknown_format(flask_test_client): +def test_download_post_unknown_format(flask_test_client, requests_mock): + requests_mock.get("http://data-store/organisations", json=[]) + requests_mock.get("http://data-store/regions", json=[]) + requests_mock.get("http://data-store/funds", json=[]) + requests_mock.get("http://data-store/outcome-categories", json=[]) + requests_mock.get( + "http://data-store/reporting-period-range", + json={"end_date": "2023-02-01T00:00:00Z", "start_date": "2023-02-12T00:00:00Z"}, + ) response = flask_test_client.post("/download", data={"file_format": "foobar"}) - assert response.status_code == 500 - + assert response.status_code == 200 -def test_download_post_no_format(flask_test_client): - response = flask_test_client.post("/download") - assert response.status_code == 500 + soup = BeautifulSoup(response.text, "html.parser") + error_summary = soup.find("p", {"class": "govuk-error-message"}) + assert error_summary is not None + assert "Select a file format" in error_summary.text -@patch("app.main.download_data.get_response") -def test_download_post_unknown_format_from_api(mock_get_response, flask_test_client): - mock_response = mock_get_response.return_value - mock_response.headers = {"content-type": "InvalidType"} +def test_download_post_no_format(flask_test_client, requests_mock): + requests_mock.get("http://data-store/organisations", json=[]) + requests_mock.get("http://data-store/regions", json=[]) + requests_mock.get("http://data-store/funds", json=[]) + requests_mock.get("http://data-store/outcome-categories", json=[]) + requests_mock.get( + "http://data-store/reporting-period-range", + json={"end_date": "2023-02-01T00:00:00Z", "start_date": "2023-02-12T00:00:00Z"}, + ) + response = flask_test_client.post("/download", data={"file_format": " "}) + assert response.headers["Content-Type"] == "text/html; charset=utf-8" + assert response.status_code == 200 - response = flask_test_client.post("/download?file_format=anything") - assert response.status_code == 500 + soup = BeautifulSoup(response.text, "html.parser") + error_summary = soup.find("p", {"class": "govuk-error-message"}) + assert error_summary is not None + assert "Select a file format" in error_summary.text def test_known_http_error_redirect(flask_test_client):