Skip to content

Commit

Permalink
- Define schema for completion criteria
Browse files Browse the repository at this point in the history
- Lower jsonschema test requirement to work for more pythons
- Skip schema tests for python 3.4 since supporting jsonschema package is too old
  • Loading branch information
bjester committed Jan 20, 2022
1 parent b208f6b commit 3908c24
Show file tree
Hide file tree
Showing 10 changed files with 596 additions and 18 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,4 @@ tests/testcontent/

# Pycharm project settings
.idea/
*.iml
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ The module `le_utils.constants.languages` defines two other language lookup meth

#### Useful links

The following websites are usful for researching language codes:
The following websites are useful for researching language codes:

- https://www.ethnologue.com/
- https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
Expand Down
119 changes: 119 additions & 0 deletions js/CompletionCriteria.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,122 @@ export default {
REFERENCE: "reference",
TIME: "time",
};

export const SCHEMA = {
"type": "object",
"description": "Schema for completion criteria of content nodes",
"additionalProperties": false,
"definitions": {
"model": {
"type": "string",
"$exportConstants": "completion_criteria",
"enum": [
"time",
"approx_time",
"pages",
"mastery",
"reference"
]
},
"mastery_criteria": {
"type": "object",
"$comment": "TODO move to separate schema",
"additionalProperties": false,
"required": ["mastery_model"],
"properties": {
"m": true,
"n": true,
"mastery_model": {
"type": "string",
"enum": [
"do_all",
"m_of_n",
"num_correct_in_a_row_2",
"num_correct_in_a_row_3",
"num_correct_in_a_row_5",
"num_correct_in_a_row_10"
]
}
},
"anyOf": [
{
"properties": {
"mastery_model": {
"const": "m_of_n"
}
},
"required": ["m", "n"]
},
{
"properties": {
"mastery_model": {
"enum": [
"do_all",
"num_correct_in_a_row_2",
"num_correct_in_a_row_3",
"num_correct_in_a_row_5",
"num_correct_in_a_row_10"
]
},
"m": {
"type": "null"
},
"n": {
"type": "null"
}
}
}
]
}
},
"properties": {
"model": {
"$ref": "#/definitions/model"
},
"learner_managed": {
"type": "boolean"
},
"threshold": true
},
"required": ["model"],
"anyOf": [
{
"properties": {
"model": {
"anyOf": [
{"const": "time"},
{"const": "approx_time"},
{"const": "pages"}
]
},
"threshold": {
"type": "number",
"exclusiveMinimum": 0
}
},
"required": ["threshold"]
},
{
"properties": {
"model": {
"const": "mastery"
},
"threshold": {
"$ref": "#/definitions/mastery_criteria"
}
},
"required": ["threshold"]
},
{
"properties": {
"model": {
"const": "reference"
},
"threshold": {
"type": "null"
}
},
"required": []
}
]
};
2 changes: 1 addition & 1 deletion js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@
"test": "echo \"Error: no test specified\" && exit 1"
},
"version": "0.1.38"
}
}
90 changes: 90 additions & 0 deletions le_utils/constants/completion_criteria.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,93 @@
REFERENCE,
TIME,
]

SCHEMA = {
"type": "object",
"description": "Schema for completion criteria of content nodes",
"additionalProperties": False,
"definitions": {
"model": {
"type": "string",
"$exportConstants": "completion_criteria",
"enum": ["time", "approx_time", "pages", "mastery", "reference"],
},
"mastery_criteria": {
"type": "object",
"$comment": "TODO move to separate schema",
"additionalProperties": False,
"required": ["mastery_model"],
"properties": {
"m": True,
"n": True,
"mastery_model": {
"type": "string",
"enum": [
"do_all",
"m_of_n",
"num_correct_in_a_row_2",
"num_correct_in_a_row_3",
"num_correct_in_a_row_5",
"num_correct_in_a_row_10",
],
},
},
"anyOf": [
{
"properties": {"mastery_model": {"const": "m_of_n"}},
"required": ["m", "n"],
},
{
"properties": {
"mastery_model": {
"enum": [
"do_all",
"num_correct_in_a_row_2",
"num_correct_in_a_row_3",
"num_correct_in_a_row_5",
"num_correct_in_a_row_10",
]
},
"m": {"type": "null"},
"n": {"type": "null"},
}
},
],
},
},
"properties": {
"model": {"$ref": "#/definitions/model"},
"learner_managed": {"type": "boolean"},
"threshold": True,
},
"required": ["model"],
"anyOf": [
{
"properties": {
"model": {
"anyOf": [
{"const": "time"},
{"const": "approx_time"},
{"const": "pages"},
]
},
"threshold": {"type": "number", "exclusiveMinimum": 0},
},
"required": ["threshold"],
},
{
"properties": {
"model": {"const": "mastery"},
"threshold": {"$ref": "#/definitions/mastery_criteria"},
},
"required": ["threshold"],
},
{
"properties": {
"model": {"const": "reference"},
"threshold": {"type": "null"},
},
"required": [],
},
],
}
1 change: 1 addition & 0 deletions requirements-test.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pycountry==17.5.14
pytest==3.7.1
attrs==20.3.0
jsonschema==3.2.0;python_version!="3.4"
61 changes: 52 additions & 9 deletions scripts/generate_from_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@

