<% if @case_log.status == "in_progress" && next_incomplete_section_redirect_path != "error" %>
<%= govuk_button_link_to "Save and go to next incomplete section", "/logs/#{@case_log.id}/#{next_incomplete_section_redirect_path}", secondary: true %>
<% if @lettings_log.status == "in_progress" && next_incomplete_section_redirect_path != "error" %>
<%= govuk_button_link_to "Save and go to next incomplete section", "/logs/#{@lettings_log.id}/#{next_incomplete_section_redirect_path}", secondary: true %>
You can review and make changes to this log until 2nd June <%= @case_log.collection_start_year.present? ? @case_log.collection_start_year + 1 : "" %>.
You can review and make changes to this log until 2nd June <%= @lettings_log.collection_start_year.present? ? @lettings_log.collection_start_year + 1 : "" %>.
</p>
<% @case_log.form.sections.map do |section| %>
<% @lettings_log.form.sections.map do |section| %>
<p class="govuk-body">This log has not been started.</p>
<% elsif @case_log.status == "completed" %>
<% elsif @lettings_log.status == "completed" %>
<p class="govuk-body">
<%= status_tag(@case_log.status) %>
<%= status_tag(@lettings_log.status) %>
</p>
<p class="govuk-body">
You can <%= govuk_link_to "review and make changes to this log", "/logs/#{@case_log.id}/review" %> until 2nd June <%= @case_log.collection_start_year.present? ? @case_log.collection_start_year + 1 : "" %>.
You can <%= govuk_link_to "review and make changes to this log", "/logs/#{@lettings_log.id}/review" %> until 2nd June <%= @lettings_log.collection_start_year.present? ? @lettings_log.collection_start_year + 1 : "" %>.
<%= govuk_button_to "Create a new lettings log for this organisation", case_logs_path(case_log: { owning_organisation_id: @organisation.id }, method: :post) %>
<%= govuk_button_to "Create a new lettings log for this organisation", lettings_logs_path(lettings_log: { owning_organisation_id: @organisation.id }, method: :post) %>
<p class="govuk-body">The data will be used to update the national record for social housing. It will also help to inform policy about the cost of social housing and what type of housing needs to be built.</p>
<p class="govuk-body">This service is only for social housing in England.</p>
Using Turbo Frames allows us to swap out just the question part of the page without needing full page refreshes as you go through the form and provides a "Single Page Application like" user experience. Each question still gets a unique URL that can be navigated to directly with the Case Log ID and the overall user experience is that form navigation feels faster.
Using Turbo Frames allows us to swap out just the question part of the page without needing full page refreshes as you go through the form and provides a "Single Page Application like" user experience. Each question still gets a unique URL that can be navigated to directly with the lettings log ID and the overall user experience is that form navigation feels faster.
## Impact on interrupted sessions
We currently have a single Active Record model for Case Logs that contains all the question fields. Every time a question is submitted the answer will be saved in the Active Record model instance before the next frame is rendered. This model will need to be able to handle partial records and partial validation anyway since not all API users will have all the required data. Validation can occur based on the data already saved and/or once the form is finally submitted. Front end validation will still happen additionally as you go through the form to help make sure users don’t get a long list of errors at the end. Using session data here and updating the model only once the form is completed would not seem to have any advantages over this approach.
We currently have a single Active Record model for lettings logs that contains all the question fields. Every time a question is submitted the answer will be saved in the Active Record model instance before the next frame is rendered. This model will need to be able to handle partial records and partial validation anyway since not all API users will have all the required data. Validation can occur based on the data already saved and/or once the form is finally submitted. Front end validation will still happen additionally as you go through the form to help make sure users don’t get a long list of errors at the end. Using session data here and updating the model only once the form is completed would not seem to have any advantages over this approach.
This means that when a user navigates away from the form or closes the tab etc, they can use the URL to navigate directly back to where they left off, or follow the form flow through again, and in both cases their submitted answers will still be there.
## Impact on API
The API will still expect to take a JSON describing the case log, instantiate the model with the given fields, and run validations as if it had been submitted.
The API will still expect to take a JSON describing the lettings log, instantiate the model with the given fields, and run validations as if it had been submitted.
@ -15,7 +15,7 @@ These are handled slightly differently:
These run for all submitted data. Every time a form page (in the UI) is submitted, the fields related to that form page will be checked to ensure that any responses given are valid. If they are not, an error message will be shown on screen, and it will not be possible to ‘Save and continue’ until the response is fixed or removed.
Similarly if an API request is made to create a case log with data that contains _invalid_ fields, that data will be rejected, and an error message will be returned.
Similarly if an API request is made to create a lettings log with data that contains _invalid_ fields, that data will be rejected, and an error message will be returned.
## Presence checks
@ -23,8 +23,8 @@ These are not strictly error checks since it’s possible to submit partial data
Similarly the API client (3rd party software system) may not have all the required data and may only be submitting a partial log. This is still a valid use case so we should not be enforcing presence checks and returning errors based on them for either submission type.
Instead we determine the _status_ of the case log based the presence checks. Every time data is submitted (via a form page, bulk upload or API), before saving the data, the system will check whether all fields have been completed _and_ pass validity checks. If so, the case log will be marked as _completed_, if not it will be marked as _in progress_.
Instead we determine the _status_ of the lettings log based the presence checks. Every time data is submitted (via a form page, bulk upload or API), before saving the data, the system will check whether all fields have been completed _and_ pass validity checks. If so, the lettings log will be marked as _completed_, if not it will be marked as _in progress_.
By default all fields that a Case Log has will be assumed to be required unless explicitly marked as not required (for example as a result of other answers rendering a question inapplicable).
By default all fields that a lettings log has will be assumed to be required unless explicitly marked as not required (for example as a result of other answers rendering a question inapplicable).
On the form UI this will work by not allowing you to submit the form, until all presence checks have been satisfied, but all other navigation is allowed. On the API this will work by returning a Case Log that is ‘in progress’ if you’ve submitted a partial log, or ‘completed’ if you’ve submitted a full log, or ‘errors’ if you’ve submitted an invalid log.
On the form UI this will work by not allowing you to submit the form, until all presence checks have been satisfied, but all other navigation is allowed. On the API this will work by returning a lettings log that is ‘in progress’ if you’ve submitted a partial log, or ‘completed’ if you’ve submitted a full log, or ‘errors’ if you’ve submitted an invalid log.
All data collected by the application needs to be exported to the Consolidated Data Store (CDS) which is a data warehouse based on MS SQL running in the DAP (Data Analytics Platform).
This is done via XML exports saved in an S3 bucket located in the DAP VPC using dedicated credentials shared out of band. The data mapping for this export can be found in `app/services/exports/case_log_export_service.rb`
This is done via XML exports saved in an S3 bucket located in the DAP VPC using dedicated credentials shared out of band. The data mapping for this export can be found in `app/services/exports/lettings_log_export_service.rb`
Initially the application database field names and field types were chosen to match the existing CDS data as closely as possible to minimise the amount of transformation needed. This has led to a less than optimal data model though and increasingly we should look to transform at the mapping layer where beneficial for our application.
@ -23,9 +23,9 @@ As a result it’s not modelled as part of the config but rather as code. It sti
- Conditional questions (`conditional_for`) – Radio and checkbox questions can support conditional text or numeric questions that show/hide on the same page when the triggering option is selected
- Routing (`depends_on`) – all pages can specify conditions (attributes of the case log) that determine whether or not they’re shown to the user
- Routing (`depends_on`) – all pages can specify conditions (attributes of the lettings log) that determine whether or not they’re shown to the user
- Methods can be chained (i.e. you can have conditions in the form `{ owning_organisation.provider_type: "local_authority"`) which will call `case_log.owning_organisation.provider_type` and compare the result to the provided value.
- Methods can be chained (i.e. you can have conditions in the form `{ owning_organisation.provider_type: "local_authority"`) which will call `lettings_log.owning_organisation.provider_type` and compare the result to the provided value.
- Numeric questions support math expression depends_on conditions such as `{ age2: ">16" }`
@ -108,7 +108,7 @@ Assumptions made by the format:
- 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).
- The ActiveRecord lettings 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
@ -87,4 +87,4 @@ In the above example the width is an optional attribute and can be provided for
The above example links to the first example as both of these questions would be on the same page. The `inferred_check_answers_value` is what should be displayed on the check answers page for this question if we infer it. If the value of `postcode_known` was given as `0` (which is a no), as seen in the condition part of `inferred_check_answers_value` then we can infer that the data inputter does not know the postcode and so we would display the value of `Not known` on the check answers page for the postcode.
In the above example the `inferred_answers` refers to a question where we can infer the answer based on the answer of this question. In this case the `la` question can be inferred from the postcode value given by the data inputter as we are able to lookup the local authority based on the postcode given. We then set a property on the case log `is_la_inferred` to true to indicate that this is an answer we've inferred.
In the above example the `inferred_answers` refers to a question where we can infer the answer based on the answer of this question. In this case the `la` question can be inferred from the postcode value given by the data inputter as we are able to lookup the local authority based on the postcode given. We then set a property on the lettings log `is_la_inferred` to true to indicate that this is an answer we've inferred.
Routes for each form page are generated by looping over each Page instance in each Form instance held by the form handler and defining a `GET` path. The corresponding controller method is also auto-generated with meta-programming via the same looping in `app/controllers/form_controller.rb`
All form pages submit to the same controller method (`app/controllers/form_controller.rb#submit_form`) which validates and persists the data, and then redirects to the next form page that identifies as `routed_to` given the current case log state.
All form pages submit to the same controller method (`app/controllers/form_controller.rb#submit_form`) which validates and persists the data, and then redirects to the next form page that identifies as `routed_to` given the current lettings log state.
@ -31,7 +31,7 @@ An example subsection might look something like this:
In the above example the the subsection has the id `property_information`. The `depends_on` contains the set of conditions that must be met for the section to be accessible to a data provider, in this example subsection depends on the completion of the setup section/subsection (note that this is a common condition as the answers provided to questions in the setup subsection often have an impact on what questions are asked of the data provider in later subsections of the form).
The label contains the text that users will see for that subsection in the task list page of a case log.
The label contains the text that users will see for that subsection in the task list page of a lettings log.
The pages of the subsection in the example would be `property_postcode` and `property_local_authority`.
@ -46,13 +46,13 @@ This is a useful analogy as a parent can have multiple children, and a child can
### User permissions within organisations
The case logs that a user can see depends on their role:
The lettings logs that a user can see depends on their role:
- Customer support users can access any case log
- Customer support users can access any lettings log
- Data coordinators can access any case log for which the organisation they work for is ultimately responsible for, meaning they can see logs managed by a child organisation
- Data coordinators can access any lettings log for which the organisation they work for is ultimately responsible for, meaning they can see logs managed by a child organisation
- Data providers can only access case logs for which their organisation manages (or directly owns)
- Data providers can only access lettings logs for which their organisation manages (or directly owns)
Taking the relationships from the above diagram, and looking at which logs each user can access:
On [GOV.UK PaaS](https://www.cloud.service.gov.uk/), service credentials are appended to the environment variable `VCAP_SERVICES` when services [are bound](https://docs.cloud.service.gov.uk/deploying_services/s3/#bind-an-aws-s3-bucket-to-your-app) to an application.
Such services include datastores and S3 buckets.
Our application uses S3 and Redis clients and supports two different ways of parsing their configuration:
* Via the environment variable `VCAP_SERVICES` using the `PaasConfigurationService` class
* Via the environment variables `S3_CONFIG` and `REDIS_CONFIG` using the `EnvConfigurationService` class
`S3_CONFIG` and `REDIS_CONFIG` are populated using a similar structure than `VCAP_SERVICES`:
S3_CONFIG:
```json
[
{
"instance_name": "bucket_1",
"credentials": {
"aws_access_key_id": "123",
"aws_secret_access_key": "456",
"aws_region": "eu-west-1",
"bucket_name": "my-bucket"
}
}
]
```
REDIS_CONFIG:
```json
[
{
"instance_name": "redis_1",
"credentials": {
"uri": "redis_uri"
}
}
]
```
In order to switch from using [GOV.UK PaaS](https://www.cloud.service.gov.uk/) provided services to external ones, instances of `PaasConfigurationService` need to be replaced by `EnvConfigurationService`.
This assumes that `S3_CONFIG` or/and `REDIS_CONFIG` are available.
Please check `full_import.rake` and `rack_attack.rb` for examples of how the configuration is used.
## Deployment
This application is running on [GOV.UK PaaS](https://www.cloud.service.gov.uk/). To deploy you need to: