diff --git a/.github/workflows/deploy-to-dev.yml b/.github/workflows/deploy-to-dev.yml index c5ff467..75f5258 100644 --- a/.github/workflows/deploy-to-dev.yml +++ b/.github/workflows/deploy-to-dev.yml @@ -19,13 +19,6 @@ env: ECR_REGION: ${{ vars.DEVELOPMENT_ECR_REGION }} ECR_ROLE_TO_ASSUME: ${{ secrets.DEVELOPMENT_ECR_ROLE_TO_ASSUME }} - # AUTH0_CLIENT_ID: ${{ secrets.AUTH0_CLIENT_ID }} - # AUTH0_CLIENT_SECRET: ${{ secrets.AUTH0_CLIENT_SECRET }} - - # FLASK_APP_SECRET: ${{ secrets.DEV_FLASK_APP_SECRET }} - # OPS_ENG_REPORTS_ENCRYPT_KEY: ${{ secrets.DEV_OPS_ENG_REPORTS_ENCRYPT_KEY }} - # OPERATIONS_ENGINEERING_REPORTS_API_KEY: ${{ secrets.DEV_OPERATIONS_ENGINEERING_REPORTS_API_KEY }} - jobs: build-push: runs-on: ubuntu-latest @@ -83,9 +76,3 @@ jobs: --set image.repository=${ECR_REGISTRY}/${ECR_REPOSITORY} \ --set ingress.host=operations-engineering-example-${ENV}.cloud-platform.service.justice.gov.uk \ --set ingress.identifier=example-ingress-operations-engineering-example-${ENV}-green - - # --set application.auth0ClientId=${AUTH0_CLIENT_ID} \ - # --set application.auth0ClientSecret=${AUTH0_CLIENT_SECRET} \ - # --set application.appSecretKey=${FLASK_APP_SECRET} \ - # --set application.encryptionKey=${OPS_ENG_REPORTS_ENCRYPT_KEY} \ - # --set application.apiKey=${OPERATIONS_ENGINEERING_REPORTS_API_KEY} \ diff --git a/.github/workflows/deploy-to-prod.yml b/.github/workflows/deploy-to-prod.yml index 7f681c6..1fbff86 100644 --- a/.github/workflows/deploy-to-prod.yml +++ b/.github/workflows/deploy-to-prod.yml @@ -20,13 +20,6 @@ env: ECR_REGION: ${{ vars.PRODUCTION_ECR_REGION }} ECR_ROLE_TO_ASSUME: ${{ secrets.PRODUCTION_ECR_ROLE_TO_ASSUME }} -# AUTH0_CLIENT_ID: ${{ secrets.AUTH0_CLIENT_ID }} -# AUTH0_CLIENT_SECRET: ${{ secrets.AUTH0_CLIENT_SECRET }} - -# FLASK_APP_SECRET: ${{ secrets.PROD_FLASK_APP_SECRET }} -# OPS_ENG_REPORTS_ENCRYPT_KEY: ${{ secrets.PROD_OPS_ENG_REPORTS_ENCRYPT_KEY }} -# OPERATIONS_ENGINEERING_REPORTS_API_KEY: ${{ secrets.PROD_OPERATIONS_ENGINEERING_REPORTS_API_KEY }} - jobs: build-push: runs-on: ubuntu-latest @@ -89,20 +82,3 @@ jobs: --set image.repository=${ECR_REGISTRY}/${ECR_REPOSITORY} \ --set ingress.host=operations-engineering-example-${ENV}.cloud-platform.service.justice.gov.uk \ --set ingress.identifier=example-ingress-operations-engineering-example-${ENV}-green - - - # --set application.auth0ClientId=${AUTH0_CLIENT_ID} \ - # --set application.auth0ClientSecret=${AUTH0_CLIENT_SECRET} \ - # --set application.appSecretKey=${FLASK_APP_SECRET} \ - # --set application.encryptionKey=${OPS_ENG_REPORTS_ENCRYPT_KEY} \ - # --set application.apiKey=${OPERATIONS_ENGINEERING_REPORTS_API_KEY} \ - - # - name: Report failure to Slack - # if: always() - # uses: ravsamhq/notify-slack-action@v2 - # with: - # status: ${{ job.status }} - # notify_when: "failure" - # notification_title: "Failed to deploy the example application to production" - # env: - # SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.gitignore b/.gitignore index fa12a23..3d7f2ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,56 @@ -.env -.venv -.terraform/ -coverage/ -venv/ -env/ .DS_STORE .vscode *.code-workspace *.sha256 + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +### Terraform ### +.terraform/ terraform.tfstate -__pycache__/ \ No newline at end of file + +### GOV.UK Frontend ### +application/static/* +govuk_components* + +### Flask ### +instance/* +!instance/.gitignore +.webassets-cache + +### Flask.Python Stack ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Distribution / packaging +.Python +build +lib/ +lib64/ +var/ +wheels/ + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +coverage/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ diff --git a/Dockerfile b/Dockerfile index 5b8b394..871846d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM python:3.12.0-alpine3.17 # Set working directory in the container -WORKDIR /app/operations-engineering-example +WORKDIR /app # Set to run as non-root user RUN addgroup -S appgroup && adduser -S appuser -G appgroup -u 1051 @@ -16,10 +16,14 @@ RUN \ # Copy dirs/files from the repo to the container working directory COPY requirements.txt requirements.txt -COPY ops_eng_app ops_eng_app +COPY application application +COPY build.py build.py +COPY config.py config.py +# Install deps and run build RUN pip3 install --upgrade pip && \ pip3 install --no-cache-dir --upgrade -r requirements.txt +RUN python build.py # Send logs direct to terminal ENV PYTHONUNBUFFERED 1 @@ -31,6 +35,6 @@ USER 1051 EXPOSE 1551 # Use in production, bind to another port so not to run as root -ENTRYPOINT gunicorn ops_eng_app:app \ +ENTRYPOINT gunicorn application:app \ --bind 0.0.0.0:1551 \ --timeout 120 diff --git a/application/__init__.py b/application/__init__.py new file mode 100644 index 0000000..6bfdaa7 --- /dev/null +++ b/application/__init__.py @@ -0,0 +1,28 @@ +from flask import Flask, render_template, url_for +import logging +import os + +from flask import Flask +from flask_cors import CORS +from jinja2 import ChoiceLoader, PackageLoader, PrefixLoader + +from config import Config + + +app = Flask(__name__) +app.config.from_object(Config) + +app.jinja_loader = ChoiceLoader( + [ + PackageLoader("application"), + PrefixLoader({"govuk_frontend_jinja": PackageLoader("govuk_frontend_jinja")}), + ] +) + +app.jinja_env.trim_blocks = True +app.jinja_env.lstrip_blocks = True + + +@app.route("/", methods=["GET", "POST"]) +def index(): + return render_template("index.html") diff --git a/application/templates/404.html b/application/templates/404.html new file mode 100644 index 0000000..187f9a4 --- /dev/null +++ b/application/templates/404.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} + +{% block pageTitle %}Page not found – {{config['SERVICE_NAME']}} – GOV.UK{% endblock %} + +{% set mainClasses = "govuk-main-wrapper--l" %} + +{% block content %} +
+
+

Page not found

+

If you typed the web address, check it is correct.

+

If you pasted the web address, check you copied the entire address.

+
+
+{% endblock %} \ No newline at end of file diff --git a/application/templates/429.html b/application/templates/429.html new file mode 100644 index 0000000..fcaef96 --- /dev/null +++ b/application/templates/429.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} + +{% block pageTitle %}Too many requests – {{config['SERVICE_NAME']}} – GOV.UK{% endblock %} + +{% set mainClasses = "govuk-main-wrapper--l" %} + +{% block content %} +
+
+

Sorry, there is a problem

+

There have been too many attempts to access this page.

+

Try again later.

+
+
+{% endblock %} \ No newline at end of file diff --git a/application/templates/500.html b/application/templates/500.html new file mode 100644 index 0000000..a0781d7 --- /dev/null +++ b/application/templates/500.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} + +{% block pageTitle %}Sorry, there is a problem with the service – {{config['SERVICE_NAME']}} – GOV.UK{% endblock %} + +{% set mainClasses = "govuk-main-wrapper--l" %} + +{% block content %} +
+
+

Sorry, there is a problem with the service

+

Try again later.

+

We saved your answers. They will be available for 30 days.

+
+
+{% endblock %} \ No newline at end of file diff --git a/application/templates/503.html b/application/templates/503.html new file mode 100644 index 0000000..a08854a --- /dev/null +++ b/application/templates/503.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} + +{% block pageTitle %}Sorry, the service is unavailable – {{config['SERVICE_NAME']}} – GOV.UK{% endblock %} + +{% set mainClasses = "govuk-main-wrapper--l" %} + +{% block content %} +
+
+

Sorry, the service is unavailable

+

You will be able to use the service from 9am on Monday 19 November 2018.

+
+
+{% endblock %} \ No newline at end of file diff --git a/application/templates/accessibility.html b/application/templates/accessibility.html new file mode 100644 index 0000000..71f40da --- /dev/null +++ b/application/templates/accessibility.html @@ -0,0 +1,260 @@ +{% extends "base.html" %} + +{%- from 'govuk_frontend_jinja/components/back-link/macro.html' import govukBackLink -%} +{%- from 'govuk_frontend_jinja/components/inset-text/macro.html' import govukInsetText -%} + +{% block pageTitle %}Accessibility statement – {{config['SERVICE_NAME']}} – GOV.UK{% endblock %} + +{% block beforeContent %} + {{ super() }} + {{ govukBackLink({ + 'text': "Back", + 'href': url_for('main.index') + }) }} +{% endblock %} + +{% block content %} +
+
+ {{ super() }} +

Accessibility statement for {{config['SERVICE_NAME']}}

+ + {{ govukInsetText({ + 'text': "Note: start with a brief explanation of which websites or mobile apps the statement covers. + + You can have a single accessibility statement that covers multiple domains, or a separate statement for each domain or subdomain. As long as the user can access relevant accessibility information easily from any page on your website." + }) }} + +

This accessibility statement applies to {{config['SERVICE_URL']}}.

+ + {{ govukInsetText({ + 'text': "Note: use this section to make a brief, general statement about what the website allows disabled users to do. Base it on the evaluation covered in detail in the ‘Technical information about this website’s accessibility’ section. If you’re not confident that something is accurate, leave it out. If you’re not confident enough to say anything specific here, leave this section out completely." + }) }} + +

This website is run by {{config['DEPARTMENT_NAME']}}. We want as many people as possible to be able + to use this website. For example, that means you should be able to:

+ +

We’ve also made the website text as simple as possible to understand.

+

AbilityNet has advice on making your device easier to use if you have a disability.

+ +

How accessible this website is

+ + {{ govukInsetText({ + 'text': "Note: use this section to provide information that a disabled user can act on - for example, avoid a particular section of the website, or request an alternative version rather than waste time trying to make it work with their assistive technology. Try to list in order of most impact to least impact." + }) }} + +

We know some parts of this website are not fully accessible:

+ + +

Feedback and contact information

+ +

If you need information on this website in a different format like accessible PDF, large + print, easy read, audio recording or braille:

+ +

We’ll consider your request and get back to you in [number] days.

+

If you cannot view the map on our ‘contact us’ page, call or email us [add link to contact + details page] for directions.

+ +

Reporting accessibility problems with this website

+ +

We’re always looking to improve the accessibility of this website. If you find any problems + not listed on this page or think we’re not meeting accessibility requirements, contact: [provide both details of + how to report these issues to your organisation, and contact details for the unit or person responsible for + dealing with these reports].

+ +

Enforcement procedure

+ +

The Equality and Human Rights Commission (EHRC) is responsible for enforcing the Public Sector + Bodies (Websites and Mobile Applications) (No. 2) Accessibility Regulations 2018 (the ‘accessibility + regulations’). If you’re not happy with how we respond to your complaint, contact the Equality Advisory and + Support Service (EASS).

+ + {{ govukInsetText({ + 'text': "Note: if your organisation is based in Northern Ireland, refer users who want to complain to the Equalities Commission for Northern Ireland (ECNI) instead of the EASS and EHRC." + }) }} + +

Contacting us by phone or visiting us in person

+ +

We provide a text relay service for people who are D/deaf, hearing impaired or have a speech + impediment.

+

Our offices have audio induction loops, or if you contact us before your visit we can arrange + a British Sign Language (BSL) interpreter.

+

Find out how to contact us [add link to contact details page].

+ +

Technical information about this website’s accessibility

+ + {{ govukInsetText({ + 'text': "Note: this form of wording is legally required, so do not change it." + }) }} + +

{{config['DEPARTMENT_NAME']}} is committed to making its website accessible, in accordance with the + Public Sector Bodies (Websites and Mobile Applications) (No. 2) Accessibility Regulations 2018.

+ +

Compliance status

+ + {{ govukInsetText({ + 'html': "Note: say that the website is fully compliant if the website meets WCAG 2.1 AA standard in full. Say that it’s partially compliant if it meets most requirements of the WCAG 2.1 AA standard. If it does not meet most requirements of the WCAG 2.1 AA standard, say that it’s not compliant.

+ + If your website is either partially compliant or not compliant WCAG 2.1 AA standard, you’ll need to explain why. This will be due to one or both of the following: +
+ There’s a legally required way of expressing the compliance status of your website, so do not change it. The 3 options are as follows:" + }) }} + +

This website is fully compliant with the Web Content Accessibility Guidelines version 2.1 AA + standard.

+

This website is partially compliant with the Web Content Accessibility Guidelines version 2.1 + AA standard, due to [insert one of the following: ‘the non-compliances’, ‘the exemptions’ or ‘the non-compliances + and exemptions’] listed below.

+

This website is not compliant with the Web Content Accessibility Guidelines version 2.1 AA + standard. The [insert one of the following: ‘non-compliances’, ‘exemptions’ or ‘non-compliances and exemptions’] + are listed below.

+ + {{ govukInsetText({ + 'text': "Note: delete the options that do not apply." + }) }} + +

Non-accessible content

+ + {{ govukInsetText({ + 'html': "Note: if the website is fully compliant with the WCAG 2.1 AA standard, you can leave the ‘Non-accessible content’ section out.

+ + Otherwise, do not change the ‘Non-accessible content’ heading or the ‘The content listed below is non-accessible for the following reasons’ sentence - they’re legally required.

+ + Do not change the ‘Non-compliance with the accessibility regulations’, ‘Disproportionate burden’ and ‘Content that’s not within the scope of the accessibility regulations’ subheadings: they’re also legally required.

+ + But if you need to list a lot of problems, you can break these subsections up with further subheadings - for example, ‘Navigation and accessing information’ or ‘Interactive tools and transactions’." + }) }} + +

The content listed below is non-accessible for the following reasons.

+ +

Non-compliance with the accessibility regulations

+ + {{ govukInsetText({ + 'html': "Note: In this subsection, list:
+
+ Do not include any problems where you’re claiming disproportionate burden, or where the problem is outside the scope of the accessibility regulations (those should go in the subsections below)." + }) }} + +

Some images do not have a text alternative, so people using a screen reader cannot access the + information. This fails WCAG 2.1 success criterion 1.1.1 (non-text content).

+ +

We plan to add text alternatives for all images by September 2020. When we publish new content + we’ll make sure our use of images meets accessibility standards.

+ +

Disproportionate burden

+ + {{ govukInsetText({ + 'html': "Note: in this subsection list accessibility problems you’re claiming would be a disproportionate burden to fix

+ + Bear in mind that something which is a disproportionate burden now will not necessarily be a disproportionate burden forever. If the circumstances change, your ability to claim disproportionate burden may change too." + }) }} + +

Navigation and accessing information

+ +

There’s no way to skip the repeated content in the page header (for example, a ‘skip to main + content’ option).

+ +

It’s not always possible to change the device orientation from horizontal to vertical without + making it more difficult to view the content.

+ +

It’s not possible for users to change text size without some of the content overlapping.

+ +

Interactive tools and transactions

+ +

Some of our interactive forms are difficult to navigate using a keyboard. For example, because + some form controls are missing a ‘label’ tag.

+ +

Our forms are built and hosted through third party software and ‘skinned’ to look like our + website.

+ +

We’ve assessed the cost of fixing the issues with navigation and accessing information, and + with interactive tools and transactions. We believe that doing so now would be a disproportionate burden within + the meaning of the accessibility regulations. We will make another assessment when the supplier contract is up for + renewal, likely to be in [rough timing].

+ +

Content that’s not within the scope of the accessibility regulations

+ + {{ govukInsetText({ + 'text': "Note: in this subsection list accessibility problems that fall outside the scope of the accessibility regulations." + }) }} + +

PDFs and other documents

+ +

Some of our PDFs and Word documents are essential to providing our services. For example, we + have PDFs with information on how users can access our services, and forms published as Word documents. By + September 2020, we plan to either fix these or replace them with accessible HTML pages.

+ +

The accessibility regulations do not require us to fix PDFs or other documents published + before 23 September 2018 if they’re not essential to providing our services. For example, we do not plan to fix + [example of non-essential document].

+ +

Any new PDFs or Word documents we publish will meet accessibility standards.

+ +

Live video

+ +

We do not plan to add captions to live video streams because live video is exempt from meeting + the accessibility regulations.

+ +

What we’re doing to improve accessibility

+ + {{ govukInsetText({ + 'text': "Note: publishing an accessibility roadmap is optional. It’s a good idea to publish one if you want to be specific about the order you’re planning to tackle accessibility issues, and there’s no space to do so in the accessibility statement itself." + }) }} + +

Our accessibility roadmap [add link to roadmap] shows how and when we plan to improve + accessibility on this website.

+ +

Preparation of this accessibility statement

+ + {{ govukInsetText({ + 'text': "Note: the wording about when the statement was prepared is legally required, so do not change it." + }) }} + +

This statement was prepared on [date when it was first published]. It was last reviewed on + [date when it was last reviewed].

+ +

This website was last tested on [date]. The test was carried out by [add name of organisation + that carried out test, or indicate that you did your own testing].

+ +

We used this approach to deciding on a sample of pages to test [add link to explanation of how + you decided which pages to test].

+ + {{ govukInsetText({ + 'text': "Note: you do not have to use this approach to sampling, but you should link to a full explanation of what you tested and how you chose it. If you get a third party auditor to test your website for you, they should include sampling details in test report - so you can just to link to that." + }) }} + +

You can read the full accessibility test report [add link to report].

+ + {{ govukInsetText({ + 'text': "Note: publishing the test report is optional, but doing so may allow you to make your accessibility statement shorter and more focused." + }) }} +
+
+{% endblock %} diff --git a/application/templates/base.html b/application/templates/base.html new file mode 100644 index 0000000..af08797 --- /dev/null +++ b/application/templates/base.html @@ -0,0 +1,58 @@ +{% from 'govuk_frontend_jinja/components/accordion/macro.html' import govukAccordion %} +{% from 'govuk_frontend_jinja/components/back-link/macro.html' import govukBackLink %} +{% from 'govuk_frontend_jinja/components/breadcrumbs/macro.html' import govukBreadcrumbs %} +{% from 'govuk_frontend_jinja/components/button/macro.html' import govukButton %} +{% from 'govuk_frontend_jinja/components/character-count/macro.html' import govukCharacterCount %} +{% from 'govuk_frontend_jinja/components/checkboxes/macro.html' import govukCheckboxes %} +{% from 'govuk_frontend_jinja/components/cookie-banner/macro.html' import govukCookieBanner %} +{% from 'govuk_frontend_jinja/components/date-input/macro.html' import govukDateInput %} +{% from 'govuk_frontend_jinja/components/details/macro.html' import govukDetails %} +{% from 'govuk_frontend_jinja/components/error-message/macro.html' import govukErrorMessage %} +{% from 'govuk_frontend_jinja/components/error-summary/macro.html' import govukErrorSummary %} +{% from 'govuk_frontend_jinja/components/fieldset/macro.html' import govukFieldset %} +{% from 'govuk_frontend_jinja/components/file-upload/macro.html' import govukFileUpload %} +{% from 'govuk_frontend_jinja/components/footer/macro.html' import govukFooter %} +{% from 'govuk_frontend_jinja/components/header/macro.html' import govukHeader %} +{% from 'govuk_frontend_jinja/components/hint/macro.html' import govukHint %} +{% from 'govuk_frontend_jinja/components/input/macro.html' import govukInput %} +{% from 'govuk_frontend_jinja/components/inset-text/macro.html' import govukInsetText %} +{% from 'govuk_frontend_jinja/components/label/macro.html' import govukLabel %} +{% from 'govuk_frontend_jinja/components/notification-banner/macro.html' import govukNotificationBanner %} +{% from 'govuk_frontend_jinja/components/panel/macro.html' import govukPanel %} +{% from 'govuk_frontend_jinja/components/phase-banner/macro.html' import govukPhaseBanner %} +{% from 'govuk_frontend_jinja/components/radios/macro.html' import govukRadios %} +{% from 'govuk_frontend_jinja/components/select/macro.html' import govukSelect %} +{% from 'govuk_frontend_jinja/components/skip-link/macro.html' import govukSkipLink %} +{% from 'govuk_frontend_jinja/components/summary-list/macro.html' import govukSummaryList %} +{% from 'govuk_frontend_jinja/components/table/macro.html' import govukTable %} +{% from 'govuk_frontend_jinja/components/tabs/macro.html' import govukTabs %} +{% from 'govuk_frontend_jinja/components/tag/macro.html' import govukTag %} +{% from 'govuk_frontend_jinja/components/textarea/macro.html' import govukTextarea %} +{% from 'govuk_frontend_jinja/components/warning-text/macro.html' import govukWarningText %} + +{% extends 'govuk_frontend_jinja/template.html' %} + + +{% block head %} + + + + +{% endblock %} + +{% block header %} + {{ govukHeader({ + 'homepageUrl': url_for('index'), + 'serviceName': config['SERVICE_NAME'], + 'serviceUrl': url_for('index') + }) }} +{% endblock %} + +{% block beforeContent %} + {{ govukPhaseBanner({ + 'tag': { + 'text': config['SERVICE_PHASE'] + }, + 'html': 'This is a new service – your feedback will help us to improve it.' + }) }} +{% endblock %} diff --git a/application/templates/cookies.html b/application/templates/cookies.html new file mode 100644 index 0000000..6d6d26f --- /dev/null +++ b/application/templates/cookies.html @@ -0,0 +1,79 @@ +{% extends "base.html" %} + +{%- from 'govuk_frontend_jinja/components/back-link/macro.html' import govukBackLink -%} +{%- from 'govuk_frontend_jinja/components/table/macro.html' import govukTable -%} + +{% block pageTitle %}{%- if form.errors %}Error: {% endif -%}Cookies – {{config['SERVICE_NAME']}} – GOV.UK{% endblock %} + +{% block beforeContent %} + {{ super() }} + {{ govukBackLink({ + 'text': "Back", + 'href': url_for('main.index') + }) }} +{% endblock %} + +{% block content %} +
+
+ {{ super() }} +

Cookies

+

Cookies are small files saved on your phone, tablet or computer when you visit a website. +

+

We use cookies to make {{config['SERVICE_NAME']}} work and collect information about how you use our + service.

+ +

Essential cookies

+

Essential cookies keep your information secure while you use {{config['SERVICE_NAME']}}. We do not need + to ask permission to use them.

+ {{ govukTable( + { + 'head': [{'text': 'Name'}, {'text': 'Purpose'}, {'text': 'Expires'}], + 'rows': [ + [{'text': 'cookie_policy'}, {'text': 'Saves your cookie consent settings'}, {'text': '1 year'}], + [{'text': 'session'}, {'text': 'Temporary storage'}, {'text': 'Session'}] + ] + } + )}} + +

Functional cookies

+

Functional cookies allow you to take advantage of some functionality, for example remembering + settings between visits. The service will work without them.

+ {{ govukTable( + { + 'head': [{'text': 'Name'}, {'text': 'Purpose'}, {'text': 'Expires'}], + 'rows': [ + [{'text': 'foo'}, {'text': 'bar'}, {'text': 'baz'}] + ] + } + )}} + +

Analytics cookies

+

With your permission, we use Google Analytics to collect data about how you use {{config['SERVICE_NAME']}}. This + information helps us to improve our service.

+

Google is not allowed to use or share our analytics data with anyone.

+

Google Analytics stores anonymised information about:

+ + {{ govukTable( + { + 'head': [{'text': 'Name'}, {'text': 'Purpose'}, {'text': 'Expires'}], + 'rows': [ + [{'text': 'foo'}, {'text': 'bar'}, {'text': 'baz'}] + ] + } + )}} + +

Change your cookie settings

+
+ {{ form.csrf_token }} + {{ form.functional }} + {{ form.analytics }} + {{ form.save }} +
+
+
+{% endblock %} \ No newline at end of file diff --git a/application/templates/index.html b/application/templates/index.html new file mode 100644 index 0000000..e935734 --- /dev/null +++ b/application/templates/index.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} + +{% block beforeContent %} +{{ super() }} +{% endblock %} + +{% block content %} + {{ super() }} + + + +

Good afternoon!

+ +

I hope you're having a lovely day.

+ + + + + +{% endblock %} diff --git a/application/templates/privacy.html b/application/templates/privacy.html new file mode 100644 index 0000000..053dcd0 --- /dev/null +++ b/application/templates/privacy.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} + +{%- from 'govuk_frontend_jinja/components/back-link/macro.html' import govukBackLink -%} + +{% block pageTitle %}Privacy notice – {{config['SERVICE_NAME']}} – GOV.UK{% endblock %} + +{% block beforeContent %} + {{ super() }} + {{ govukBackLink({ + 'text': "Back", + 'href': url_for('main.index') + }) }} +{% endblock %} + +{% block content %} +
+
+ {{ super() }} +

Privacy notice

+
+
+{% endblock %} \ No newline at end of file diff --git a/build.py b/build.py new file mode 100644 index 0000000..d64aadc --- /dev/null +++ b/build.py @@ -0,0 +1,78 @@ +"""Download and extract govuk-frontend files to static folders""" +from urllib.request import urlretrieve +from pathlib import Path +import shutil +import zipfile +import os + +print("Get govuk-frontend assets") +from urllib.request import urlretrieve +from pathlib import Path +import shutil +import zipfile +import os + +assets_js_path = Path("application/assets/js") +assets_css_path = Path("application/assets/css") +assets_image_path = Path("application/assets/images") + +dirpath = Path("application/static") +css_path = dirpath / "stylesheets" +js_path = dirpath / "javascript" +image_path = dirpath / "images" +zip_file = "govuk_frontend.zip" +css = "*.css" +urlretrieve( + ( + "https://github.com/alphagov/govuk-frontend/releases/download/" + "v4.7.0/release-v4.7.0.zip" + ), + zip_file, +) +if dirpath.exists(): + shutil.rmtree(dirpath) +with zipfile.ZipFile(zip_file, "r") as zip_ref: + zip_ref.extractall(dirpath) +assets_fonts_path = str(Path(dirpath) / "assets/fonts") +static_fonts_path = str(Path(dirpath) / "fonts") +shutil.move(assets_fonts_path, static_fonts_path) +assets_images_path = str(Path(dirpath) / "assets/images") +shutil.move(assets_images_path, image_path) +assets_path = str(dirpath / "assets") +shutil.rmtree(assets_path) + +css_path.mkdir(parents=True, exist_ok=True) +for each_file in Path(dirpath).glob(css): + path = str(css_path / each_file.name) + shutil.move(str(each_file), path) + +# Rename asset to static in *.css files +for each_file in Path(css_path).glob(css): + print(each_file) + with open(each_file, "r", encoding="utf-8") as file: + filedata = file.read() + + # Replace the target string + filedata = filedata.replace("/assets", "/static") + + # Write the file out again + with open(each_file, "w", encoding="utf-8") as file: + file.write(filedata) + +for each_file in Path(assets_css_path).glob(css): + shutil.copy(each_file, css_path / each_file.name) + +js_path.mkdir(parents=True, exist_ok=True) +for each_file in Path(dirpath).glob("*.js"): + path = str(js_path / each_file.name) + shutil.move(str(each_file), path) + +for each_file in Path(assets_js_path).glob("*.js"): + shutil.copy(each_file, js_path / each_file.name) + +for each_file in Path(assets_image_path).glob("*.png"): + shutil.copy(each_file, image_path / each_file.name) + +os.remove(zip_file) +print("Finished") +print("If version has changed update the versions in build.py and application/templates/base.html") diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..cf69d26 --- /dev/null +++ b/compose.yml @@ -0,0 +1,9 @@ +services: + web: + container_name: app + build: . + restart: always + ports: + - 1551:1551 + volumes: + - .:/home/containeruser diff --git a/config.py b/config.py new file mode 100644 index 0000000..8ace2bb --- /dev/null +++ b/config.py @@ -0,0 +1,16 @@ +import os + + +class Config(object): + CONTACT_EMAIL = "operations_engineering@digital.justice.gov.uk" + # CONTACT_PHONE = os.environ.get("CONTACT_PHONE") + DEPARTMENT_NAME = "Ministry of Justice" + # DEPARTMENT_URL = os.environ.get("DEPARTMENT_URL") + # RATELIMIT_HEADERS_ENABLED = True + # RATELIMIT_STORAGE_URI = os.environ.get("REDIS_URL") + # SECRET_KEY = os.environ.get("SECRET_KEY") + SERVICE_NAME = "Operations Engineering Flask App Template" + SERVICE_PHASE = "Dev" + SERVICE_URL = "https://operations-engineering-example.cloud-platform.service.justice.gov.uk/home" + # SESSION_COOKIE_HTTPONLY = True + # SESSION_COOKIE_SECURE = True diff --git a/makefile b/makefile index 299f7cf..02c28ef 100644 --- a/makefile +++ b/makefile @@ -25,65 +25,10 @@ setup: @venv/bin/pip3 install -r requirements.txt preview: - flask --app ops_eng_app/__init__ --debug run - - - -# venv: requirements.txt requirements-test.txt -# python3 -m venv venv -# @venv/bin/pip3 install --upgrade pip -# @venv/bin/pip3 install -r requirements.txt - -# lint: venv -# @venv/bin/flake8 --ignore=E501,W503 $(PYTHON_SOURCE_FILES) -# @venv/bin/mypy --ignore-missing-imports $(PYTHON_SOURCE_FILES) -# @venv/bin/pylint --recursive=y $(PYTHON_SOURCE_FILES) - -# format: venv -# @venv/bin/black $(PYTHON_SOURCE_FILES) - -# test: -# export FLASK_CONFIGURATION=development; python3 -m pytest -v - -# clean-test: -# rm -fr venv -# rm -fr .tox/ -# rm -fr .pytest_cache -# rm -fr .mypy_cache -# rm -fr .coverage -# rm -fr htmlcov/ -# rm -fr .pytest_cache + flask --app application/__init__ --debug run +local: + docker compose up --build -# # To run locally, you need to pass the following: -# # make deploy IMAGE=my-image RELEASE_NAME=my-release AUTH0_CLIENT_ID=my-auth0-id AUTH0_CLIENT_SECRET=my-secret APP_SECRET_KEY=my-app-secret ENCRYPTION_KEY=my-encryption-key API_KEY=my-api-key HOST_SUFFIX=my-host-suffix -# deploy-dev: -# helm --debug upgrade $(RELEASE_NAME) helm/operations-engineering-reports \ -# --install \ -# --force \ -# --wait \ -# --set image.tag=$(IMAGE) \ -# --set application.auth0ClientId=$(AUTH0_CLIENT_ID) \ -# --set application.auth0ClientSecret=$(AUTH0_CLIENT_SECRET) \ -# --set application.appSecretKey=$(APP_SECRET_KEY) \ -# --set application.encryptionKey=$(ENCRYPTION_KEY) \ -# --set application.apiKey=$(API_KEY) \ -# --set ingress.hosts={operations-engineering-reports-dev-$(HOST_SUFFIX).cloud-platform.service.justice.gov.uk} \ -# --set image.repository=754256621582.dkr.ecr.eu-west-2.amazonaws.com/operations-engineering/operations-engineering-reports-dev-ecr \ -# --namespace operations-engineering-reports-dev - -# delete-dev: -# helm delete $(RELEASE_NAME) --namespace operations-engineering-reports-dev - -# all: - -# dev: -# bash scripts/start-db-dev.sh - -# db-ui: -# bash scripts/start-db-ui.sh - -# stop: -# docker-compose down -v --remove-orphans - -.PHONY: preview setup help # dev stop venv lint test format local prod clean-test all + +.PHONY: help setup preview local diff --git a/ops_eng_app/__init__.py b/ops_eng_app/__init__.py deleted file mode 100644 index 477f2ee..0000000 --- a/ops_eng_app/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from flask import Flask, render_template, url_for - -app = Flask(__name__) - -@app.route("/", methods=["GET", "POST"]) -def gudetama(): - return render_template("index.html") - -# if __name__ == '__main__': -# app.run() \ No newline at end of file diff --git a/ops_eng_app/static/gudetama_meh.png b/ops_eng_app/static/gudetama_meh.png deleted file mode 100644 index 4588c98..0000000 Binary files a/ops_eng_app/static/gudetama_meh.png and /dev/null differ diff --git a/ops_eng_app/templates/index.html b/ops_eng_app/templates/index.html deleted file mode 100644 index 49bf347..0000000 --- a/ops_eng_app/templates/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - -

Hello Gudetama!

- -

I hope you're having a lovely day?

- - - - - - diff --git a/requirements.txt b/requirements.txt index 833b712..5920a7b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,12 +2,20 @@ boto3~=1.29.4 # 1.26.159 botocore~=1.32.4 # 1.29.159 #cryptography~=41.0.3 -flask~=3.0.0 # 2.3.3 +cssmin +email_validator +flask~=3.0.0 +flask-assets +flask-compress flask-cors~=4.0.0 +flask-limiter +flask-talisman govuk-frontend-jinja~=2.7.0 +govuk-frontend-wtf gunicorn~=21.2.0 +Jinja2~=3.1.2 +jsmin python-dotenv~=1.0.0 -requests~=2.31.0 pytest~=7.4.3 +requests~=2.31.0 #setuptools~=68.0.0 -Jinja2~=3.1.2