py_output_dir = os.path.join(os.path.dirname(__file__), "..", "le_utils", "constants")

py_labels_output_dir = os.path.join(
os.path.dirname(__file__), "..", "le_utils", "constants", "labels"
)
py_labels_output_dir = os.path.join(py_output_dir, "labels")


def pascal_to_snake(name):
Expand Down Expand Up @@ -156,7 +154,42 @@ def read_constants_specs():
return constants_outputs


def write_python_file(output_file, name, ordered_output):
def read_schema_specs():
schema_spec_files = glob(
os.path.join(
os.path.dirname(__file__),
"..",
"spec",
"schema-*.json",
)
)
schema_outputs = {}
constants_outputs = {}
for schema_spec_file in schema_spec_files:
with open(schema_spec_file) as json_schema_spec_file:
key = snake_to_pascal(schema_spec_file.split("-")[-1].split(".")[0])
schema_outputs[key] = json_schema_spec_file.read()

json_schema = json.loads(schema_outputs[key])
# find definitions that should be exported as constants
for json_schema_def in json_schema.get("definitions", {}).values():
export_name = json_schema_def.get("$exportConstants")
if export_name is not None and "enum" in json_schema_def:
constants_outputs.update(
{
snake_to_pascal(export_name): OrderedDict(
[
(a.upper(), a)
for a in sorted(json_schema_def.get("enum"))
]
)
}
)

return schema_outputs, constants_outputs


def write_python_file(output_file, name, ordered_output, schema=None):
with open(output_file, "w") as f:
f.write("# -*- coding: utf-8 -*-\n")
f.write("# Generated by scripts/generate_from_specs.py\n")
Expand All @@ -176,14 +209,17 @@ def write_python_file(output_file, name, ordered_output):
for key in ordered_output.keys():
f.write(" {},\n".format(key))
f.write("]\n")
if schema:
f.write("\n")
f.write("SCHEMA = {}\n".format(repr(json.loads(schema))))


def write_js_header(f):
f.write("// -*- coding: utf-8 -*-\n")
f.write("// Generated by scripts/generate_from_specs.py\n")


def write_js_file(output_file, name, ordered_output):
def write_js_file(output_file, name, ordered_output, schema=None):
with open(output_file, "w") as f:
write_js_header(f)
f.write("// {}\n".format(name))
Expand All @@ -192,6 +228,9 @@ def write_js_file(output_file, name, ordered_output):
for key, value in ordered_output.items():
f.write(' {key}: "{value}",\n'.format(key=key, value=value))
f.write("};\n")
if schema:
f.write("\n")
f.write("export const SCHEMA = {};\n".format(schema))


def write_labels_src_files(label_outputs):
Expand All @@ -205,15 +244,16 @@ def write_labels_src_files(label_outputs):
write_js_file(js_output_file, label_type, ordered_output)


def write_constants_src_files(constants_outputs):
def write_constants_src_files(constants_outputs, schemas):
for constant_type, ordered_output in constants_outputs.items():
py_output_file = os.path.join(
py_output_dir, "{}.py".format(pascal_to_snake(constant_type))
)
write_python_file(py_output_file, constant_type, ordered_output)
schema = schemas.get(constant_type)
write_python_file(py_output_file, constant_type, ordered_output, schema=schema)

js_output_file = os.path.join(js_output_dir, "{}.js".format(constant_type))
write_js_file(js_output_file, constant_type, ordered_output)
write_js_file(js_output_file, constant_type, ordered_output, schema=schema)


def set_package_json_version():
Expand Down Expand Up @@ -242,8 +282,11 @@ def set_package_json_version():

constants_to_write = read_constants_specs()

schemas_to_write, schema_constants_to_write = read_schema_specs()
constants_to_write.update(schema_constants_to_write)

write_labels_src_files(labels_to_write)

write_constants_src_files(constants_to_write)
write_constants_src_files(constants_to_write, schemas_to_write)

set_package_json_version()
7 changes: 0 additions & 7 deletions spec/constants-completion_criteria.json

This file was deleted.

Loading

0 comments on commit 3908c24

Please sign in to comment.