Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Follow up questions #381

Merged
merged 18 commits into from
Feb 17, 2017
Merged

Follow up questions #381

merged 18 commits into from
Feb 17, 2017

Conversation

allait
Copy link
Contributor

@allait allait commented Feb 16, 2017

Split multiquestion schema generation

Adds helper methods to generate schema for flat (storing all properties
in a top-level object) and nested (storing properties in a nested object)
multiquestions.

Adds support for 'radios' question type to the followup schema
generation, and refactors it to use a merge_schemas helper to
make it easier to join multiple follow-up schemas.

Schema generation was using an older version of content-loader, which
didn't have DynamicList question class. Since DynamicList constructor
switches question type to 'multiquestion' when updating content-loader
we need to add additional dispatch logic to multiquestion so it can
call dynamic_list generator for DynamicList questions.

Don't add lead question to the oneOf subschema required

Always listing lead-in question as required in a subschema makes the
validation fail when used with enforce_required=False in the API.

This wasn't a problem for brief responses, since both the lead-in and
the followup questions are nested inside one key, which is missing in
unrelated updates. But when storing them as separate top-level key we
need to be able to unset the required fields using the schema required
list.

So instead of listing the lead-in question as required in the subschemas
we add it to the main schema required list.

Let multiquestions extend main schema fields

Allows property schema generators to return a schema addition to be
merged with the main schema. This enables multiquestions to add
allOf/oneOff subschemas for followup questions and modify the
required list on the main schema.

Changes the existing specialists anyOf/dependency additions to use
merge_schemas.

Splits dynamic_list from nestd_multiquestion output

Dynamic lists are serialized as an array, but other multiquestions
aren't, so it makes sense to move the array part of the schema to
the _dynamic_list function.

And since we can no longer dispatch to the dynamic_list property
based on the question type we need to remove it from the types
mapping.

README.md Outdated
@@ -71,8 +71,7 @@ Question keys
* `dynamic_field` defines the field in the filter context that will be used to generate dynamic list questions (eg when passing
a brief as filter context setting `dynamic_field` to `brief.niceToHaveRequirements` will generate questions for nice-to-have
requirements) (only valid for `dynamic_list` questions)
* `followup` sets the name of the question that will be required/shown if the current question is answered with "Yes" (only valid
for `boolean` questions)
* `followup` is a dictionary where each key is the name of the question that will be required/shown and the value for the key is a list of question answers that should display the follow up (only valid for `boolean` and `radios` questions)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Followups are required for checkboxes, too.

@@ -0,0 +1,16 @@
def merge_schemas(a, b):
if not (isinstance(a, dict) and isinstance(b, dict)):
raise ValueError("Error merging '{}' and '{}'".format(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be a TypeError, and the error message could probably be more descriptive: "Unable to merge disparate types: '{}' and '{}'."

@allait allait force-pushed the follow-up-questions branch 2 times, most recently from 1bdb2bb to 8863942 Compare February 16, 2017 15:30
@allait
Copy link
Contributor Author

allait commented Feb 16, 2017

@samuelhwilliams added support for checkbox question with follow-ups and optional follow-up questions. The tests need a new content-loader version to get merged, which I'm adding the checkbox support to now.

@allait allait force-pushed the follow-up-questions branch 2 times, most recently from a676cdc to 292f9d9 Compare February 16, 2017 16:34
@samuelhwilliams
Copy link
Contributor

Probably need to add some tests around the followup schema generation.

Adds helper methods to generate schema for flat (storing all properties
in a top-level object) and nested (storing properties in a nested object)
multiquestions.

Adds support for 'radios' question type to the followup schema
generation, and refactors it to use a `merge_schemas` helper to
make it easier to join multiple follow-up schemas.

Schema generation was using an older version of content-loader, which
didn't have DynamicList question class. Since DynamicList constructor
switches question type to 'multiquestion' when updating content-loader
we need to add additional dispatch logic to `multiquestion` so it can
call `dynamic_list` generator for `DynamicList` questions.
Getting a list of required fields for multiquestions is complicated
by the addition of followups and the fact that dynamic list / nested
multiquestions have to deal with two required fields lists: one for
the main schema and one for the nested object schema.

Allowing property schema functions to return an optional list of
required fields allows us to move that logic closer to the followup
subschema generation without having to make any changes to the
`.form_field` methods on the question classes, that could potentially
affect other parts of the application code.
Always listing lead-in question as required in a subschema makes the
validation fail when used with `enforce_required=False` in the API.

This wasn't a problem for brief responses, since both the lead-in and
the followup questions are nested inside one key, which is missing in
unrelated updates. But when storing them as separate top-level key we
need to be able to unset the required fields using the schema required
list.

So instead of listing the lead-in question as required in the subschemas
we add it to the main schema required list.
Allows property schema generators to return a schema addition to be
merged with the main schema. This enables multiquestions to add
allOf/oneOff subschemas for followup questions and modify the
required list on the main schema.

Changes the existing specialists anyOf/dependency additions to use
merge_schemas.
Dynamic lists are serialized as an array, but other multiquestions
aren't, so it makes sense to move the array part of the schema to
the `_dynamic_list` function.

And since we can no longer dispatch to the dynamic_list property
based on the question type we need to remove it from the types
mapping.
Fixes the schema accepting an answer for the follow up when the lead
in question wasn't answered.

Since the subschema without the follow-up doesn't list the lead-in
question in required list we can still use page_questions without
failing the oneOf.
Since checkboxes are answered with a list of strings some of which
can have a followup we need a way to check that the question answer
list contains a given value.

There's no direct way to do this using JSON schema, but we can check
whether the list of answers contains only the values that don't have
a follow-up. So using that and the inverse of that schema we can
separate answers that only have no-followup values and answers that
contain something else, which we assume IS the follow-up value we
wanted to check for (it could be some other invalid value, in which
case the schema will still require an answer for the followup but
the parent property schema will also flag the value as invalid).
This requires us to use `.get_question` on the multiquestion, but
has a nice side-effect of validating that follow-up question IDs
are correct and belong to the same multiquestion as the lead-in.
@allait allait force-pushed the follow-up-questions branch from 292f9d9 to 8d77435 Compare February 17, 2017 11:27
@allait
Copy link
Contributor Author

allait commented Feb 17, 2017

@samuelhwilliams fair point, I've added a few tests

@samuelhwilliams
Copy link
Contributor

Other than that, from local testing and reading through, looks good. 👍

@allait allait merged commit 8c00adc into master Feb 17, 2017
@allait allait deleted the follow-up-questions branch February 17, 2017 11:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants