Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin' into new-service
Browse files Browse the repository at this point in the history
rebase
  • Loading branch information
Tiny49 committed Jul 18, 2024
2 parents fb834a7 + a700fd9 commit b02d386
Show file tree
Hide file tree
Showing 94 changed files with 4,397 additions and 2,435 deletions.
11 changes: 7 additions & 4 deletions .devcontainer/python-container/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
{
"name": "Python Container",
"dockerComposeFile": [
"../../docker-compose.yml"
"../../docker-compose.yml",
"../../compose.override.yml"
],
"service": "fsd-self-serve",
"service": "fab",
"shutdownAction": "none",
"workspaceFolder": "/self-serve",
"workspaceFolder": "/fab",
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance",
"ms-python.debugpy",
"ms-python.black-formatter"
"ms-python.black-formatter",
"ms-python.isort",
"monosans.djlint"
]
}
}
Expand Down
34 changes: 34 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
repos:
- repo: https://github.com/psf/black
rev: 24.3.0
hooks:
- id: black
language_version: python3
- repo: https://github.com/PyCQA/flake8
rev: 7.0.0
hooks:
- id: flake8
additional_dependencies: [Flake8-pyproject]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-ast
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
name: isort (python)
- repo: https://github.com/Yelp/detect-secrets
rev: v1.4.0
hooks:
- id: detect-secrets
args:
[
"--disable-plugin",
"HexHighEntropyString",
"--disable-plugin",
"Base64HighEntropyString",
]
exclude: .env.development
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@
"autoStartBrowser": false
}
]
}
}
7 changes: 5 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
{ "workbench.colorCustomizations": {
"titleBar.activeBackground": "#bf4bb7",
"titleBar.activeForeground": "#fdfbfb",
},
"python.testing.pytestArgs": [
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}
}
10 changes: 5 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
###############################################################################
#
# Self Service Dev Image
# Fund Application Builder (FAB) Dev Image
#
###############################################################################

FROM ghcr.io/communitiesuk/fsd-base-dev/frontend:sha-68d9e31a4ff4adc9b5ead035e1a82203ec93d919 as self-serve-dev
FROM python:3.10-bullseye as fab-dev


WORKDIR /app

COPY . .
RUN apt-get update && apt-get install -y postgresql-client

RUN python3 -m pip install --upgrade pip && pip install -r requirements.txt


RUN python3 -m pip install --upgrade pip && pip install pip-tools && pip install -r requirements.txt
RUN python3 -m pip install -r requirements-dev.txt
92 changes: 90 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,90 @@
# funding-service-design-self-serve
Prototype self service app
# Prototype Fund Application Builder (FAB)
Flask app, with database, for building a fund and applications.

# Run
## Dev Containers
This repo contains a devcontainer spec for VS code at [.devcontainer](.devcontainer/python-container/devcontainer.json). This uses docker-compose to start up a container to develop the app, and also a companion postgres instance. The default connection URLs used for tests and at runtime are set to pickup the DB from that docker-compose instance so you don't need to set anything up manually.

## Local development
If developing locally, you will need a postgres instance running and to set the following environment variables:
- `DATABASE_URL` to a suitable connection string. The default used is
`postgresql://postgres:password@fab-db:5432/fund_builder`. # pragma: allowlist secret
- `DATABASE_URL_UNIT_TEST` default
`postgresql://postgres:password@fab-db:5432/fund_builder_unit_test` # pragma: allowlist secret

## General
Run the app with `flask run` (include `--debug` for auto reloading on file changes)

## Helper Tasks
Contained in [db_tasks.py](./tasks/db_tasks.py)

### Recreate Local DBs
For both `DATABASE_URL` and `DATABASE_URL_UNIT_TEST`, drops the database if it exists and then recreates it.

### Init migrations
Deletes the [versions](./app/db/migrations/versions/) directory and runs `migrate()` to generate a new intial migration version for the SQLAlchemy models.

### Create Test Data
Inserts a set of seed data comprising one Fund, 2 rounds and the applicaiton/assessment config for one of those rounds. The data created is defined in [test_data.py](./tasks/test_data.py)

