ccs-digitalmarketplace-frameworks 3.0.3 → 3.2.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 +4 -1
- package/frameworks/digital-outcomes-and-specialists-7/manifests/edit_service.yml +40 -6
- package/frameworks/digital-outcomes-and-specialists-7/manifests/edit_service_as_admin.yml +43 -6
- package/frameworks/digital-outcomes-and-specialists-7/manifests/edit_submission.yml +47 -6
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefDataOfficerBasicDayRate.yml +26 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefDataOfficerBasicDayRateSpecialist.yml +26 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefDataOfficerBasicLocations.yml +32 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefDataOfficerBasicOffshoreDayRate.yml +27 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefDataOfficerBasicQuantity.yml +22 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefDataOfficerBasicSecurityClearance.yml +23 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefDataOfficerBasicUkDayRate.yml +26 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefDataOfficerRoles.yml +19 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefInformationSecurityOfficerBasicDayRate.yml +26 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefInformationSecurityOfficerBasicDayRateSpecialist.yml +26 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefInformationSecurityOfficerBasicLocations.yml +32 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefInformationSecurityOfficerBasicOffshoreDayRate.yml +27 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefInformationSecurityOfficerBasicQuantity.yml +22 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefInformationSecurityOfficerBasicSecurityClearance.yml +23 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefInformationSecurityOfficerBasicUkDayRate.yml +26 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefInformationSecurityOfficerRoles.yml +19 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefTechnologyOfficerBasicDayRate.yml +26 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefTechnologyOfficerBasicDayRateSpecialist.yml +26 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefTechnologyOfficerBasicLocations.yml +32 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefTechnologyOfficerBasicOffshoreDayRate.yml +27 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefTechnologyOfficerBasicQuantity.yml +22 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefTechnologyOfficerBasicSecurityClearance.yml +23 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefTechnologyOfficerBasicUkDayRate.yml +26 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/chiefDigitalAndDataRolesChiefTechnologyOfficerRoles.yml +19 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/complyWithMrsCodeOfConduct.yml +19 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/icoRegistered.yml +21 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/icoRegistrationReference.yml +16 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/mrsMember.yml +18 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/multiqChiefDigitalAndDataRolesChiefDataOfficer.yml +19 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/multiqChiefDigitalAndDataRolesChiefDataOfficerBasic.yml +20 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/multiqChiefDigitalAndDataRolesChiefDataOfficerBasicDCDP.yml +20 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/multiqChiefDigitalAndDataRolesChiefDataOfficerDCDP.yml +14 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/multiqChiefDigitalAndDataRolesChiefInformationSecurityOfficer.yml +19 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/multiqChiefDigitalAndDataRolesChiefInformationSecurityOfficerBasic.yml +20 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/multiqChiefDigitalAndDataRolesChiefInformationSecurityOfficerBasicDCDP.yml +20 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/multiqChiefDigitalAndDataRolesChiefInformationSecurityOfficerDCDP.yml +14 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/multiqChiefDigitalAndDataRolesChiefTechnologyOfficer.yml +19 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/multiqChiefDigitalAndDataRolesChiefTechnologyOfficerBasic.yml +20 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/multiqChiefDigitalAndDataRolesChiefTechnologyOfficerBasicDCDP.yml +20 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/multiqChiefDigitalAndDataRolesChiefTechnologyOfficerDCDP.yml +14 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/multiqIcoRegistered.yml +17 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/multiqOtherTradeOrIndustryBodies.yml +17 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/otherTradeOrIndustryBodies.yml +29 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/otherTradeOrIndustryBodiesOther.yml +18 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/rasAccredited.yml +18 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/useRasAccreditedSubcontractors.yml +18 -0
- 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/whatTypesOfUserResearchStudio.yml +24 -0
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/whatUserResearchServices.yml +46 -0
- package/package.json +1 -1
- package/requirements-dev.txt +2 -2
- package/requirements.in +1 -1
- package/requirements.txt +1 -1
- package/schema_generator/validation.py +89 -17
- package/schemas/questions.json +23 -0
- package/tests/test_content.py +43 -0
- package/tests/test_generate_validation_schema.py +86 -2
- package/assets/style.css +0 -186
- package/assets/tests_test_generate_validation_schema.py__test_boolean_list_property_0_0.txt +0 -11
- package/assets/tests_test_generate_validation_schema.py__test_boolean_property_with_required_value_0_0.txt +0 -11
- package/assets/tests_test_generate_validation_schema.py__test_checkbox_property_0_0.txt +0 -14
- package/assets/tests_test_generate_validation_schema.py__test_checkbox_tree_property_0_0.txt +0 -13
- package/assets/tests_test_generate_validation_schema.py__test_list_property_0_0.txt +0 -11
- package/assets/tests_test_generate_validation_schema.py__test_number_property_0_0.txt +0 -11
- package/assets/tests_test_generate_validation_schema.py__test_number_property_limits_0_0.txt +0 -11
- package/assets/tests_test_generate_validation_schema.py__test_optional_text_property_0_0.txt +0 -11
- package/assets/tests_test_generate_validation_schema.py__test_parse_question_limits_char_count_0_0.txt +0 -11
- package/assets/tests_test_generate_validation_schema.py__test_parse_question_limits_word_count_0_0.txt +0 -11
- package/assets/tests_test_generate_validation_schema.py__test_parse_question_limits_word_count_optional_0_0.txt +0 -11
- package/assets/tests_test_generate_validation_schema.py__test_radios_property_0_0.txt +0 -14
- package/assets/tests_test_generate_validation_schema.py__test_text_property_0_0.txt +0 -11
- package/assets/tests_test_generate_validation_schema.py__test_text_property_format_0_0.txt +0 -11
- package/assets/tests_test_generate_validation_schema.py__test_uri_property_0_0.txt +0 -11
- package/frameworks/digital-outcomes-and-specialists-7/questions/services/serviceName.yml +0 -18
|
@@ -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,24 @@
|
|
|
1
|
+
name: What types of user research studio can you provide?
|
|
2
|
+
question: What types of user research studio 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: "Virtual studio"
|
|
13
|
+
value: "virtual-studio"
|
|
14
|
+
- label: "Physical location"
|
|
15
|
+
value: "physical-location"
|
|
16
|
+
|
|
17
|
+
depends_on_follow_up:
|
|
18
|
+
whatUserResearchServices:
|
|
19
|
+
- user-research-studios
|
|
20
|
+
|
|
21
|
+
validations:
|
|
22
|
+
-
|
|
23
|
+
name: answer_required
|
|
24
|
+
message: You must select the types of user research studio can you provide
|
|
@@ -0,0 +1,46 @@
|
|
|
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
|
+
mrsMember:
|
|
23
|
+
- user-research-participants
|
|
24
|
+
otherTradeOrIndustryBodies:
|
|
25
|
+
- user-research-participants
|
|
26
|
+
rasAccredited:
|
|
27
|
+
- user-research-participants
|
|
28
|
+
useRasAccreditedSubcontractors:
|
|
29
|
+
- user-research-participants
|
|
30
|
+
complyWithMrsCodeOfConduct:
|
|
31
|
+
- user-research-participants
|
|
32
|
+
icoRegistered:
|
|
33
|
+
- user-research-participants
|
|
34
|
+
whatTypesOfUserResearchStudio:
|
|
35
|
+
- user-research-studios
|
|
36
|
+
|
|
37
|
+
dependent_follow_up_followup:
|
|
38
|
+
otherTradeOrIndustryBodiesOther:
|
|
39
|
+
- user-research-participants
|
|
40
|
+
icoRegistrationReference:
|
|
41
|
+
- user-research-participants
|
|
42
|
+
|
|
43
|
+
validations:
|
|
44
|
+
-
|
|
45
|
+
name: answer_required
|
|
46
|
+
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.2.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.10.0
|
|
38
38
|
# via -r requirements.txt
|
|
39
39
|
ccs-digitalmarketplace-utils==68.0.0
|
|
40
40
|
# via
|
|
@@ -115,7 +115,7 @@ govuk-country-register==0.5.0
|
|
|
115
115
|
# via
|
|
116
116
|
# -r requirements.txt
|
|
117
117
|
# ccs-digitalmarketplace-utils
|
|
118
|
-
hypothesis==6.
|
|
118
|
+
hypothesis==6.115.0
|
|
119
119
|
# via -r requirements-dev.in
|
|
120
120
|
idna==3.7
|
|
121
121
|
# 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.10.0
|
|
22
22
|
# via -r requirements.in
|
|
23
23
|
ccs-digitalmarketplace-utils==68.0.0
|
|
24
24
|
# via
|
|
@@ -280,7 +280,8 @@ def load_questions(schema_type, framework_slug, lot_slug_or_agreement_version):
|
|
|
280
280
|
|
|
281
281
|
manifest = loader.get_manifest(framework_slug, MANIFESTS[schema_type]['manifest']).filter(
|
|
282
282
|
{filter_key: lot_slug_or_agreement_version},
|
|
283
|
-
dynamic=False
|
|
283
|
+
dynamic=False,
|
|
284
|
+
ignore_depends_on_follow_up=True
|
|
284
285
|
)
|
|
285
286
|
|
|
286
287
|
return {q['id']: q for q in sum((s.questions for s in manifest.sections), [])}
|
|
@@ -348,22 +349,41 @@ def uri_property(question):
|
|
|
348
349
|
}}
|
|
349
350
|
|
|
350
351
|
|
|
352
|
+
def has_dependent_follow_up(property_func):
|
|
353
|
+
"""
|
|
354
|
+
Adds the additional dependent follow up schema to the property
|
|
355
|
+
"""
|
|
356
|
+
def inner_func(question):
|
|
357
|
+
schema = property_func(question)
|
|
358
|
+
schema_addition = {}
|
|
359
|
+
|
|
360
|
+
if question.get('dependent_follow_up'):
|
|
361
|
+
schema_addition = _dependent_follow_up(question)
|
|
362
|
+
|
|
363
|
+
return schema, schema_addition
|
|
364
|
+
|
|
365
|
+
return inner_func
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
@has_dependent_follow_up
|
|
351
369
|
def checkbox_property(question):
|
|
352
370
|
"""
|
|
353
371
|
Convert a checkbox question into JSON Schema.
|
|
354
372
|
"""
|
|
355
|
-
return {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
"
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
373
|
+
return {
|
|
374
|
+
question['id']: {
|
|
375
|
+
"type": "array",
|
|
376
|
+
"uniqueItems": True,
|
|
377
|
+
"minItems": 0 if question.get('optional') else 1,
|
|
378
|
+
"maxItems": question.get('number_of_items', len(question['options'])),
|
|
379
|
+
"items": {
|
|
380
|
+
"enum": [
|
|
381
|
+
option.get('value', option['label'])
|
|
382
|
+
for option in question['options']
|
|
383
|
+
]
|
|
384
|
+
}
|
|
365
385
|
}
|
|
366
|
-
}
|
|
386
|
+
}
|
|
367
387
|
|
|
368
388
|
|
|
369
389
|
def checkbox_tree_property(question):
|
|
@@ -625,6 +645,45 @@ def _followup(question, root):
|
|
|
625
645
|
return {'allOf': schemas}
|
|
626
646
|
|
|
627
647
|
|
|
648
|
+
def _dependent_follow_up(question):
|
|
649
|
+
schemas = []
|
|
650
|
+
question_values = [
|
|
651
|
+
option.get('value', option['label'])
|
|
652
|
+
for option in question.options
|
|
653
|
+
]
|
|
654
|
+
for dependent_follow_up_field, values in question["dependent_follow_up"].items():
|
|
655
|
+
non_dependent_question_values = [
|
|
656
|
+
value
|
|
657
|
+
for value in question_values
|
|
658
|
+
if value not in values
|
|
659
|
+
]
|
|
660
|
+
should_not_have_followup = {
|
|
661
|
+
"items": {"enum": non_dependent_question_values}
|
|
662
|
+
}
|
|
663
|
+
should_have_followup = {
|
|
664
|
+
"not": {"items": {"enum": non_dependent_question_values}}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
schemas.append({
|
|
668
|
+
'oneOf': [
|
|
669
|
+
{
|
|
670
|
+
"properties": {
|
|
671
|
+
question['id']: should_not_have_followup,
|
|
672
|
+
dependent_follow_up_field: {"type": "null"}
|
|
673
|
+
},
|
|
674
|
+
},
|
|
675
|
+
{
|
|
676
|
+
"properties": {
|
|
677
|
+
question['id']: should_have_followup,
|
|
678
|
+
},
|
|
679
|
+
"required": [dependent_follow_up_field]
|
|
680
|
+
},
|
|
681
|
+
]
|
|
682
|
+
})
|
|
683
|
+
|
|
684
|
+
return {'allOf': schemas}
|
|
685
|
+
|
|
686
|
+
|
|
628
687
|
def _flat_multiquestion(question):
|
|
629
688
|
properties = {}
|
|
630
689
|
for nested_question in question.questions:
|
|
@@ -666,7 +725,7 @@ def _nested_multiquestion(question):
|
|
|
666
725
|
|
|
667
726
|
|
|
668
727
|
def _flat_multiquestion_required(question):
|
|
669
|
-
if question.get('optional'):
|
|
728
|
+
if question.get('optional') or question.get('depends_on_follow_up'):
|
|
670
729
|
return []
|
|
671
730
|
|
|
672
731
|
return _nested_multiquestion_required(question)
|
|
@@ -825,8 +884,13 @@ def add_assurance(value_schema, assurance_approach):
|
|
|
825
884
|
def build_question_properties(question):
|
|
826
885
|
question_data = QUESTION_TYPES[question['type']](question)
|
|
827
886
|
if question.get('assuranceApproach'):
|
|
828
|
-
|
|
829
|
-
|
|
887
|
+
if isinstance(question_data, tuple):
|
|
888
|
+
for key, value_schema in question_data[0].items():
|
|
889
|
+
question_data[0][key] = add_assurance(value_schema, question['assuranceApproach'])
|
|
890
|
+
else:
|
|
891
|
+
for key, value_schema in question_data.items():
|
|
892
|
+
question_data[key] = add_assurance(value_schema, question['assuranceApproach'])
|
|
893
|
+
|
|
830
894
|
return question_data
|
|
831
895
|
|
|
832
896
|
|
|
@@ -843,10 +907,18 @@ def build_schema_properties(schema, questions):
|
|
|
843
907
|
if isinstance(property_schema, tuple):
|
|
844
908
|
property_schema, schema_addition = property_schema
|
|
845
909
|
schema['properties'].update(property_schema)
|
|
846
|
-
|
|
910
|
+
|
|
911
|
+
if (
|
|
912
|
+
not schema_addition or question.get('dependent_follow_up')
|
|
913
|
+
) and not question.get('depends_on_follow_up'):
|
|
914
|
+
schema['required'].extend(question.required_form_fields)
|
|
915
|
+
|
|
916
|
+
if schema_addition:
|
|
917
|
+
schema = merge_schemas(schema, schema_addition)
|
|
847
918
|
else:
|
|
848
919
|
schema['properties'].update(property_schema)
|
|
849
|
-
|
|
920
|
+
if not question.get('depends_on_follow_up'):
|
|
921
|
+
schema['required'].extend(question.required_form_fields)
|
|
850
922
|
|
|
851
923
|
schema['required'].sort()
|
|
852
924
|
|
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,24 @@
|
|
|
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
|
+
"dependent_follow_up_followup": {
|
|
336
|
+
"type": "object",
|
|
337
|
+
"patternProperties": {
|
|
338
|
+
"^[a-zA-Z0-9]+$": {"type": "array"}
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
"depends_on_follow_up": {
|
|
342
|
+
"type": "object",
|
|
343
|
+
"patternProperties": {
|
|
344
|
+
"^[a-zA-Z0-9]+$": {"type": "array"}
|
|
345
|
+
}
|
|
346
|
+
},
|
|
324
347
|
"dynamic_field": {
|
|
325
348
|
"type": "string"
|
|
326
349
|
},
|
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,48 @@ 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
|
+
questions_with_dependent_follow_up_followup = {}
|
|
141
|
+
expected_questions_with_dependent_follow_up = {}
|
|
142
|
+
expected_questions_with_dependent_follow_up_followup = {}
|
|
143
|
+
|
|
144
|
+
for section in loaded_content.sections:
|
|
145
|
+
for section_question in section.questions:
|
|
146
|
+
if isinstance(section_question, Multiquestion):
|
|
147
|
+
questions = section_question.get_questions_flat()
|
|
148
|
+
else:
|
|
149
|
+
questions = [section_question]
|
|
150
|
+
|
|
151
|
+
for question in questions:
|
|
152
|
+
if question.get("dependent_follow_up"):
|
|
153
|
+
questions_with_dependent_follow_up[question.id] = question.get("dependent_follow_up")
|
|
154
|
+
if question.get("dependent_follow_up_followup"):
|
|
155
|
+
questions_with_dependent_follow_up_followup[question.id] = \
|
|
156
|
+
question.get("dependent_follow_up_followup")
|
|
157
|
+
|
|
158
|
+
for question_id, question_values in question.get("depends_on_follow_up", {}).items():
|
|
159
|
+
if question_id not in expected_questions_with_dependent_follow_up:
|
|
160
|
+
expected_questions_with_dependent_follow_up[question_id] = {}
|
|
161
|
+
|
|
162
|
+
for field in question.form_fields:
|
|
163
|
+
expected_questions_with_dependent_follow_up[question_id][field] = question_values
|
|
164
|
+
|
|
165
|
+
for fllowup_question_id in question.get('followup', {}).keys():
|
|
166
|
+
if question_id not in expected_questions_with_dependent_follow_up_followup:
|
|
167
|
+
expected_questions_with_dependent_follow_up_followup[question_id] = {}
|
|
168
|
+
|
|
169
|
+
expected_questions_with_dependent_follow_up_followup[question_id][fllowup_question_id] = \
|
|
170
|
+
question_values
|
|
171
|
+
|
|
172
|
+
assert questions_with_dependent_follow_up == expected_questions_with_dependent_follow_up
|
|
173
|
+
assert questions_with_dependent_follow_up_followup == expected_questions_with_dependent_follow_up_followup
|
|
174
|
+
|
|
175
|
+
|
|
133
176
|
def strip_paragraph_wrapper(value):
|
|
134
177
|
match = re.fullmatch(r"^(<p.*?>)(.+?)(</p>)", value)
|
|
135
178
|
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):
|
package/assets/style.css
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
body {
|
|
2
|
-
font-family: Helvetica, Arial, sans-serif;
|
|
3
|
-
font-size: 12px;
|
|
4
|
-
/* do not increase min-width as some may use split screens */
|
|
5
|
-
min-width: 800px;
|
|
6
|
-
color: #999;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
h1 {
|
|
10
|
-
font-size: 24px;
|
|
11
|
-
color: black;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
h2 {
|
|
15
|
-
font-size: 16px;
|
|
16
|
-
color: black;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
p {
|
|
20
|
-
color: black;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
a {
|
|
24
|
-
color: #999;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
table {
|
|
28
|
-
border-collapse: collapse;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/******************************
|
|
32
|
-
* SUMMARY INFORMATION
|
|
33
|
-
******************************/
|
|
34
|
-
#environment td {
|
|
35
|
-
padding: 5px;
|
|
36
|
-
border: 1px solid #E6E6E6;
|
|
37
|
-
}
|
|
38
|
-
#environment tr:nth-child(odd) {
|
|
39
|
-
background-color: #f6f6f6;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/******************************
|
|
43
|
-
* TEST RESULT COLORS
|
|
44
|
-
******************************/
|
|
45
|
-
span.passed,
|
|
46
|
-
.passed .col-result {
|
|
47
|
-
color: green;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
span.skipped,
|
|
51
|
-
span.xfailed,
|
|
52
|
-
span.rerun,
|
|
53
|
-
.skipped .col-result,
|
|
54
|
-
.xfailed .col-result,
|
|
55
|
-
.rerun .col-result {
|
|
56
|
-
color: orange;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
span.error,
|
|
60
|
-
span.failed,
|
|
61
|
-
span.xpassed,
|
|
62
|
-
.error .col-result,
|
|
63
|
-
.failed .col-result,
|
|
64
|
-
.xpassed .col-result {
|
|
65
|
-
color: red;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/******************************
|
|
69
|
-
* RESULTS TABLE
|
|
70
|
-
*
|
|
71
|
-
* 1. Table Layout
|
|
72
|
-
* 2. Extra
|
|
73
|
-
* 3. Sorting items
|
|
74
|
-
*
|
|
75
|
-
******************************/
|
|
76
|
-
/*------------------
|
|
77
|
-
* 1. Table Layout
|
|
78
|
-
*------------------*/
|
|
79
|
-
#results-table {
|
|
80
|
-
border: 1px solid #e6e6e6;
|
|
81
|
-
color: #999;
|
|
82
|
-
font-size: 12px;
|
|
83
|
-
width: 100%;
|
|
84
|
-
}
|
|
85
|
-
#results-table th,
|
|
86
|
-
#results-table td {
|
|
87
|
-
padding: 5px;
|
|
88
|
-
border: 1px solid #E6E6E6;
|
|
89
|
-
text-align: left;
|
|
90
|
-
}
|
|
91
|
-
#results-table th {
|
|
92
|
-
font-weight: bold;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/*------------------
|
|
96
|
-
* 2. Extra
|
|
97
|
-
*------------------*/
|
|
98
|
-
.log {
|
|
99
|
-
background-color: #e6e6e6;
|
|
100
|
-
border: 1px solid #e6e6e6;
|
|
101
|
-
color: black;
|
|
102
|
-
display: block;
|
|
103
|
-
font-family: "Courier New", Courier, monospace;
|
|
104
|
-
height: 230px;
|
|
105
|
-
overflow-y: scroll;
|
|
106
|
-
padding: 5px;
|
|
107
|
-
white-space: pre-wrap;
|
|
108
|
-
}
|
|
109
|
-
.log:only-child {
|
|
110
|
-
height: inherit;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
div.image {
|
|
114
|
-
border: 1px solid #e6e6e6;
|
|
115
|
-
float: right;
|
|
116
|
-
height: 240px;
|
|
117
|
-
margin-left: 5px;
|
|
118
|
-
overflow: hidden;
|
|
119
|
-
width: 320px;
|
|
120
|
-
}
|
|
121
|
-
div.image img {
|
|
122
|
-
width: 320px;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
div.video {
|
|
126
|
-
border: 1px solid #e6e6e6;
|
|
127
|
-
float: right;
|
|
128
|
-
height: 240px;
|
|
129
|
-
margin-left: 5px;
|
|
130
|
-
overflow: hidden;
|
|
131
|
-
width: 320px;
|
|
132
|
-
}
|
|
133
|
-
div.video video {
|
|
134
|
-
overflow: hidden;
|
|
135
|
-
width: 320px;
|
|
136
|
-
height: 240px;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
.collapsed {
|
|
140
|
-
display: none;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
.expander::after {
|
|
144
|
-
content: " (show details)";
|
|
145
|
-
color: #BBB;
|
|
146
|
-
font-style: italic;
|
|
147
|
-
cursor: pointer;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
.collapser::after {
|
|
151
|
-
content: " (hide details)";
|
|
152
|
-
color: #BBB;
|
|
153
|
-
font-style: italic;
|
|
154
|
-
cursor: pointer;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/*------------------
|
|
158
|
-
* 3. Sorting items
|
|
159
|
-
*------------------*/
|
|
160
|
-
.sortable {
|
|
161
|
-
cursor: pointer;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
.sort-icon {
|
|
165
|
-
font-size: 0px;
|
|
166
|
-
float: left;
|
|
167
|
-
margin-right: 5px;
|
|
168
|
-
margin-top: 5px;
|
|
169
|
-
/*triangle*/
|
|
170
|
-
width: 0;
|
|
171
|
-
height: 0;
|
|
172
|
-
border-left: 8px solid transparent;
|
|
173
|
-
border-right: 8px solid transparent;
|
|
174
|
-
}
|
|
175
|
-
.inactive .sort-icon {
|
|
176
|
-
/*finish triangle*/
|
|
177
|
-
border-top: 8px solid #E6E6E6;
|
|
178
|
-
}
|
|
179
|
-
.asc.active .sort-icon {
|
|
180
|
-
/*finish triangle*/
|
|
181
|
-
border-bottom: 8px solid #999;
|
|
182
|
-
}
|
|
183
|
-
.desc.active .sort-icon {
|
|
184
|
-
/*finish triangle*/
|
|
185
|
-
border-top: 8px solid #999;
|
|
186
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
tests/test_generate_validation_schema.py::test_boolean_list_property:
|
|
2
|
-
|
|
3
|
-
- during reuse phase (0.00 seconds):
|
|
4
|
-
- Typical runtimes: < 1ms, ~ 57% in data generation
|
|
5
|
-
- 2 passing examples, 0 failing examples, 0 invalid examples
|
|
6
|
-
|
|
7
|
-
- during generate phase (0.11 seconds):
|
|
8
|
-
- Typical runtimes: < 1ms, ~ 73% in data generation
|
|
9
|
-
- 98 passing examples, 0 failing examples, 1 invalid examples
|
|
10
|
-
|
|
11
|
-
- Stopped because settings.max_examples=100
|