Submit social housing lettings and sales data (CORE)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
baarkerlounger 79fc3f7e48 Fix more tests 2 years ago
.github/workflows Fix lettings allocation (cbl, chr, cap) import (#665) 2 years ago
app CLDC-456-start-date-validations (#683) 2 years ago
bin Switch to JSBundling with webpack (#392) 3 years ago
config CLDC-456-start-date-validations (#683) 2 years ago
db CLDC-456-start-date-validations (#683) 2 years ago
docs Update alpha document 2 years ago
lib Lint JavaScript (#667) 2 years ago
log Add application skeleton to repo 3 years ago
public Link to privacy policy page, not old PDF 3 years ago
spec Fix more tests 2 years ago
storage Add application skeleton to repo 3 years ago
tmp Add application skeleton to repo 3 years ago
vendor Add application skeleton to repo 3 years ago
.cfignore Add docs dir to dockerignore and cfignore (#555) 2 years ago
.dockerignore Add docs dir to dockerignore and cfignore (#555) 2 years ago
.editorconfig Add application skeleton to repo 3 years ago
.env.example Use rake task to send onboarding emails (#566) 2 years ago
.erb-lint.yml Explicitly exclude vendor directories from being covered by ERB Lint 3 years ago
.gitignore Switch to JSBundling with webpack (#392) 3 years ago
.nvmrc Bump node and ruby for new buildpack (#300) 3 years ago
.overcommit.yml Add application skeleton to repo 3 years ago
.rspec Add application skeleton to repo 3 years ago
.rubocop.yml Remove rubocop rule 3 years ago
.ruby-version Bump dependencies (#597) 2 years ago
Dockerfile Bump bundler in dockerfiles 2 years ago
Dockerfile_dev Bump bundler in dockerfiles 2 years ago
Gemfile Bump deps 2 years ago
Gemfile.lock Bump deps 2 years ago
LICENSE Remove all ampersands from the service 3 years ago
Procfile.dev Switch to JSBundling with webpack (#392) 3 years ago
README.md Fix typo in README (#690) 2 years ago
Rakefile Add application skeleton to repo 3 years ago
babel.config.js Lint JavaScript (#667) 2 years ago
config.ru Add application skeleton to repo 3 years ago
docker-compose.yml Fix docker compose flow (#275) 3 years ago
infrastructure_setup.md Infra documentation (#290) 3 years ago
manifest.yml CLDC-1015: limit password reset (#356) 3 years ago
package.json Lint JavaScript (#667) 2 years ago
postcss.config.js Add application skeleton to repo 3 years ago
webpack.config.js Lint JavaScript (#667) 2 years ago
yarn.lock Refactor log setup into code (#672) 2 years ago

README.md

Submit social housing lettings and sales data (CORE)

Production CI/CD Pipeline Staging CI/CD Pipeline

Codebase for the Ruby on Rails app that handles the submission of lettings and sales of social housing data in England.

API documentation

API documentation can be found here: https://communitiesuk.github.io/mhclg-data-collection-beta. This is driven by OpenAPI docs

Required Setup

Pre-requisites:

  • Ruby
  • Rails
  • Postgres

Quick start

  1. Copy the .env.example to .env and replace the database credentials with your local postgres user credentials.

  2. Install the dependencies:
    bundle install

  3. Create the database:
    rake db:create

  4. Run the database migrations:
    rake db:migrate

  5. Seed the database if required:
    rake db:seed

  6. Seed the database with rent ranges if required (~7000 rows per year):
    rake "data_import:rent_ranges[<start_year>,<rent_ranges_path>]"

    For 2021-2022 ranges run:
    rake "data_import:rent_ranges[2021,config/rent_range_data/2021.csv]"

  7. Install the frontend depenencies:
    yarn install

  8. Start the dev servers using foreman:
    ./bin/dev

Or start them individually:\

a. Rails:
bundle exec rails s

b. JS (for hot reloading):
yarn build --mode=development --watch

If you're not modifying front end assets you can bundle them as a one off task:
yarn build --mode=development

Development mode will target the latest versions of Chrome, Firefox and Safari for transpilation while production mode will target older browsers.

The Rails server will start on http://localhost:3000.

Running the test suite (front end assets need to be built or server needs to be running):
bundle exec rspec

Using Docker

  1. Build the image:
    docker-compose build

  2. Run the database migrations:
    docker-compose run --rm app /bin/bash -c 'rake db:migrate'

  3. Seed the database if required:
    docker-compose run --rm app /bin/bash -c 'rake db:seed'

  4. To be able to debug with Pry run the app using:
    docker-compose run --service-ports app

If this is not needed you can run docker-compose up as normal

The Rails server will start on http://localhost:8080.

Infrastructure

This application is running on GOV.UK PaaS. To deploy you need to:

  1. Contact your organisation manager to get an account in dluhc-core organization and in the relevant spaces (staging/production).

  2. Install the Cloud Foundry CLI

  3. Login:
    cf login -a api.london.cloud.service.gov.uk -u <your_username>

  4. Set your deployment target (staging/production):
    cf target -o dluhc-core -s <deploy_environment>

  5. Deploy:
    cf push dluhc-core --strategy rolling. This will use the manifest file

Once the app is deployed:

  1. Get a Rails console:
    cf ssh dluhc-core-staging -t -c "/tmp/lifecycle/launcher /home/vcap/app 'rails console' ''"

  2. Check logs:
    cf logs dluhc-core-staging --recent

Troubleshooting deployments

A failed Github deployment action will occasionally leave a Cloud Foundry deployment in a broken state. As a result all subsequent Github deployment actions will also fail with the message Cannot update this process while a deployment is in flight.

cf cancel-deployment dluhc-core

You'd then need to check the logs and fix the issue that caused the initial deployment to fail.

CI/CD

When a commit is made to main the following GitHub action jobs are triggered:

  1. Test: RSpec runs our test suite
  2. Deploy: If the Test stage passes, this job will deploy the app to our GOV.UK PaaS account using the Cloud Foundry CLI

When a pull request is opened to main only the Test stage runs.

Frontend

GOV.UK Design System components

This service follows the guidance and recommendations from the GOV.UK Design System. This is achieved using the following libraries:

Service-specific components

Service-specific components are built using the ViewComponent framework, and can be found in app/components.

Components use HTML class names that follow the BEM methodology. We use the app-* prefix to prevent collisions with components provided by the Design System (which uses govuk-*). See Extending and modifying components in production.

Stylesheets are written using Sass (and the SCSS syntax), using the mixins and helpers provided by govuk-frontend.

Separate stylesheets are used for each component, with filenames that match the component’s namespace.

Like the components provided by the Design System, components are progressively enhanced. We use Stimulus to add any client-side JavaScript enhancements.

Single log submission form configuration

The form for this is driven by a JSON file in /config/forms/{start_year}_{end_year}.json

The JSON should follow the structure:

{
  "form_type": "lettings" / "sales",
  "start_year": Integer, // i.e. 2020
  "end_year": Integer, // i.e. 2021
  "sections": {
    "[snake_case_section_name_string]": {
      "label": String,
      "description": String,
      "subsections": {
        "[snake_case_subsection_name_string]": {
          "label": String,
          "pages": {
            "[snake_case_page_name_string]": {
              "header": String,
              "description": String,
              "questions": {
                "[snake_case_question_name_string]": {
                  "header": String,
                  "hint_text": String,
                  "check_answer_label": String,
                  "type": "text" / "numeric" / "radio" / "checkbox" / "date",
                  "min": Integer, // numeric only
                  "max": Integer, // numeric only
                  "step": Integer, // numeric only
                  "width": 2 / 3 / 4 / 5 / 10 / 20, // text and numeric only
                  "prefix": String, // numeric only
                  "suffix": String, //numeric only
                  "answer_options": { // checkbox and radio only
                    "0": String,
                    "1": String
                  },
                  "conditional_for": {
                    "[snake_case_question_to_enable_1_name_string]": ["condition-that-enables"],
                    "[snake_case_question_to_enable_2_name_string]": ["condition-that-enables"]
                  },
                  "inferred_answers": { "field_that_gets_inferred_from_current_field": { "is_that_field_inferred": true } },
                  "inferred_check_answers_value": {
                    "condition": { "field_name_for_inferred_check_answers_condition": "field_value_for_inferred_check_answers_condition" },
                    "value": "Inferred value that gets displayed if condition is met"
                  }
                }
              },
              "depends_on": [{ "question_key": "answer_value_required_for_this_page_to_be_shown" }]
            }
          }
        }
      }
    }
  }
}

Assumptions made by the format:

  • All forms have at least 1 section

  • All sections have at least 1 subsection

  • All subsections have at least 1 page

  • All pages have at least 1 question

  • The ActiveRecord case log model has a field for each question name (must match). In the case of checkbox questions it must have one field for every answer option (again names must match).

  • Text not required by a page/question such as a header or hint text should be passed as an empty string

  • For conditionally shown questions, conditions that have been implemented and can be used are:

    • Radio question answer option selected matches one of conditional e.g. ["answer-options-1-string", "answer-option-3-string"]
    • Numeric question value matches condition e.g. [">2"], ["<7"] or ["== 6"]
  • When the top level question is a radio button and the conditional question is a numeric, text or date field then the conditional question is shown inline

  • When the conditional question is a radio, checkbox or select field it should be displayed on it's own page and "depends_on" should be used rather than "conditional_for"

    Page routing:

    • Form navigation works by stepping sequentially through every page defined in the JSON form definition for the given subsection. For every page it checks if it has "depends_on" conditions. If it does, it evaluates them to determine whether that page should be show or not.

    • In this way we can build up whole branches by having:

    "page_1": { "questions": { "question_1: "answer_options": ["A", "B"] } },
    "page_2": { "questions": { "question_2: "answer_options": ["C", "D"] }, "depends_on": [{ "question_1": "A" }] },
    "page_3": { "questions": { "question_3: "answer_options": ["E", "F"] }, "depends_on": [{ "question_1": "A" }] },
    "page_4": { "questions": { "question_4: "answer_options": ["G", "H"] }, "depends_on": [{ "question_1": "B" }] },
    

JSON form validation against Schema

To validate the form JSON against the schema you can run:
rake form_definition:validate["config/forms/2021_2022.json"]

n.b. You may have to escape square brackets in zsh
rake form_definition:validate\["config/forms/2021_2022.json"\]

This will validate the given form definition against the schema in config/forms/schema/generic.json.

You can also run:
rake form_definition:validate_all

This will validate all forms in directories = ["config/forms", "spec/fixtures/forms"]