## With Form Runner for Form Previews
In order to use the 'Preview Form' function, FAB needs to connect to a running instance of form runner. For prototyping, no session is created in the form runner, so it needs to be started without expecting the JWT information.

Below is a docker-compose file that will start both the FAB app, and the form runner, with the appropriate connection information. FAB will start on http://localhost:8080 and connect to the form runner defined in docker compose. If you want to do this, create the following directory structure and put this file in docker-compose.yml under DC.


```
workspace
| - dc
| - docker-compose.yml
| - digital-form-builder
| - funding-service-design-fund-application-builder
```

```
services:
fab:
hostname: fab
build:
context: ../funding-service-design-fund-application-builder
dockerfile: Dockerfile
command: ["sh", "-c", "python -m flask db upgrade && inv create-test-data && python -m flask run --host 0.0.0.0 --port 8080"]
ports:
- 8080:8080
environment:
- FORM_RUNNER_INTERNAL_HOST=http://form-runner:3009
- FORM_RUNNER_EXTERNAL_HOST=http://localhost:3009
- DATABASE_URL=postgresql://postgres:password@fab-db:5432/fund_builder # pragma: allowlist secret
depends_on: [fab-db]
fab-db:
image: postgres
environment:
- POSTGRES_PASSWORD=password
- POSTGRES_DB=fund_builder
form-runner:
build:
context: ../digital-form-builder
dockerfile: ./fsd_config/Dockerfile
command: yarn runner startdebug
links:
- fab:fab
ports:
- 3009:3009
- 9228:9228
environment:
- LOG_LEVEL=debug
- 'NODE_CONFIG={"safelist": ["fab"]}'
- CONTACT_US_URL=http://localhost:3008/contact_us
- FEEDBACK_LINK=http://localhost:3008/feedback
- COOKIE_POLICY_URL=http://localhost:3008/cookie_policy
- ACCESSIBILITY_STATEMENT_URL=http://localhost:3008/accessibility_statement
- SERVICE_START_PAGE=http://localhost:3008/account
- MULTIFUND_URL=http://localhost:3008/account
- LOGOUT_URL=http://localhost:3004/sessions/sign-out
- PRIVACY_POLICY_URL=http://localhost:3008/privacy
- ELIGIBILITY_RESULT_URL=http://localhost:3008/eligibility-result
- PREVIEW_MODE=true
- NODE_ENV=development
```
1 change: 0 additions & 1 deletion app/all_questions/metadata_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,6 @@ def generate_print_data_for_sections(
form_print_data = {}
form_idx = 0
for child_form in section["forms"]:
form_name = child_form["name"]
form_data = child_form["form_data"]
form_metadata = generate_metadata(form_data)
form_index = {}
Expand Down
1 change: 0 additions & 1 deletion app/all_questions/read_forms.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

def determine_display_value_for_condition(
condition_value: str,
list_name: str = None,
Expand Down
40 changes: 30 additions & 10 deletions app/app.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,43 @@
from flask import Flask

from jinja2 import PackageLoader, ChoiceLoader
from flask_assets import Bundle
from jinja2 import PrefixLoader
from app.blueprints.self_serve.routes import self_serve_bp
from app.blueprints.dev.routes import dev_bp
from flask_assets import Environment
from jinja2 import ChoiceLoader
from jinja2 import PackageLoader
from jinja2 import PrefixLoader

import static_assets
from app.blueprints.dev.routes import dev_bp
from app.blueprints.fund_builder.routes import build_fund_bp
from app.blueprints.self_serve.routes import self_serve_bp
from app.db.models import Fund # noqa:F401
from app.db.models import Round # noqa:F401


def create_app() -> Flask:

flask_app = Flask("__name__", static_url_path="/assets")
flask_app.secret_key="dev"
flask_app.secret_key = "dev" # pragma: allowlist secret
flask_app.register_blueprint(self_serve_bp)
flask_app.register_blueprint(dev_bp)
flask_app.register_blueprint(build_fund_bp)

flask_app.config.from_object("config.Config")

# flask_app.static_url_path = "/static"
flask_app.static_folder = "app/static/dist"

from app.db import db
from app.db import migrate

# Bind SQLAlchemy ORM to Flask app
db.init_app(flask_app)
# Bind Flask-Migrate db utilities to Flask app
migrate.init_app(
flask_app,
db,
directory="app/db/migrations",
render_as_batch=True,
compare_type=True,
compare_server_default=True,
)

# Bundle and compile assets
assets = Environment()
Expand All @@ -36,7 +56,7 @@ def create_app() -> Flask:
]
)


