ccs-digitalmarketplace-frameworks 3.0.2 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/frameworks/digital-outcomes-and-specialists-7/manifests/edit_service.yml +11 -6
- package/frameworks/digital-outcomes-and-specialists-7/manifests/edit_service_as_admin.yml +11 -0
- package/frameworks/digital-outcomes-and-specialists-7/manifests/edit_submission.yml +11 -6
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/itOperationsRolesItServiceManagerHeadDayRate.yml +1 -1
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/itOperationsRolesItServiceManagerHeadDayRateSpecialist.yml +1 -1
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/itOperationsRolesItServiceManagerHeadLocations.yml +2 -2
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/itOperationsRolesItServiceManagerHeadUkDayRate.yml +1 -1
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/itOperationsRolesItServiceManagerSeniorDayRate.yml +1 -1
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/itOperationsRolesItServiceManagerSeniorDayRateSpecialist.yml +1 -1
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/itOperationsRolesItServiceManagerSeniorLocations.yml +2 -2
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/itOperationsRolesItServiceManagerSeniorUkDayRate.yml +1 -1
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/userResearchParticipantsEssentialsManage.yml +22 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/userResearchParticipantsEssentialsRecruit.yml +22 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/whatUserResearchServices.yml +26 -0
- package/package.json +1 -1
- package/requirements-dev.txt +1 -1
- package/requirements.in +1 -1
- package/requirements.txt +1 -1
- package/schema_generator/validation.py +83 -15
- package/schemas/questions.json +17 -0
- package/tests/test_content.py +30 -0
- package/tests/test_generate_validation_schema.py +86 -2
package/README.md
CHANGED
|
@@ -85,7 +85,9 @@ Question keys
|
|
|
85
85
|
* `dynamic_field` defines the field in the filter context that will be used to generate dynamic list questions (eg when passing
|
|
86
86
|
a brief as filter context setting `dynamic_field` to `brief.niceToHaveRequirements` will generate questions for nice-to-have
|
|
87
87
|
requirements) (only valid for `dynamic_list` questions)
|
|
88
|
-
* `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`, `radios` and `checkboxes` questions)
|
|
88
|
+
* `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`, `radios` and `checkboxes` questions). **Important** These questions appear on the same page as the parent question
|
|
89
|
+
* `dependent_follow_up` 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`, `radios` and `checkboxes` questions). **Important** These questions appear in a different section to the parent question and need a corresponding `depends_on_follow_up` to link the questions
|
|
90
|
+
* `depends_on_follow_up` is a dictionary where each key is the name of the question this question depends on and the value is a list of values that will determine if it appears. **Important** These belong to a parent question and need a corresponding `dependent_followup` to link the questions
|
|
89
91
|
|
|
90
92
|
|
|
91
93
|
Manifest files and section keys
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
-
|
|
2
|
-
name: Research studio name
|
|
3
|
-
editable: True
|
|
4
|
-
questions:
|
|
5
|
-
- serviceName
|
|
6
|
-
|
|
7
1
|
- name: Service essentials
|
|
8
2
|
editable: False
|
|
9
3
|
questions:
|
|
@@ -206,3 +200,14 @@
|
|
|
206
200
|
editable: False
|
|
207
201
|
questions:
|
|
208
202
|
- outcomesLocations
|
|
203
|
+
|
|
204
|
+
- name: User research services
|
|
205
|
+
editable: False
|
|
206
|
+
questions:
|
|
207
|
+
- whatUserResearchServices
|
|
208
|
+
|
|
209
|
+
- name: User research participants essentials
|
|
210
|
+
editable: False
|
|
211
|
+
questions:
|
|
212
|
+
- userResearchParticipantsEssentialsRecruit
|
|
213
|
+
- userResearchParticipantsEssentialsManage
|
|
@@ -231,3 +231,14 @@
|
|
|
231
231
|
editable: True
|
|
232
232
|
questions:
|
|
233
233
|
- outcomesLocations
|
|
234
|
+
|
|
235
|
+
- name: User research services
|
|
236
|
+
editable: True
|
|
237
|
+
questions:
|
|
238
|
+
- whatUserResearchServices
|
|
239
|
+
|
|
240
|
+
- name: User research participants essentials
|
|
241
|
+
editable: True
|
|
242
|
+
questions:
|
|
243
|
+
- userResearchParticipantsEssentialsRecruit
|
|
244
|
+
- userResearchParticipantsEssentialsManage
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
-
|
|
2
|
-
name: Research studio name
|
|
3
|
-
editable: True
|
|
4
|
-
questions:
|
|
5
|
-
- serviceName
|
|
6
|
-
|
|
7
1
|
- name: Service essentials
|
|
8
2
|
editable: True
|
|
9
3
|
description: >
|
|
@@ -271,3 +265,14 @@
|
|
|
271
265
|
editable: True
|
|
272
266
|
questions:
|
|
273
267
|
- outcomesLocations
|
|
268
|
+
|
|
269
|
+
- name: User research services
|
|
270
|
+
editable: True
|
|
271
|
+
questions:
|
|
272
|
+
- whatUserResearchServices
|
|
273
|
+
|
|
274
|
+
- name: User research participants essentials
|
|
275
|
+
editable: True
|
|
276
|
+
questions:
|
|
277
|
+
- userResearchParticipantsEssentialsRecruit
|
|
278
|
+
- userResearchParticipantsEssentialsManage
|
|
@@ -20,7 +20,7 @@ digit_limits:
|
|
|
20
20
|
validations:
|
|
21
21
|
- name: answer_required
|
|
22
22
|
field: itOperationsRolesItServiceManagerHeadPriceMax
|
|
23
|
-
message: Enter the amount you will charge for a head of
|
|
23
|
+
message: Enter the amount you will charge for a head of IT service management
|
|
24
24
|
- name: not_money_format
|
|
25
25
|
field: itOperationsRolesItServiceManagerHeadPriceMax
|
|
26
26
|
message: Maximum price must be a number less than 10,000, without units, like 99.95
|
|
@@ -20,7 +20,7 @@ digit_limits:
|
|
|
20
20
|
validations:
|
|
21
21
|
- name: answer_required
|
|
22
22
|
field: itOperationsRolesItServiceManagerHeadPriceMax
|
|
23
|
-
message: Enter the amount you will charge for a head of
|
|
23
|
+
message: Enter the amount you will charge for a head of IT service management
|
|
24
24
|
- name: not_money_format
|
|
25
25
|
field: itOperationsRolesItServiceManagerHeadPriceMax
|
|
26
26
|
message: Maximum price must be a number less than 10,000, without units, like 99.95
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
name: Where will your head of
|
|
1
|
+
name: Where will your head of IT service management work?
|
|
2
2
|
question: |
|
|
3
|
-
<p class="govuk-!-margin-bottom-2"><span class="govuk-fieldset__legend--s">Where will your head of
|
|
3
|
+
<p class="govuk-!-margin-bottom-2"><span class="govuk-fieldset__legend--s">Where will your head of IT service management work?</span></p>
|
|
4
4
|
<p class="govuk-!-margin-bottom-0"><span class="govuk-fieldset__legend--s">Choose all the locations where the specialist can work at buyer’s sites, and whether they can work offsite.</span></p>
|
|
5
5
|
hint: |
|
|
6
6
|
‘Offsite’ means the supplier will not be working at the buyer’s own sites.
|
|
@@ -20,7 +20,7 @@ digit_limits:
|
|
|
20
20
|
validations:
|
|
21
21
|
- name: answer_required
|
|
22
22
|
field: itOperationsRolesItServiceManagerHeadUkPriceMax
|
|
23
|
-
message: Enter the amount you will charge for a head of
|
|
23
|
+
message: Enter the amount you will charge for a head of IT service management
|
|
24
24
|
- name: not_money_format
|
|
25
25
|
field: itOperationsRolesItServiceManagerHeadUkPriceMax
|
|
26
26
|
message: Maximum price must be a number less than 10,000, without units, like 99.95
|
|
@@ -20,7 +20,7 @@ digit_limits:
|
|
|
20
20
|
validations:
|
|
21
21
|
- name: answer_required
|
|
22
22
|
field: itOperationsRolesItServiceManagerSeniorPriceMax
|
|
23
|
-
message: Enter the amount you will charge for a senior
|
|
23
|
+
message: Enter the amount you will charge for a senior IT service manager
|
|
24
24
|
- name: not_money_format
|
|
25
25
|
field: itOperationsRolesItServiceManagerSeniorPriceMax
|
|
26
26
|
message: Maximum price must be a number less than 10,000, without units, like 99.95
|
|
@@ -20,7 +20,7 @@ digit_limits:
|
|
|
20
20
|
validations:
|
|
21
21
|
- name: answer_required
|
|
22
22
|
field: itOperationsRolesItServiceManagerSeniorPriceMax
|
|
23
|
-
message: Enter the amount you will charge for a senior
|
|
23
|
+
message: Enter the amount you will charge for a senior IT service manager
|
|
24
24
|
- name: not_money_format
|
|
25
25
|
field: itOperationsRolesItServiceManagerSeniorPriceMax
|
|
26
26
|
message: Maximum price must be a number less than 10,000, without units, like 99.95
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
name: Where will your senior
|
|
1
|
+
name: Where will your senior IT service manager work?
|
|
2
2
|
question: |
|
|
3
|
-
<p class="govuk-!-margin-bottom-2"><span class="govuk-fieldset__legend--s">Where will your senior
|
|
3
|
+
<p class="govuk-!-margin-bottom-2"><span class="govuk-fieldset__legend--s">Where will your senior IT service manager work?</span></p>
|
|
4
4
|
<p class="govuk-!-margin-bottom-0"><span class="govuk-fieldset__legend--s">Choose all the locations where the specialist can work at buyer’s sites, and whether they can work offsite.</span></p>
|
|
5
5
|
hint: |
|
|
6
6
|
‘Offsite’ means the supplier will not be working at the buyer’s own sites.
|
|
@@ -20,7 +20,7 @@ digit_limits:
|
|
|
20
20
|
validations:
|
|
21
21
|
- name: answer_required
|
|
22
22
|
field: itOperationsRolesItServiceManagerSeniorUkPriceMax
|
|
23
|
-
message: Enter the amount you will charge for a senior
|
|
23
|
+
message: Enter the amount you will charge for a senior IT service manager
|
|
24
24
|
- name: not_money_format
|
|
25
25
|
field: itOperationsRolesItServiceManagerSeniorUkPriceMax
|
|
26
26
|
message: Maximum price must be a number less than 10,000, without units, like 99.95
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Manage incentives to user research participants
|
|
2
|
+
question: Do you agree to manage any incentives to user research participants?
|
|
3
|
+
|
|
4
|
+
depends:
|
|
5
|
+
- "on": lot
|
|
6
|
+
being:
|
|
7
|
+
- user-research-studios-and-participants
|
|
8
|
+
|
|
9
|
+
depends_on_follow_up:
|
|
10
|
+
whatUserResearchServices:
|
|
11
|
+
- user-research-participants
|
|
12
|
+
|
|
13
|
+
type: checkboxes
|
|
14
|
+
|
|
15
|
+
options:
|
|
16
|
+
- label: "Yes"
|
|
17
|
+
value: "yes"
|
|
18
|
+
|
|
19
|
+
validations:
|
|
20
|
+
-
|
|
21
|
+
name: answer_required
|
|
22
|
+
message: Select if you agree to manage any incentives to user research participants
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Recruit participants without revealing details
|
|
2
|
+
question: Can you, if asked, recruit participants without revealing details of the organisation you’re recruiting for?
|
|
3
|
+
|
|
4
|
+
depends:
|
|
5
|
+
- "on": lot
|
|
6
|
+
being:
|
|
7
|
+
- user-research-studios-and-participants
|
|
8
|
+
|
|
9
|
+
depends_on_follow_up:
|
|
10
|
+
whatUserResearchServices:
|
|
11
|
+
- user-research-participants
|
|
12
|
+
|
|
13
|
+
type: checkboxes
|
|
14
|
+
|
|
15
|
+
options:
|
|
16
|
+
- label: "Yes"
|
|
17
|
+
value: "yes"
|
|
18
|
+
|
|
19
|
+
validations:
|
|
20
|
+
-
|
|
21
|
+
name: answer_required
|
|
22
|
+
message: Select if you can recruit participants without revealing details of the organisation you’re recruiting for
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: What services can you provide?
|
|
2
|
+
question: What services can you provide?
|
|
3
|
+
|
|
4
|
+
depends:
|
|
5
|
+
- "on": lot
|
|
6
|
+
being:
|
|
7
|
+
- user-research-studios-and-participants
|
|
8
|
+
|
|
9
|
+
type: checkboxes
|
|
10
|
+
|
|
11
|
+
options:
|
|
12
|
+
- label: "User research studios"
|
|
13
|
+
value: "user-research-studios"
|
|
14
|
+
- label: "User research participants"
|
|
15
|
+
value: "user-research-participants"
|
|
16
|
+
|
|
17
|
+
dependent_follow_up:
|
|
18
|
+
userResearchParticipantsEssentialsRecruit:
|
|
19
|
+
- user-research-participants
|
|
20
|
+
userResearchParticipantsEssentialsManage:
|
|
21
|
+
- user-research-participants
|
|
22
|
+
|
|
23
|
+
validations:
|
|
24
|
+
-
|
|
25
|
+
name: answer_required
|
|
26
|
+
message: You must select the services you can provide
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccs-digitalmarketplace-frameworks",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Data files for Digital Marketplace’s procurement frameworks",
|
|
5
5
|
"repository": "git@github.com:Crown-Commercial-Service/ccs-digitalmarketplace-frameworks",
|
|
6
6
|
"author": "enquiries@digitalmarketplace.service.gov.uk"
|
package/requirements-dev.txt
CHANGED
|
@@ -34,7 +34,7 @@ ccs-digitalmarketplace-apiclient==25.11.0
|
|
|
34
34
|
# via
|
|
35
35
|
# -r requirements.txt
|
|
36
36
|
# ccs-digitalmarketplace-utils
|
|
37
|
-
ccs-digitalmarketplace-content-loader==11.
|
|
37
|
+
ccs-digitalmarketplace-content-loader==11.7.0
|
|
38
38
|
# via -r requirements.txt
|
|
39
39
|
ccs-digitalmarketplace-utils==68.0.0
|
|
40
40
|
# via
|
package/requirements.in
CHANGED
package/requirements.txt
CHANGED
|
@@ -18,7 +18,7 @@ cachelib==0.13.0
|
|
|
18
18
|
# via flask-session
|
|
19
19
|
ccs-digitalmarketplace-apiclient==25.11.0
|
|
20
20
|
# via ccs-digitalmarketplace-utils
|
|
21
|
-
ccs-digitalmarketplace-content-loader==11.
|
|
21
|
+
ccs-digitalmarketplace-content-loader==11.7.0
|
|
22
22
|
# via -r requirements.in
|
|
23
23
|
ccs-digitalmarketplace-utils==68.0.0
|
|
24
24
|
# via
|
|
@@ -348,22 +348,41 @@ def uri_property(question):
|
|
|
348
348
|
}}
|
|
349
349
|
|
|
350
350
|
|
|
351
|
+
def has_dependent_follow_up(property_func):
|
|
352
|
+
"""
|
|
353
|
+
Adds the additional dependent follow up schema to the property
|
|
354
|
+
"""
|
|
355
|
+
def inner_func(question):
|
|
356
|
+
schema = property_func(question)
|
|
357
|
+
schema_addition = {}
|
|
358
|
+
|
|
359
|
+
if question.get('dependent_follow_up'):
|
|
360
|
+
schema_addition = _dependent_follow_up(question)
|
|
361
|
+
|
|
362
|
+
return schema, schema_addition
|
|
363
|
+
|
|
364
|
+
return inner_func
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
@has_dependent_follow_up
|
|
351
368
|
def checkbox_property(question):
|
|
352
369
|
"""
|
|
353
370
|
Convert a checkbox question into JSON Schema.
|
|
354
371
|
"""
|
|
355
|
-
return {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
"
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
372
|
+
return {
|
|
373
|
+
question['id']: {
|
|
374
|
+
"type": "array",
|
|
375
|
+
"uniqueItems": True,
|
|
376
|
+
"minItems": 0 if question.get('optional') else 1,
|
|
377
|
+
"maxItems": question.get('number_of_items', len(question['options'])),
|
|
378
|
+
"items": {
|
|
379
|
+
"enum": [
|
|
380
|
+
option.get('value', option['label'])
|
|
381
|
+
for option in question['options']
|
|
382
|
+
]
|
|
383
|
+
}
|
|
365
384
|
}
|
|
366
|
-
}
|
|
385
|
+
}
|
|
367
386
|
|
|
368
387
|
|
|
369
388
|
def checkbox_tree_property(question):
|
|
@@ -625,6 +644,45 @@ def _followup(question, root):
|
|
|
625
644
|
return {'allOf': schemas}
|
|
626
645
|
|
|
627
646
|
|
|
647
|
+
def _dependent_follow_up(question):
|
|
648
|
+
schemas = []
|
|
649
|
+
question_values = [
|
|
650
|
+
option.get('value', option['label'])
|
|
651
|
+
for option in question.options
|
|
652
|
+
]
|
|
653
|
+
for dependent_follow_up_field, values in question["dependent_follow_up"].items():
|
|
654
|
+
non_dependent_question_values = [
|
|
655
|
+
value
|
|
656
|
+
for value in question_values
|
|
657
|
+
if value not in values
|
|
658
|
+
]
|
|
659
|
+
should_not_have_followup = {
|
|
660
|
+
"items": {"enum": non_dependent_question_values}
|
|
661
|
+
}
|
|
662
|
+
should_have_followup = {
|
|
663
|
+
"not": {"items": {"enum": non_dependent_question_values}}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
schemas.append({
|
|
667
|
+
'oneOf': [
|
|
668
|
+
{
|
|
669
|
+
"properties": {
|
|
670
|
+
question['id']: should_not_have_followup,
|
|
671
|
+
dependent_follow_up_field: {"type": "null"}
|
|
672
|
+
},
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
"properties": {
|
|
676
|
+
question['id']: should_have_followup,
|
|
677
|
+
},
|
|
678
|
+
"required": [dependent_follow_up_field]
|
|
679
|
+
},
|
|
680
|
+
]
|
|
681
|
+
})
|
|
682
|
+
|
|
683
|
+
return {'allOf': schemas}
|
|
684
|
+
|
|
685
|
+
|
|
628
686
|
def _flat_multiquestion(question):
|
|
629
687
|
properties = {}
|
|
630
688
|
for nested_question in question.questions:
|
|
@@ -825,8 +883,13 @@ def add_assurance(value_schema, assurance_approach):
|
|
|
825
883
|
def build_question_properties(question):
|
|
826
884
|
question_data = QUESTION_TYPES[question['type']](question)
|
|
827
885
|
if question.get('assuranceApproach'):
|
|
828
|
-
|
|
829
|
-
|
|
886
|
+
if isinstance(question_data, tuple):
|
|
887
|
+
for key, value_schema in question_data[0].items():
|
|
888
|
+
question_data[0][key] = add_assurance(value_schema, question['assuranceApproach'])
|
|
889
|
+
else:
|
|
890
|
+
for key, value_schema in question_data.items():
|
|
891
|
+
question_data[key] = add_assurance(value_schema, question['assuranceApproach'])
|
|
892
|
+
|
|
830
893
|
return question_data
|
|
831
894
|
|
|
832
895
|
|
|
@@ -842,8 +905,13 @@ def build_schema_properties(schema, questions):
|
|
|
842
905
|
property_schema = build_question_properties(question)
|
|
843
906
|
if isinstance(property_schema, tuple):
|
|
844
907
|
property_schema, schema_addition = property_schema
|
|
845
|
-
|
|
846
|
-
|
|
908
|
+
|
|
909
|
+
if schema_addition:
|
|
910
|
+
schema['properties'].update(property_schema)
|
|
911
|
+
schema = merge_schemas(schema, schema_addition)
|
|
912
|
+
else:
|
|
913
|
+
schema['properties'].update(property_schema)
|
|
914
|
+
schema['required'].extend(question.required_form_fields)
|
|
847
915
|
else:
|
|
848
916
|
schema['properties'].update(property_schema)
|
|
849
917
|
schema['required'].extend(question.required_form_fields)
|
package/schemas/questions.json
CHANGED
|
@@ -48,6 +48,11 @@
|
|
|
48
48
|
"properties": {
|
|
49
49
|
"type": {"enum": ["boolean", "radios", "checkboxes"]}
|
|
50
50
|
}
|
|
51
|
+
},
|
|
52
|
+
"dependent_follow_up": {
|
|
53
|
+
"properties": {
|
|
54
|
+
"type": {"enum": ["checkboxes"]}
|
|
55
|
+
}
|
|
51
56
|
}
|
|
52
57
|
},
|
|
53
58
|
"properties": {
|
|
@@ -321,6 +326,18 @@
|
|
|
321
326
|
"^[a-zA-Z0-9]+$": {"type": "array"}
|
|
322
327
|
}
|
|
323
328
|
},
|
|
329
|
+
"dependent_follow_up": {
|
|
330
|
+
"type": "object",
|
|
331
|
+
"patternProperties": {
|
|
332
|
+
"^[a-zA-Z0-9]+$": {"type": "array"}
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
"depends_on_follow_up": {
|
|
336
|
+
"type": "object",
|
|
337
|
+
"patternProperties": {
|
|
338
|
+
"^[a-zA-Z0-9]+$": {"type": "array"}
|
|
339
|
+
}
|
|
340
|
+
},
|
|
324
341
|
"dynamic_field": {
|
|
325
342
|
"type": "string"
|
|
326
343
|
},
|
package/tests/test_content.py
CHANGED
|
@@ -6,6 +6,7 @@ import pytest
|
|
|
6
6
|
|
|
7
7
|
from dmcontent import ContentLoader
|
|
8
8
|
from dmcontent.utils import TemplateField
|
|
9
|
+
from dmcontent.questions import Multiquestion
|
|
9
10
|
|
|
10
11
|
content = ContentLoader('./')
|
|
11
12
|
|
|
@@ -130,6 +131,35 @@ def test_render_question(framework, question_id, question_set, question):
|
|
|
130
131
|
)
|
|
131
132
|
|
|
132
133
|
|
|
134
|
+
@pytest.mark.parametrize("framework, question_set, manifest", get_manifests())
|
|
135
|
+
def test_dependent_follow_ups(framework, question_set, manifest):
|
|
136
|
+
content.load_manifest(framework, question_set, manifest)
|
|
137
|
+
loaded_content = content.get_manifest(framework, manifest)
|
|
138
|
+
|
|
139
|
+
questions_with_dependent_follow_up = {}
|
|
140
|
+
expected_questions_with_dependent_follow_up = {}
|
|
141
|
+
|
|
142
|
+
for section in loaded_content.sections:
|
|
143
|
+
for section_question in section.questions:
|
|
144
|
+
if isinstance(section_question, Multiquestion):
|
|
145
|
+
questions = section_question.get_questions_flat()
|
|
146
|
+
else:
|
|
147
|
+
questions = [section_question]
|
|
148
|
+
|
|
149
|
+
for question in questions:
|
|
150
|
+
if question.get("dependent_follow_up"):
|
|
151
|
+
questions_with_dependent_follow_up[question.id] = question.get("dependent_follow_up")
|
|
152
|
+
|
|
153
|
+
for question_id, question_values in question.get("depends_on_follow_up", {}).items():
|
|
154
|
+
if question_id not in expected_questions_with_dependent_follow_up:
|
|
155
|
+
expected_questions_with_dependent_follow_up[question_id] = {}
|
|
156
|
+
|
|
157
|
+
for field in question.form_fields:
|
|
158
|
+
expected_questions_with_dependent_follow_up[question_id][field] = question_values
|
|
159
|
+
|
|
160
|
+
assert questions_with_dependent_follow_up == expected_questions_with_dependent_follow_up
|
|
161
|
+
|
|
162
|
+
|
|
133
163
|
def strip_paragraph_wrapper(value):
|
|
134
164
|
match = re.fullmatch(r"^(<p.*?>)(.+?)(</p>)", value)
|
|
135
165
|
if match:
|
|
@@ -274,12 +274,14 @@ def test_checkbox_property(id, options):
|
|
|
274
274
|
"id": id,
|
|
275
275
|
"options": options
|
|
276
276
|
}
|
|
277
|
-
result = checkbox_property(question)
|
|
277
|
+
result, additional_schema = checkbox_property(question)
|
|
278
278
|
assert result[id]['minItems'] == 1
|
|
279
|
+
assert additional_schema == {}
|
|
279
280
|
|
|
280
281
|
question['optional'] = True
|
|
281
|
-
result = checkbox_property(question)
|
|
282
|
+
result, additional_schema = checkbox_property(question)
|
|
282
283
|
assert result[id]['minItems'] == 0
|
|
284
|
+
assert additional_schema == {}
|
|
283
285
|
|
|
284
286
|
enum = result[id]['items']['enum']
|
|
285
287
|
assert len(enum) == len(options)
|
|
@@ -289,6 +291,88 @@ def test_checkbox_property(id, options):
|
|
|
289
291
|
for option in options if 'value' not in option)
|
|
290
292
|
|
|
291
293
|
|
|
294
|
+
def test_checkbox_property_with_dependent_follow_up():
|
|
295
|
+
options = [
|
|
296
|
+
{"value": "1", "label": "One"},
|
|
297
|
+
{"label": "Two"},
|
|
298
|
+
{"value": "3", "label": "Three"}
|
|
299
|
+
]
|
|
300
|
+
|
|
301
|
+
question = ContentQuestion({
|
|
302
|
+
"id": "q1",
|
|
303
|
+
"options": options,
|
|
304
|
+
"dependent_follow_up": {
|
|
305
|
+
"a": [
|
|
306
|
+
"1",
|
|
307
|
+
"Two"
|
|
308
|
+
],
|
|
309
|
+
"b": [
|
|
310
|
+
"Two",
|
|
311
|
+
],
|
|
312
|
+
"c": [
|
|
313
|
+
"1",
|
|
314
|
+
"3"
|
|
315
|
+
]
|
|
316
|
+
}
|
|
317
|
+
})
|
|
318
|
+
result, additional_schema = checkbox_property(question)
|
|
319
|
+
assert result['q1']['minItems'] == 1
|
|
320
|
+
|
|
321
|
+
enum = result['q1']['items']['enum']
|
|
322
|
+
assert len(enum) == len(options)
|
|
323
|
+
assert all(option['value'] in enum
|
|
324
|
+
for option in options if 'value' in option)
|
|
325
|
+
assert all(option['label'] in enum
|
|
326
|
+
for option in options if 'value' not in option)
|
|
327
|
+
|
|
328
|
+
assert additional_schema == {
|
|
329
|
+
"allOf": [
|
|
330
|
+
{
|
|
331
|
+
"oneOf": [
|
|
332
|
+
{
|
|
333
|
+
"properties": {
|
|
334
|
+
"q1": {"items": {"enum": ["3"]}},
|
|
335
|
+
"a": {"type": "null"},
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
"properties": {"q1": {"not": {"items": {"enum": ["3"]}}}},
|
|
340
|
+
"required": ["a"],
|
|
341
|
+
},
|
|
342
|
+
]
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
"oneOf": [
|
|
346
|
+
{
|
|
347
|
+
"properties": {
|
|
348
|
+
"q1": {"items": {"enum": ["1", "3"]}},
|
|
349
|
+
"b": {"type": "null"},
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
"properties": {"q1": {"not": {"items": {"enum": ["1", "3"]}}}},
|
|
354
|
+
"required": ["b"],
|
|
355
|
+
},
|
|
356
|
+
]
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
"oneOf": [
|
|
360
|
+
{
|
|
361
|
+
"properties": {
|
|
362
|
+
"q1": {"items": {"enum": ["Two"]}},
|
|
363
|
+
"c": {"type": "null"},
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
"properties": {"q1": {"not": {"items": {"enum": ["Two"]}}}},
|
|
368
|
+
"required": ["c"],
|
|
369
|
+
},
|
|
370
|
+
]
|
|
371
|
+
},
|
|
372
|
+
]
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
|
|
292
376
|
@settings(suppress_health_check=[HealthCheck.too_slow])
|
|
293
377
|
@given(st.text(), nested_checkboxes_list(), st.integers(min_value=1))
|
|
294
378
|
def test_checkbox_tree_property(id, options, number_of_items):
|