Skip to content

Commit 3b8e47d

Browse files
authored
Merge pull request #10 from satra/enh-schema-support
Enh schema support
2 parents 15b5dc4 + d078154 commit 3b8e47d

File tree

9 files changed

+422
-1
lines changed

9 files changed

+422
-1
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,4 @@ dmypy.json
129129
.pyre/
130130

131131
# Pycharm
132-
.idea
132+
.idea/

reproschema/models/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .protocol import Protocol
2+
from .activity import Activity
3+
from .item import Item

reproschema/models/activity.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
from .base import SchemaBase
2+
3+
4+
class Activity(SchemaBase):
5+
"""
6+
class to deal with reproschema activities
7+
"""
8+
9+
schema_type = "reproschema:Activity"
10+
11+
def __init__(self, version=None):
12+
super().__init__(version)
13+
self.schema["ui"] = {"shuffle": [], "order": [], "addProperties": []}
14+
15+
def set_ui_shuffle(self, shuffle=False):
16+
self.schema["ui"]["shuffle"] = shuffle
17+
18+
def set_URI(self, URI):
19+
self.URI = URI
20+
21+
def get_URI(self):
22+
return self.URI
23+
24+
# TODO
25+
# preamble
26+
# compute
27+
# citation
28+
# image
29+
30+
def set_defaults(self, name):
31+
self._ReproschemaSchema__set_defaults(name) # this looks wrong
32+
self.set_ui_shuffle(False)
33+
34+
def update_activity(self, item_info):
35+
36+
# TODO
37+
# - remove the hard coding on visibility and valueRequired
38+
39+
# update the content of the activity schema with new item
40+
41+
item_info["URI"] = "items/" + item_info["name"]
42+
43+
append_to_activity = {
44+
"variableName": item_info["name"],
45+
"isAbout": item_info["URI"],
46+
"isVis": item_info["visibility"],
47+
"valueRequired": False,
48+
}
49+
50+
self.schema["ui"]["order"].append(item_info["URI"])
51+
self.schema["ui"]["addProperties"].append(append_to_activity)
52+
53+
def sort(self):
54+
schema_order = [
55+
"@context",
56+
"@type",
57+
"@id",
58+
"prefLabel",
59+
"description",
60+
"schemaVersion",
61+
"version",
62+
"ui",
63+
]
64+
self.sort_schema(schema_order)
65+
66+
ui_order = ["shuffle", "order", "addProperties"]
67+
self.sort_ui(ui_order)

reproschema/models/base.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import json
2+
import os
3+
4+
5+
class SchemaBase:
6+
"""
7+
class to deal with reproschema schemas
8+
"""
9+
10+
schema_type = None
11+
12+
def __init__(self, version):
13+
14+
URL = "https://raw.githubusercontent.com/ReproNim/reproschema/"
15+
VERSION = version or "1.0.0-rc2"
16+
17+
self.schema = {
18+
"@context": URL + VERSION + "/contexts/generic",
19+
"@type": self.schema_type,
20+
"schemaVersion": VERSION,
21+
"version": "0.0.1",
22+
}
23+
24+
def set_filename(self, name):
25+
self.schema_file = name + "_schema"
26+
self.schema["@id"] = name + "_schema"
27+
28+
def get_name(self):
29+
return self.schema_file.replace("_schema", "")
30+
31+
def get_filename(self):
32+
return self.schema_file
33+
34+
def set_pref_label(self, pref_label):
35+
self.schema["prefLabel"] = pref_label
36+
37+
def set_description(self, description):
38+
self.schema["description"] = description
39+
40+
def set_directory(self, output_directory):
41+
self.dir = output_directory
42+
43+
def __set_defaults(self, name):
44+
self.set_filename(name)
45+
self.set_directory(name)
46+
self.set_pref_label(name.replace("_", " "))
47+
self.set_description(name.replace("_", " "))
48+
49+
def sort_schema(self, schema_order):
50+
51+
reordered_dict = {k: self.schema[k] for k in schema_order}
52+
self.schema = reordered_dict
53+
54+
def sort_ui(self, ui_order):
55+
56+
reordered_dict = {k: self.schema["ui"][k] for k in ui_order}
57+
self.schema["ui"] = reordered_dict
58+
59+
def write(self, output_dir):
60+
with open(os.path.join(output_dir, self.schema_file), "w") as ff:
61+
json.dump(self.schema, ff, sort_keys=False, indent=4)
62+
63+
@classmethod
64+
def from_data(cls, data):
65+
if cls.schema_type is None:
66+
raise ValueError("SchemaBase cannot be used to instantiate class")
67+
if cls.schema_type != data["@type"]:
68+
raise ValueError(f"Mismatch in type {data['@type']} != {cls.schema_type}")
69+
klass = cls()
70+
klass.schema = data
71+
return klass
72+
73+
@classmethod
74+
def from_file(cls, filepath):
75+
with open(filepath) as fp:
76+
data = json.load(fp)
77+
if "@type" not in data:
78+
raise ValueError("Missing @type key")
79+
return cls.from_data(data)

reproschema/models/item.py

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
from .base import SchemaBase
2+
3+
4+
class Item(SchemaBase):
5+
"""
6+
class to deal with reproschema activities
7+
"""
8+
9+
schema_type = "reproschema:Field"
10+
11+
def __init__(self, version=None):
12+
super().__init__(version)
13+
self.schema["ui"] = {"inputType": []}
14+
self.schema["question"] = {}
15+
self.schema["responseOptions"] = {}
16+
# default input type is "char"
17+
self.set_input_type_as_char()
18+
19+
def set_URI(self, URI):
20+
self.URI = URI
21+
22+
# TODO
23+
# image
24+
# readonlyValue
25+
26+
def set_defaults(self, name):
27+
self._ReproschemaSchema__set_defaults(name) # this looks wrong
28+
self.schema_file = name
29+
self.schema["@id"] = name
30+
self.set_input_type_as_char()
31+
32+
def set_question(self, question, lang="en"):
33+
self.schema["question"][lang] = question
34+
35+
def set_input_type(self, input_type):
36+
self.schema["ui"]["inputType"] = input_type
37+
38+
def set_response_options(self, response_options):
39+
self.schema["responseOptions"] = response_options
40+
41+
"""
42+
43+
input types with different response choices
44+
45+
"""
46+
47+
def set_input_type_as_radio(self, response_options):
48+
self.set_input_type("radio")
49+
self.set_response_options(response_options)
50+
51+
def set_input_type_as_select(self, response_options):
52+
self.set_input_type("select")
53+
self.set_response_options(response_options)
54+
55+
def set_input_type_as_slider(self):
56+
self.set_input_type_as_char() # until the slide item of the ui is fixed
57+
# self.set_input_type("slider")
58+
# self.set_response_options({"valueType": "xsd:string"})
59+
60+
def set_input_type_as_language(self):
61+
62+
URL = "https://raw.githubusercontent.com/ReproNim/reproschema/"
63+
64+
self.set_input_type("selectLanguage")
65+
66+
response_options = {
67+
"valueType": "xsd:string",
68+
"multipleChoice": True,
69+
"choices": URL + "master/resources/languages.json",
70+
}
71+
self.set_response_options(response_options)
72+
73+
"""
74+
75+
input types with no response choice
76+
77+
"""
78+
79+
def set_input_type_as_char(self):
80+
self.set_input_type("text")
81+
self.set_response_options({"valueType": "xsd:string"})
82+
83+
def set_input_type_as_int(self):
84+
self.set_input_type("number")
85+
self.set_response_options({"valueType": "xsd:integer"})
86+
87+
def set_input_type_as_float(self):
88+
self.set_input_type("float")
89+
self.set_response_options({"valueType": "xsd:float"})
90+
91+
def set_input_type_as_time_range(self):
92+
self.set_input_type("timeRange")
93+
self.set_response_options({"valueType": "datetime"})
94+
95+
def set_input_type_as_date(self):
96+
self.set_input_type("date")
97+
self.set_response_options({"valueType": "xsd:date"})
98+
99+
"""
100+
101+
input types with no response choice but with some parameters
102+
103+
"""
104+
105+
def set_input_type_as_multitext(self, max_length=300):
106+
self.set_input_type("text")
107+
self.set_response_options({"valueType": "xsd:string", "maxLength": max_length})
108+
109+
# TODO
110+
# email: EmailInput/EmailInput.vue
111+
# audioCheck: AudioCheck/AudioCheck.vue
112+
# audioRecord: WebAudioRecord/Audio.vue
113+
# audioPassageRecord: WebAudioRecord/Audio.vue
114+
# audioImageRecord: WebAudioRecord/Audio.vue
115+
# audioRecordNumberTask: WebAudioRecord/Audio.vue
116+
# audioAutoRecord: AudioCheckRecord/AudioCheckRecord.vue
117+
# year: YearInput/YearInput.vue
118+
# selectCountry: SelectInput/SelectInput.vue
119+
# selectState: SelectInput/SelectInput.vue
120+
# documentUpload: DocumentUpload/DocumentUpload.vue
121+
# save: SaveData/SaveData.vue
122+
# static: Static/Static.vue
123+
# StaticReadOnly: Static/Static.vue
124+
125+
def set_basic_response_type(self, response_type):
126+
127+
# default (also valid for "char" input type)
128+
self.set_input_type_as_char()
129+
130+
if response_type == "int":
131+
self.set_input_type_as_int()
132+
133+
elif response_type == "float":
134+
self.set_input_type_as_float()
135+
136+
elif response_type == "date":
137+
self.set_input_type_as_date()
138+
139+
elif response_type == "time range":
140+
self.set_input_type_as_time_range()
141+
142+
elif response_type == "language":
143+
self.set_input_type_as_language()
144+
145+
def sort(self):
146+
schema_order = [
147+
"@context",
148+
"@type",
149+
"@id",
150+
"prefLabel",
151+
"description",
152+
"schemaVersion",
153+
"version",
154+
"ui",
155+
"question",
156+
"responseOptions",
157+
]
158+
self.sort_schema(schema_order)

reproschema/models/protocol.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
from .base import SchemaBase
2+
3+
4+
class Protocol(SchemaBase):
5+
"""
6+
class to deal with reproschema protocols
7+
"""
8+
9+
schema_type = "reproschema:Protocol"
10+
11+
def __init__(self, version=None):
12+
super().__init__(version)
13+
self.schema["ui"] = {
14+
"allow": [],
15+
"shuffle": [],
16+
"order": [],
17+
"addProperties": [],
18+
}
19+
20+
def set_landing_page(self, landing_page_url, lang="en"):
21+
self.schema["landingPage"] = {"@id": landing_page_url, "@language": lang}
22+
23+
# TODO
24+
# def add_landing_page(self, landing_page_url, lang="en"):
25+
# preamble
26+
# compute
27+
28+
def set_image(self, image_url):
29+
self.schema["image"] = image_url
30+
31+
def set_ui_allow(self):
32+
self.schema["ui"]["allow"] = [
33+
"reproschema:AutoAdvance",
34+
"reproschema:AllowExport",
35+
]
36+
37+
def set_ui_shuffle(self, shuffle=False):
38+
self.schema["ui"]["shuffle"] = shuffle
39+
40+
def set_defaults(self, name):
41+
self._ReproschemaSchema__set_defaults(name) # this looks wrong
42+
self.set_landing_page("../../README-en.md")
43+
self.set_ui_allow()
44+
self.set_ui_shuffle(False)
45+
46+
def append_activity(self, activity):
47+
48+
# TODO
49+
# - remove the hard coding on visibility and valueRequired
50+
51+
# update the content of the protocol with this new activity
52+
append_to_protocol = {
53+
"variableName": activity.get_name(),
54+
"isAbout": activity.get_URI(),
55+
"prefLabel": {"en": activity.schema["prefLabel"]},
56+
"isVis": True,
57+
"valueRequired": False,
58+
}
59+
60+
self.schema["ui"]["order"].append(activity.URI)
61+
self.schema["ui"]["addProperties"].append(append_to_protocol)
62+
63+
def sort(self):
64+
schema_order = [
65+
"@context",
66+
"@type",
67+
"@id",
68+
"prefLabel",
69+
"description",
70+
"schemaVersion",
71+
"version",
72+
"landingPage",
73+
"ui",
74+
]
75+
self.sort_schema(schema_order)
76+
77+
ui_order = ["allow", "shuffle", "order", "addProperties"]
78+
self.sort_ui(ui_order)

reproschema/models/tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)