Skip to content

Commit

Permalink
Implement new Postgres fund store schema (#69)
Browse files Browse the repository at this point in the history

Co-authored-by: Sarah Sloan <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: tferns <[email protected]>
Co-authored-by: gio-karunakaran <[email protected]>
Co-authored-by: adamdavies1 <[email protected]>
Co-authored-by: Gio Karunakaran <[email protected]>
  • Loading branch information
7 people authored May 16, 2023
1 parent 463acd4 commit ecfcff8
Show file tree
Hide file tree
Showing 60 changed files with 4,703 additions and 394 deletions.
1 change: 1 addition & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ jobs:
with:
app_name: ${{ github.event.repository.name }}
api: true
postgres_unit_testing: true
run_performance_tests: true
run_e2e_tests: true
secrets:
Expand Down
6 changes: 6 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@
}
],
"justMyCode": true
},
{
"name": "Debug Unit Test",
"type": "python",
"purpose": ["debug-test"],
"justMyCode": false,
}
]
}
96 changes: 96 additions & 0 deletions api/routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from db.queries import get_all_funds
from db.queries import get_application_sections_for_round
from db.queries import get_assessment_sections_for_round
from db.queries import get_fund_by_id
from db.queries import get_fund_by_short_name
from db.queries import get_round_by_id
from db.queries import get_round_by_short_name
from db.queries import get_rounds_for_fund_by_id
from db.queries import get_rounds_for_fund_by_short_name
from db.schemas.fund import FundSchema
from db.schemas.round import RoundSchema
from db.schemas.section import SectionSchema
from distutils.util import strtobool
from flask import abort
from flask import request
from fsd_utils.locale_selector.get_lang import get_lang


def get_funds():
# language = request.args.get("language")

funds = get_all_funds()

if funds:
serialiser = FundSchema()
return serialiser.dump(funds, many=True)

abort(404)


def get_fund(fund_id):
# language = request.args.get("language")
short_name_arg = request.args.get("use_short_name")
use_short_name = short_name_arg and strtobool(short_name_arg)

if use_short_name:
fund = get_fund_by_short_name(fund_id)
else:
fund = get_fund_by_id(fund_id)

if fund:
serialiser = FundSchema()
return serialiser.dump(fund)

abort(404)


def get_round(fund_id, round_id):
# language = request.args.get("language")
short_name_arg = request.args.get("use_short_name")
use_short_name = short_name_arg and strtobool(short_name_arg)

if use_short_name:
round = get_round_by_short_name(fund_id, round_id)
else:
round = get_round_by_id(fund_id, round_id)
if round:
serialiser = RoundSchema()
return serialiser.dump(round)

abort(404)


def get_rounds_for_fund(fund_id):
# language = request.args.get("language")
short_name_arg = request.args.get("use_short_name")
use_short_name = short_name_arg and strtobool(short_name_arg)

if use_short_name:
rounds = get_rounds_for_fund_by_short_name(fund_id)
else:
rounds = get_rounds_for_fund_by_id(fund_id)

if rounds:
serialiser = RoundSchema()
return serialiser.dump(rounds, many=True)

abort(404)


def get_sections_for_round_application(fund_id, round_id):
sections = get_application_sections_for_round(fund_id, round_id)
if sections:
serialiser = SectionSchema()
return serialiser.dump(sections, many=True)

abort(404)


def get_sections_for_round_assessment(fund_id, round_id):
sections = get_assessment_sections_for_round(fund_id, round_id, get_lang())
if sections:
serialiser = SectionSchema()
return serialiser.dump(sections, many=True)

abort(404)
18 changes: 18 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import connexion
import psycopg2
from db.models import Fund # noqa
from db.models import Round # noqa
from db.models import Section # noqa
from flask import Flask
from fsd_utils import init_sentry
from fsd_utils.healthchecks.checkers import DbChecker
from fsd_utils.healthchecks.checkers import FlaskRunningChecker
from fsd_utils.healthchecks.healthcheck import Healthcheck
from fsd_utils.logging import logging
from openapi.utils import get_bundled_specs
from sqlalchemy_utils import Ltree


def create_app() -> Flask:
Expand All @@ -23,11 +29,23 @@ def create_app() -> Flask:
flask_app = connexion_app.app
flask_app.config.from_object("config.Config")

from db import db, 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="db/migrations", render_as_batch=True)
# Enable mapping of ltree datatype for sections
psycopg2.extensions.register_adapter(
Ltree, lambda ltree: psycopg2.extensions.QuotedString(str(ltree))
)

# Initialise logging
logging.init_app(flask_app)

health = Healthcheck(flask_app)
health.add_check(FlaskRunningChecker())
health.add_check(DbChecker(db))

return flask_app

Expand Down
5 changes: 5 additions & 0 deletions config/envs/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ class DefaultConfig(object):
STATIC_FOLDER = "static"
TEMPLATES_FOLDER = "templates"
FORCE_OPEN = strtobool(getenv("FORCE_OPEN", "False"))

# Database
SQLALCHEMY_DATABASE_URI = environ.get("DATABASE_URL", "").replace(
"postgres://", "postgresql://"
)
6 changes: 6 additions & 0 deletions config/envs/development.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Flask Local Development Environment Configuration."""
import logging
from os import environ

from config.envs.default import DefaultConfig as Config
from fsd_utils import configclass
Expand All @@ -14,3 +15,8 @@ class DevelopmentConfig(Config):

# Logging
FSD_LOG_LEVEL = logging.DEBUG

SQLALCHEMY_DATABASE_URI = environ.get(
"DATABASE_URL",
"postgresql://postgres:[email protected]:5432/fsd_fund_store_1",
)
7 changes: 7 additions & 0 deletions config/envs/unit_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Flask Local Development Environment Configuration."""
import logging
from os import environ