return flask_app

app = create_app()

app = create_app()
79 changes: 47 additions & 32 deletions app/blueprints/dev/routes.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
from flask import Blueprint, redirect, url_for, current_app, request
from app.data.data_access import (
get_responses,
clear_all_responses,
get_saved_forms,
save_response,
get_all_components,
get_all_pages,
get_all_sections,
)

from flask import Blueprint
from flask import current_app
from flask import redirect
from flask import request
from flask import url_for

from app.blueprints.self_serve.data.data_access import clear_all_responses
from app.blueprints.self_serve.data.data_access import get_all_components
from app.blueprints.self_serve.data.data_access import get_all_pages
from app.blueprints.self_serve.data.data_access import get_all_sections
from app.blueprints.self_serve.data.data_access import get_responses
from app.blueprints.self_serve.data.data_access import get_saved_forms
from app.blueprints.self_serve.data.data_access import save_response

# Blueprint for dev related routes, eg. saving responses from when the form is in preview mode in the form runner
dev_bp = Blueprint(
"dev_bp",
__name__,
Expand All @@ -19,16 +23,48 @@

@dev_bp.route("/responses")
def view_responses():
"""
Retrieves all responses received from a 'Save per page' callback when a form is in preview mode.
These are saved in-memory, not the DB
"""
responses = get_responses()
return responses


@dev_bp.route("/responses/clear")
def clear_responses():
"""
Clears all responses received from a 'Save per page' callback when a form is in preview mode.
"""
clear_all_responses()
return redirect(url_for("dev_bp.view_responses"))


@dev_bp.route("/save", methods=["PUT"])
def save_per_page():
"""
Mock version of the 'save per page' route in application store - used to save and enable viewing of save
per page requests for a form in preview mode to aid development/debugging
"""
current_app.logger.info("Saving request")
request_json = request.get_json(force=True)
current_app.logger.info(request_json)
form_dict = {
"application_id": "",
"form_name": request_json["name"],
"question_json": request_json["questions"],
"is_summary_page_submit": request_json["metadata"].get("isSummaryPageSubmit", False),
}
updated_form = save_response(form_dict=form_dict)
return updated_form, 201


# ====================================================================================================
# Functions below this line are used by the original prototype version (drag and drop) before being
# updated to use the database
# ====================================================================================================


@dev_bp.route("/forms")
def view_forms():
forms = get_saved_forms()
Expand All @@ -51,24 +87,3 @@ def view_sections():
def view_questions():
forms = get_all_components()
return forms


# @dev_bp.route("/forms/clear")
# def clear_forms():
# clear_saved_forms()
# return redirect(url_for("dev_bp.view_forms"))


@dev_bp.route("/save", methods=["PUT"])
def save_per_page():
current_app.logger.info("Saving request")
request_json = request.get_json(force=True)
current_app.logger.info(request_json)
form_dict = {
"application_id": "",
"form_name": request_json["name"],
"question_json": request_json["questions"],
"is_summary_page_submit": request_json["metadata"].get("isSummaryPageSubmit", False),
}
updated_form = save_response(form_dict=form_dict)
return updated_form, 201
15 changes: 15 additions & 0 deletions app/blueprints/fund_builder/forms/fund.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from flask_wtf import FlaskForm
from wtforms import BooleanField
from wtforms import HiddenField
from wtforms import StringField
from wtforms.validators import DataRequired
from wtforms.validators import Length


class FundForm(FlaskForm):
fund_id = HiddenField("Fund ID")
name_en = StringField("Name", validators=[DataRequired()])
title_en = StringField("Title", validators=[DataRequired()])
short_name = StringField("Short Name", validators=[DataRequired(), Length(max=6)])
description_en = StringField("Description", validators=[DataRequired()])
welsh_available = BooleanField("Welsh Available")
Loading

0 comments on commit b02d386

Please sign in to comment.