from config.envs.default import DefaultConfig as Config
from fsd_utils import configclass
Expand All @@ -13,3 +14,9 @@ class UnitTestConfig(Config):

# Logging
FSD_LOG_LEVEL = logging.DEBUG

# Database
SQLALCHEMY_DATABASE_URI = environ.get(
"DATABASE_URL",
"postgresql://postgres:[email protected]:5432/fsd_fund_store_unit_test",
)
21 changes: 21 additions & 0 deletions db/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Local setup

Tasks to help with dropping and recreating, then repopulating the db during development are in `tasks.py`

1. Set local DATABASE_URL in environment (default in config is `postgresql://postgres:[email protected]:5432/fsd_fund_store_1`)
1. To recreate the db instance, run

inv recreate-local-db

this drops and recreates the DB
1. To re-initialise the flask db migrations, run

inv init-migr
1. Once the initial migration version is created, update that python file (`db/migrations/xxxx_initial_migration.py`) to add required imports:

import sqlalchemy_utils
1. To perform the migrations on the db and seed data, run

inv seed-db

This runs the migrations and the sql scripts in `db/cof_sql`. Then runs some select statements to show result of inserts.
17 changes: 17 additions & 0 deletions db/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import MetaData

convention = {
"ix": "ix_%(column_0_label)s",
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(constraint_name)s",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s",
}

metadata = MetaData(naming_convention=convention)

db = SQLAlchemy(metadata=metadata)

migrate = Migrate()
6 changes: 6 additions & 0 deletions db/cof_sql/assessment_fields.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
INSERT INTO assessment_field(id, title, field_type, display_type)
VALUES('YdtlQZ', 'Organisation name', 'TextField', 'text');
INSERT INTO assessment_field(id, title, field_type, display_type)
VALUES('iBCGxY', 'Does your organisation use any other names', 'YesNoField', 'text');
INSERT INTO assessment_field(id, title, field_type, display_type)
VALUES('JCACTy', 'Have you done any fundraising in the community', 'YesNoField', 'text');
4 changes: 4 additions & 0 deletions db/cof_sql/form_name.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
insert into form_name (section_id, form_name) values(9, 'community-engagement');
insert into form_name (section_id, form_name) values(8, 'community-use');
insert into form_name (section_id, form_name) values(5, 'organisation-information');
insert into form_name (section_id, form_name) values(6, 'applicant-information');
9 changes: 9 additions & 0 deletions db/cof_sql/fund.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

INSERT INTO fund ("id", "name", "title", "short_name", "description") VALUES ('47aef2f5-3fcb-4d45-acb5-f0152b5f03c4',
'Community Ownership Fund',
'funding to save an asset in your community',
'COF',
'The Community Ownership Fund is a £150 million fund over 4'
' years to support community groups across England, Wales,'
' Scotland and Northern Ireland to take ownership of assets'
' which are at risk of being lost to the community.');
77 changes: 77 additions & 0 deletions db/cof_sql/rounds.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
INSERT INTO
round (
"id",
"title",
"short_name",
"opens",
"deadline",
"fund_id",
"assessment_deadline",
"prospectus",
"privacy_notice",
"contact_email",
"contact_phone",
"contact_textphone",
"support_times",
"support_days",
"instructions"
)
VALUES
(
'c603d114-5364-4474-a0c4-c41cbf4d3bbd',
'Round 2 Window 2',
'R2W2',
'2022-10-04 12:00:00',
'2022-12-14 11:59:00',
'47aef2f5-3fcb-4d45-acb5-f0152b5f03c4',
'2023-03-30 12:00:00',
'https://www.gov.uk/government/publications/community-ownership-fund-prospectus',
'https://www.gov.uk/government/publications/community-ownership-fund-privacy-notice/community-ownership-fund-privacy-notice',
'[email protected]',
null,
null,
'9am to 5pm',
'Monday to Friday',
'You must have received an invitation to apply. If we did not invite you, first'
' <a href="https://www.gov.uk/government/publications/community-ownership-fund-prospectus">'
' express your interest in the fund</a>.'
);

INSERT INTO
round (
"id",
"title",
"short_name",
"opens",
"deadline",
"fund_id",
"assessment_deadline",
"prospectus",
"privacy_notice",
"contact_email",
"contact_phone",
"contact_textphone",
"support_times",
"support_days",
"instructions"
)
VALUES
(
'5cf439bf-ef6f-431e-92c5-a1d90a4dd32f',
'Round 2 Window 3',
'R2W3',
'2022-10-04 12:00:00',
'2022-12-14 11:59:00',
'47aef2f5-3fcb-4d45-acb5-f0152b5f03c4',
'2023-03-30 12:00:00',
'https://www.gov.uk/government/publications/community-ownership-fund-prospectus',
'https://www.gov.uk/government/publications/community-ownership-fund-privacy-notice/community-ownership-fund-privacy-notice',
'[email protected]',
null,
null,
'9am to 5pm',
'Monday to Friday',
'You must have received an invitation to apply. If we did not invite you, first'
' <a href="https://www.gov.uk/government/publications/community-ownership-fund-prospectus">'
' express your interest in the fund</a>.'
);
6 changes: 6 additions & 0 deletions db/cof_sql/section_fields.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
INSERT INTO section_field(section_id, field_id, display_order)
VALUES(22, 'YdtlQZ', 10);
INSERT INTO section_field(section_id, field_id, display_order)
VALUES(22, 'iBCGxY', 20);
INSERT INTO section_field(section_id, field_id, display_order)
VALUES(20, 'JCACTy', 10);
Loading

0 comments on commit ecfcff8

Please sign in to comment.