diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index dc9cc3f..85e661b 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: os: [macos-latest, ubuntu-18.04] - python-version: [3.7, 3.8] + python-version: [3.7, 3.8, 3.9] steps: - uses: actions/checkout@v2 diff --git a/reproschema/cli.py b/reproschema/cli.py index 3683610..adbf509 100644 --- a/reproschema/cli.py +++ b/reproschema/cli.py @@ -57,13 +57,19 @@ def validate(shapefile, path): default="n-triples", show_default=True, ) +@click.option( + "--prefixfile", default=None, type=click.Path(exists=True, dir_okay=False) +) +@click.option( + "--contextfile", default=None, type=click.Path(exists=True, dir_okay=False) +) @click.argument("path", nargs=1, type=str) -def convert(path, format): +def convert(path, format, prefixfile, contextfile): if not (path.startswith("http") or os.path.exists(path)): raise ValueError(f"{path} must be a URL or an existing file or directory") from .jsonldutils import to_newformat - print(to_newformat(path, format)) + print(to_newformat(path, format, prefixfile, contextfile)) @main.command() diff --git a/reproschema/jsonldutils.py b/reproschema/jsonldutils.py index b8c21c6..52048d7 100644 --- a/reproschema/jsonldutils.py +++ b/reproschema/jsonldutils.py @@ -75,7 +75,7 @@ def validate_data(data, shape_file_path): return conforms, v_text -def to_newformat(path, format): +def to_newformat(path, format, prefixfile=None, contextfile=None): """Convert a JSONLD document to n-triples format Since PyLD requires an http url, a local server is started to serve the @@ -87,6 +87,11 @@ def to_newformat(path, format): A local path or remote url to convert to n-triples format: str of enum Returned format jsonld, n-triples, turtle + prefixfile: str + Prefixes to use when converting to turtle (ignored otherwise) + contextfile: str + Context to use for compaction when returning jsonld. If not provided, + a jsonld graph is returned. Returns ------- @@ -99,6 +104,10 @@ def to_newformat(path, format): raise ValueError(f"{format} not in {supported_formats}") data = load_file(path) if format == "jsonld": + if contextfile is not None: + with open(contextfile) as fp: + context = json.load(fp) + data = jsonld.compact(data, context) return json.dumps(data, indent=2) kwargs = {"algorithm": "URDNA2015", "format": "application/n-quads"} nt = jsonld.normalize(data, kwargs) @@ -112,5 +121,10 @@ def to_newformat(path, format): g.bind("nidm", "http://purl.org/nidash/nidm#") g.bind("skos", "http://www.w3.org/2004/02/skos/core#") g.bind("prov", "http://www.w3.org/ns/prov#") + if prefixfile is not None: + with open(prefixfile) as fp: + prefixes = json.load(fp) + for key, value in prefixes.items(): + g.bind(key, value) g.parse(data=nt, format="nt") return g.serialize(format=format).decode() diff --git a/reproschema/models/README.md b/reproschema/models/README.md new file mode 100644 index 0000000..48d833f --- /dev/null +++ b/reproschema/models/README.md @@ -0,0 +1,8 @@ +# README + +## reproschema file creation + +The creation reproschema protocols, activities and items (or Field) are handled +by 3 python classes with the same names contained in their dedicated modules. + +They all inherit from the same base class. diff --git a/reproschema/models/activity.py b/reproschema/models/activity.py index 25d27d2..f7cc7cb 100644 --- a/reproschema/models/activity.py +++ b/reproschema/models/activity.py @@ -1,5 +1,7 @@ from .base import SchemaBase +DEFAULT_LANG = "en" + class Activity(SchemaBase): """ @@ -8,47 +10,41 @@ class to deal with reproschema activities schema_type = "reproschema:Activity" + visible = True + required = False + skippable = True + def __init__(self, version=None): super().__init__(version) - self.schema["ui"] = {"shuffle": [], "order": [], "addProperties": []} - - def set_ui_shuffle(self, shuffle=False): - self.schema["ui"]["shuffle"] = shuffle - - def set_URI(self, URI): - self.URI = URI - - def get_URI(self): - return self.URI - - # TODO - # preamble - # compute - # citation - # image - def set_defaults(self, name): - self._ReproschemaSchema__set_defaults(name) # this looks wrong - self.set_ui_shuffle(False) + def set_defaults(self, name="default"): + self._SchemaBase__set_defaults(name) + self.set_preamble() + self.set_ui_default() - def update_activity(self, item_info): - - # TODO - # - remove the hard coding on visibility and valueRequired - - # update the content of the activity schema with new item + def set_compute(self, variable, expression): + self.schema["compute"] = [ + {"variableName": variable, "jsExpression": expression} + ] - item_info["URI"] = "items/" + item_info["name"] + def append_item(self, item): + # See comment and TODO of the append_activity Protocol class - append_to_activity = { - "variableName": item_info["name"], - "isAbout": item_info["URI"], - "isVis": item_info["visibility"], - "valueRequired": False, + property = { + "variableName": item.get_basename(), + "isAbout": item.get_URI(), + "isVis": item.visible, + "requiredValue": item.required, } + if item.skippable: + property["allow"] = ["reproschema:Skipped"] - self.schema["ui"]["order"].append(item_info["URI"]) - self.schema["ui"]["addProperties"].append(append_to_activity) + self.schema["ui"]["order"].append(item.get_URI()) + self.schema["ui"]["addProperties"].append(property) + + """ + writing, reading, sorting, unsetting + """ def sort(self): schema_order = [ @@ -59,9 +55,15 @@ def sort(self): "description", "schemaVersion", "version", + "preamble", + "citation", + "image", + "compute", "ui", ] self.sort_schema(schema_order) + self.sort_ui() - ui_order = ["shuffle", "order", "addProperties"] - self.sort_ui(ui_order) + def write(self, output_dir): + self.sort() + self._SchemaBase__write(output_dir) diff --git a/reproschema/models/base.py b/reproschema/models/base.py index a2d59db..4cbb2bb 100644 --- a/reproschema/models/base.py +++ b/reproschema/models/base.py @@ -1,29 +1,145 @@ import json import os +from pathlib import Path +from collections import OrderedDict + +""" +For any key that can be 'translated' we set english as the default language +in case the user does not provide it. +""" +DEFAULT_LANG = "en" + +""" +""" +DEFAULT_VERSION = "1.0.0-rc4" + + +def default_context(version): + """ + For now we assume that the github repo will be where schema will be read from. + """ + URL = "https://raw.githubusercontent.com/ReproNim/reproschema/" + VERSION = version or DEFAULT_VERSION + return URL + VERSION + "/contexts/generic" class SchemaBase: """ - class to deal with reproschema schemas + base class to deal with reproschema schemas + + The content of the schema is stored in the dictionnary ``self.schema`` + + self.schema["@context"] + self.schema["@type"] + self.schema["schemaVersion"] + self.schema["version"] + self.schema["preamble"] + self.schema["citation"] + self.schema["image"] + ... + + When the output file is created, its content is simply dumped into a json + by the ``write`` method. + """ + # TODO might be more convenient to have some of the properties not centrlized in a single dictionnary + # + # Could be more practical to only create part or all of the dictionnary when write is called + # + schema_type = None def __init__(self, version): - URL = "https://raw.githubusercontent.com/ReproNim/reproschema/" - VERSION = version or "1.0.0-rc2" + # TODO the version handling could probably be refactored + VERSION = version or DEFAULT_VERSION self.schema = { - "@context": URL + VERSION + "/contexts/generic", "@type": self.schema_type, "schemaVersion": VERSION, "version": "0.0.1", } - def set_filename(self, name): - self.schema_file = name + "_schema" - self.schema["@id"] = name + "_schema" + URL = self.get_default_context(version) + self.set_context(URL) + + # This probably needs some cleaning but is at the moment necessary to pass + # the context to the ResponseOption class + def get_default_context(self, version): + return default_context(version) + + def __set_defaults(self, name): + self.set_filename(name) + self.set_directory(name) + "We use the ``name`` of this class instance for schema keys minus some underscore" + self.set_pref_label(name.replace("_", " ")) + self.set_description(name.replace("_", " ")) + + """ + setters + """ + + def set_directory(self, output_directory): + """ + Where the file will be written by the ``write`` method + """ + self.dir = output_directory + + def set_URI(self, URI): + """ + In case we need to keep where the output file is located, + we can set it with this method. + This can be useful if we have just read or created an item + and want to add it to an activity. + """ + self.URI = URI + + """ + schema related setters + + use them to set several of the common keys of the schema + """ + + def set_context(self, context): + """ + In case we want to overide the default context + """ + self.schema["@context"] = context + + def set_preamble(self, preamble="", lang=DEFAULT_LANG): + self.schema["preamble"] = {lang: preamble} + + def set_citation(self, citation): + self.schema["citation"] = citation + + def set_image(self, image): + self.schema["image"] = image + + def set_filename(self, name, ext=".jsonld"): + """ + By default all files are given: + - the ``.jsold`` extension + - have ``_schema`` suffix appended to them + + For item files their name won't have the schema prefix. + """ + # TODO figure out if the latter is a desirable behavior + self.schema_file = name + "_schema" + ext + self.schema["@id"] = name + "_schema" + ext + + def set_pref_label(self, pref_label, lang=DEFAULT_LANG): + self.schema["prefLabel"] = {lang: pref_label} + + def set_description(self, description): + self.schema["description"] = description + + """ + getters + + to access the content of some of the keys of the schema + or some of the instance properties + """ def get_name(self): return self.schema_file.replace("_schema", "") @@ -31,32 +147,90 @@ def get_name(self): def get_filename(self): return self.schema_file - def set_pref_label(self, pref_label): - self.schema["prefLabel"] = pref_label + def get_basename(self): + return Path(self.schema_file).stem - def set_description(self, description): - self.schema["description"] = description + def get_pref_label(self): + return self.schema["prefLabel"] - def set_directory(self, output_directory): - self.dir = output_directory + def get_URI(self): + return self.URI - def __set_defaults(self, name): - self.set_filename(name) - self.set_directory(name) - self.set_pref_label(name.replace("_", " ")) - self.set_description(name.replace("_", " ")) + """ + UI: setters specific to the user interface keys of the schema - def sort_schema(self, schema_order): + The ui has its own dictionnary. - reordered_dict = {k: self.schema[k] for k in schema_order} - self.schema = reordered_dict + Mostly used by the Protocol and Activity class. - def sort_ui(self, ui_order): + """ + + def set_ui_default(self): + self.schema["ui"] = { + "shuffle": [], + "order": [], + "addProperties": [], + "allow": [], + } + self.set_ui_shuffle() + self.set_ui_allow() + + def set_ui_shuffle(self, shuffle=False): + self.schema["ui"]["shuffle"] = shuffle + + def set_ui_allow(self, auto_advance=True, allow_export=True, disable_back=False): + # TODO + # Could be more convenient to have one method for each property + # + # Also currently the way this is handled makes it hard to update a single value: + # all 3 have to be reset everytime!!! + # + # Could be useful to have a UI class with dictionnary content that is only + # generated when the file is written + allow = [] + if auto_advance: + allow.append("reproschema:AutoAdvance") + if allow_export: + allow.append("reproschema:AllowExport") + if disable_back: + allow.append("reproschema:DisableBack") + self.schema["ui"]["allow"] = allow + + """ + writing, reading, sorting, unsetting + + Editing and appending things to the dictionnary tends to give json output + that is not standardized: the @context can end up at the bottom for one file + and stay at the top for another. + So there are a couple of sorting methods to rearrange the keys of + the different dictionaries and those are called right before writing the jsonld file. + + Those methods enforces a certain order or keys in the output and + also remove any empty or unknown keys. + """ + + # TODO allow for unknown keys to be added because otherwise adding new fields in the json always + # forces you to go and change those `schema_order` and `ui_order` otherwise they will not be added. + # + # This will require modifying the helper function `reorder_dict_skip_missing` + # + + def sort_schema(self, schema_order): + """ + The ``schema_order`` is specific to each "level" of the reproschema + (protocol, activity, item) so that each can be reordered in its own way + """ + reordered_dict = reorder_dict_skip_missing(self.schema, schema_order) + self.schema = reordered_dict - reordered_dict = {k: self.schema["ui"][k] for k in ui_order} + def sort_ui(self, ui_order=["shuffle", "order", "addProperties", "allow"]): + reordered_dict = reorder_dict_skip_missing(self.schema["ui"], ui_order) self.schema["ui"] = reordered_dict - def write(self, output_dir): + def __write(self, output_dir): + """ + Reused by the write method of the children classes + """ with open(os.path.join(output_dir, self.schema_file), "w") as ff: json.dump(self.schema, ff, sort_keys=False, indent=4) @@ -77,3 +251,12 @@ def from_file(cls, filepath): if "@type" not in data: raise ValueError("Missing @type key") return cls.from_data(data) + + +def reorder_dict_skip_missing(old_dict, key_list): + """ + reorders dictionary according to ``key_list`` + removing any key with no associated value + or that is not in the key list + """ + return OrderedDict((k, old_dict[k]) for k in key_list if k in old_dict) diff --git a/reproschema/models/item.py b/reproschema/models/item.py index 595d67d..b719c83 100644 --- a/reproschema/models/item.py +++ b/reproschema/models/item.py @@ -1,146 +1,257 @@ from .base import SchemaBase +DEFAULT_LANG = "en" + + class Item(SchemaBase): """ - class to deal with reproschema activities + class to deal with reproschema items """ schema_type = "reproschema:Field" + visible = True + required = False + skippable = True def __init__(self, version=None): super().__init__(version) self.schema["ui"] = {"inputType": []} self.schema["question"] = {} + """ + The responseOptions dictionnary is kept empty until the file has to be written + then it gets its content wit the method `set_response_options` + from the `options` dictionnary of an instance of the ResponseOptions class that is kept in + ``self.response_options`` + """ self.schema["responseOptions"] = {} - # default input type is "char" - self.set_input_type_as_char() + self.response_options = ResponseOption() + + # default input type is "text" + self.set_input_type_as_text() + + def set_defaults(self, name="default"): + self._SchemaBase__set_defaults(name) + self.set_filename(name) + self.set_input_type_as_text() + + def set_filename(self, name, ext=".jsonld"): + """ + Note there is no _schema suffix for items names + """ + name = name.replace(" ", "_") + self.schema_file = name + ext + self.schema["@id"] = name + ext + + def set_question(self, question, lang=DEFAULT_LANG): + # TODO add test to check adding several questions to an item + self.schema["question"][lang] = question - def set_URI(self, URI): - self.URI = URI + """ + CREATE DIFFERENT ITEMS + """ + # TODO: items not yet covered + # audioCheck: AudioCheck/AudioCheck.vue + # audioRecord: WebAudioRecord/Audio.vue + # audioPassageRecord: WebAudioRecord/Audio.vue + # audioImageRecord: WebAudioRecord/Audio.vue + # audioRecordNumberTask: WebAudioRecord/Audio.vue + # audioAutoRecord: AudioCheckRecord/AudioCheckRecord.vue + # documentUpload: DocumentUpload/DocumentUpload.vue + # save: SaveData/SaveData.vue + # static: Static/Static.vue + # StaticReadOnly: Static/Static.vue - # TODO - # image - # readonlyValue + def set_basic_response_type(self, response_type): + """ + Handles the dispatching to other methods for specific item creations + Does not cover all items types (maybe it should as this would help + from an API point of view to pass everything through this function) + The default is "text" input type + """ + self.set_input_type_as_text() - def set_defaults(self, name): - self._ReproschemaSchema__set_defaults(name) # this looks wrong - self.schema_file = name - self.schema["@id"] = name - self.set_input_type_as_char() + if response_type == "int": + self.set_input_type_as_int() - def set_question(self, question, lang="en"): - self.schema["question"][lang] = question + elif response_type == "float": + self.set_input_type_as_float() - def set_input_type(self, input_type): - self.schema["ui"]["inputType"] = input_type + elif response_type == "date": + self.set_input_type_as_date() - def set_response_options(self, response_options): - self.schema["responseOptions"] = response_options + elif response_type == "time range": + self.set_input_type_as_time_range() - """ + elif response_type == "language": + self.set_input_type_as_language() + """ input types with different response choices + For many items it is necessary to call + + self.response_options.unset + + To remove unecessary or unwanted keys from the response_options + dictionary. + Many of those are put there by the constructor of that set + the default input type as ``self.set_input_type_as_text()`` + so it might be better to maybe have a more minimalistic constructor. + """ - def set_input_type_as_radio(self, response_options): - self.set_input_type("radio") - self.set_response_options(response_options) + def set_input_type_as_int(self): + self.set_input_type("number") + self.response_options.set_type("integer") + self.response_options.unset(["maxLength"]) - def set_input_type_as_select(self, response_options): - self.set_input_type("select") - self.set_response_options(response_options) + def set_input_type_as_float(self): + self.set_input_type("float") + self.response_options.set_type("float") + self.response_options.unset(["maxLength"]) - def set_input_type_as_slider(self): - self.set_input_type_as_char() # until the slide item of the ui is fixed - # self.set_input_type("slider") - # self.set_response_options({"valueType": "xsd:string"}) + def set_input_type_as_date(self): + self.set_input_type("date") + self.response_options.unset(["maxLength"]) + self.response_options.set_type("date") + + def set_input_type_as_time_range(self): + self.set_input_type("timeRange") + self.response_options.unset(["maxLength"]) + self.response_options.set_type("datetime") + + def set_input_type_as_year(self): + self.set_input_type("year") + self.response_options.unset(["maxLength"]) + self.response_options.set_type("date") + + """ + input types with preset response choices + """ def set_input_type_as_language(self): - URL = "https://raw.githubusercontent.com/ReproNim/reproschema/" + URL = "https://raw.githubusercontent.com/ReproNim/reproschema-library/" self.set_input_type("selectLanguage") - response_options = { - "valueType": "xsd:string", - "multipleChoice": True, - "choices": URL + "master/resources/languages.json", - } - self.set_response_options(response_options) + self.response_options.set_type("string") + self.response_options.set_multiple_choice(True) + self.response_options.use_preset(URL + "master/resources/languages.json") + self.response_options.unset(["maxLength"]) - """ + def set_input_type_as_country(self): - input types with no response choice + URL = "https://raw.githubusercontent.com/samayo/country-json/master/src/country-by-name.json" - """ + self.set_input_type("selectCountry") - def set_input_type_as_char(self): - self.set_input_type("text") - self.set_response_options({"valueType": "xsd:string"}) + self.response_options.set_type("string") + self.response_options.use_preset(URL) + self.response_options.set_length(50) - def set_input_type_as_int(self): - self.set_input_type("number") - self.set_response_options({"valueType": "xsd:integer"}) + def set_input_type_as_state(self): - def set_input_type_as_float(self): - self.set_input_type("float") - self.set_response_options({"valueType": "xsd:float"}) + URL = "https://gist.githubusercontent.com/mshafrir/2646763/raw/8b0dbb93521f5d6889502305335104218454c2bf/states_hash.json" - def set_input_type_as_time_range(self): - self.set_input_type("timeRange") - self.set_response_options({"valueType": "datetime"}) + self.set_input_type("selectState") - def set_input_type_as_date(self): - self.set_input_type("date") - self.set_response_options({"valueType": "xsd:date"}) + self.response_options.set_type("string") + self.response_options.use_preset(URL) + self.response_options.unset(["maxLength"]) """ + input types requiring user typed input + """ - input types with no response choice but with some parameters + def set_input_type_as_text(self, length=300): + self.set_input_type("text") + self.response_options.set_type("string") + self.response_options.set_length(length) + self.response_options.unset( + ["maxValue", "minValue", "multipleChoice", "choices"] + ) + + def set_input_type_as_multitext(self, length=300): + self.set_input_type("multitext") + self.response_options.set_type("string") + self.response_options.set_length(length) + + def set_input_type_as_email(self): + self.set_input_type("email") + self.response_options.unset(["maxLength"]) + + def set_input_type_as_id(self): + """ + for participant id items + """ + self.set_input_type("pid") + self.response_options.unset(["maxLength"]) """ + input types with 'different response choices' - def set_input_type_as_multitext(self, max_length=300): - self.set_input_type("text") - self.set_response_options({"valueType": "xsd:string", "maxLength": max_length}) + Those methods require an instance of ResponseOptions as input and + it will replace the one initialized in the construction. - # TODO - # email: EmailInput/EmailInput.vue - # audioCheck: AudioCheck/AudioCheck.vue - # audioRecord: WebAudioRecord/Audio.vue - # audioPassageRecord: WebAudioRecord/Audio.vue - # audioImageRecord: WebAudioRecord/Audio.vue - # audioRecordNumberTask: WebAudioRecord/Audio.vue - # audioAutoRecord: AudioCheckRecord/AudioCheckRecord.vue - # year: YearInput/YearInput.vue - # selectCountry: SelectInput/SelectInput.vue - # selectState: SelectInput/SelectInput.vue - # documentUpload: DocumentUpload/DocumentUpload.vue - # save: SaveData/SaveData.vue - # static: Static/Static.vue - # StaticReadOnly: Static/Static.vue + Most likely a bad idea and a confusing API from the user perpective: + probably better to set the input type and then let the user construct + the response choices via calls to the methods of - def set_basic_response_type(self, response_type): + self.response_options + """ - # default (also valid for "char" input type) - self.set_input_type_as_char() + def set_input_type_as_radio(self, response_options): + self.set_input_type("radio") + response_options.set_type("integer") + self.response_options = response_options - if response_type == "int": - self.set_input_type_as_int() + def set_input_type_as_select(self, response_options): + self.set_input_type("select") + response_options.set_type("integer") + self.response_options = response_options - elif response_type == "float": - self.set_input_type_as_float() + def set_input_type_as_slider(self, response_options): + self.set_input_type("slider") + response_options.set_type("integer") + self.response_options = response_options - elif response_type == "date": - self.set_input_type_as_date() + """ + UI + """ + # are input_type and read_only specific properties to items + # or should they be brought up into the base class? + # or be made part of an UI class? - elif response_type == "time range": - self.set_input_type_as_time_range() + def set_input_type(self, input_type): + self.schema["ui"]["inputType"] = input_type - elif response_type == "language": - self.set_input_type_as_language() + def set_read_only_value(self, value): + self.schema["ui"]["readonlyValue"] = value + + """ + writing, reading, sorting, unsetting + """ + + def set_response_options(self): + """ + Passes the content of the response options to the schema of the item. + To be done before writing the item + """ + self.schema["responseOptions"] = self.response_options.options + + def unset(self, keys): + """ + Mostly used to remove some empty keys from the schema. Rarely used. + """ + for i in keys: + self.schema.pop(i, None) + + def write(self, output_dir): + self.sort() + self.set_response_options() + self._SchemaBase__write(output_dir) def sort(self): schema_order = [ @@ -156,3 +267,107 @@ def sort(self): "responseOptions", ] self.sort_schema(schema_order) + + +class ResponseOption(SchemaBase): + """ + class to deal with reproschema response options + """ + + # TODO + # the dictionnary that keeps track of the content of the response options should + # be called "schema" and not "options" so as to be able to make proper use of the + # methods of the parent class and avoid copying content between + # + # self.options and self.schema + + schema_type = "reproschema:ResponseOption" + + def __init__(self): + self.options = { + "valueType": "", + "minValue": 0, + "maxValue": 0, + "choices": [], + "multipleChoice": False, + } + + def set_defaults(self, name="valueConstraints", version=None): + super().__init__(version) + self.options["@context"] = self.schema["@context"] + self.options["@type"] = self.schema_type + self.set_filename(name) + + def set_filename(self, name, ext=".jsonld"): + name = name.replace(" ", "_") + self.schema_file = name + ext + self.options["@id"] = name + ext + + def unset(self, keys): + if type(keys) == str: + keys = [keys] + for i in keys: + self.options.pop(i, None) + + def set_type(self, type): + self.options["valueType"] = "xsd:" + type + + # TODO a nice thing to do would be to read the min and max value + # from the rest of the content of self.options + # could avoid having the user to input those + def set_min(self, value): + self.options["minValue"] = value + + def set_max(self, value): + self.options["maxValue"] = value + + def set_length(self, value): + self.options["maxLength"] = value + + def set_multiple_choice(self, value): + self.options["multipleChoice"] = value + + def use_preset(self, URI): + """ + In case the list response options are read from another file + like for languages, country, state... + """ + self.options["choices"] = URI + + def add_choice(self, choice, value, lang=DEFAULT_LANG): + self.options["choices"].append({"name": {lang: choice}, "value": value}) + + def sort(self): + options_order = [ + "@context", + "@type", + "@id", + "valueType", + "minValue", + "maxValue", + "multipleChoice", + "choices", + ] + reordered_dict = reorder_dict_skip_missing(self.options, options_order) + self.options = reordered_dict + + def write(self, output_dir): + self.sort() + self.schema = self.options + self._SchemaBase__write(output_dir) + + +# TODO +# DUPLICATE from the base class to be used for ResponseOptions sorting of the options +# needs refactoring that will be made easier if the name the ResponseOptions dictionary is +# schema and note options +from collections import OrderedDict + + +def reorder_dict_skip_missing(old_dict, key_list): + """ + reorders dictionary according to ``key_list`` + removing any key with no associated value + or that is not in the key list + """ + return OrderedDict((k, old_dict[k]) for k in key_list if k in old_dict) diff --git a/reproschema/models/protocol.py b/reproschema/models/protocol.py index c630914..dded1cd 100644 --- a/reproschema/models/protocol.py +++ b/reproschema/models/protocol.py @@ -1,5 +1,7 @@ from .base import SchemaBase +DEFAULT_LANG = "en" + class Protocol(SchemaBase): """ @@ -9,56 +11,58 @@ class to deal with reproschema protocols schema_type = "reproschema:Protocol" def __init__(self, version=None): + """ + Rely on the parent class for construction of the instance + """ super().__init__(version) - self.schema["ui"] = { - "allow": [], - "shuffle": [], - "order": [], - "addProperties": [], - } - - def set_landing_page(self, landing_page_url, lang="en"): - self.schema["landingPage"] = {"@id": landing_page_url, "@language": lang} - # TODO - # def add_landing_page(self, landing_page_url, lang="en"): - # preamble - # compute + def set_defaults(self, name="default"): + self._SchemaBase__set_defaults(name) + self.set_landing_page("README-en.md") + # does it make sense to give a preamble by default to protocols since + # they already have a landing page? + self.set_preamble() + self.set_ui_default() - def set_image(self, image_url): - self.schema["image"] = image_url + def set_landing_page(self, landing_page_uri, lang=DEFAULT_LANG): + self.schema["landingPage"] = {"@id": landing_page_uri, "inLanguage": lang} - def set_ui_allow(self): - self.schema["ui"]["allow"] = [ - "reproschema:AutoAdvance", - "reproschema:AllowExport", - ] + def append_activity(self, activity): + """ + We get from an activity instance the info we need to update the protocol scheme. - def set_ui_shuffle(self, shuffle=False): - self.schema["ui"]["shuffle"] = shuffle + This appends the activity after all the other ones. - def set_defaults(self, name): - self._ReproschemaSchema__set_defaults(name) # this looks wrong - self.set_landing_page("../../README-en.md") - self.set_ui_allow() - self.set_ui_shuffle(False) - - def append_activity(self, activity): + So this means the order of the activities will be dependent + on the order in which they are "read". + This implementation assumes that the activities are read + from a list and added one after the other. + """ # TODO - # - remove the hard coding on visibility and valueRequired - - # update the content of the protocol with this new activity - append_to_protocol = { - "variableName": activity.get_name(), + # - find a way to reorder, remove or add an activity + # at any point in the protocol + # - this method is nearly identical to the append_item method of Activity + # and should probably be refactored into a single method of the parent class + # and ideally into a method of a yet to be created UI class + + property = { + # variable name is name of activity without prefix + "variableName": activity.get_basename().replace("_schema", ""), "isAbout": activity.get_URI(), - "prefLabel": {"en": activity.schema["prefLabel"]}, - "isVis": True, - "valueRequired": False, + "prefLabel": activity.get_pref_label(), + "isVis": activity.visible, + "requiredValue": activity.required, } + if activity.skippable: + property["allow"] = ["reproschema:Skipped"] + + self.schema["ui"]["order"].append(activity.get_URI()) + self.schema["ui"]["addProperties"].append(property) - self.schema["ui"]["order"].append(activity.URI) - self.schema["ui"]["addProperties"].append(append_to_protocol) + """ + writing, reading, sorting, unsetting + """ def sort(self): schema_order = [ @@ -70,9 +74,15 @@ def sort(self): "schemaVersion", "version", "landingPage", + "preamble", + "citation", + "image", + "compute", "ui", ] self.sort_schema(schema_order) + self.sort_ui() - ui_order = ["allow", "shuffle", "order", "addProperties"] - self.sort_ui(ui_order) + def write(self, output_dir): + self.sort() + self._SchemaBase__write(output_dir) diff --git a/reproschema/models/tests/.gitignore b/reproschema/models/tests/.gitignore new file mode 100644 index 0000000..2f9a1e0 --- /dev/null +++ b/reproschema/models/tests/.gitignore @@ -0,0 +1,4 @@ +items/*.jsonld +activities/*.jsonld +response_options/*.jsonld +protocols/*.jsonld diff --git a/reproschema/models/tests/README.md b/reproschema/models/tests/README.md new file mode 100644 index 0000000..0ef4b7c --- /dev/null +++ b/reproschema/models/tests/README.md @@ -0,0 +1,48 @@ +# Tests for reproschema-py "models" + +## Philosophy + +Most of the test are trying to be step by step "recipes" to create the different +files in a schema. + +Each test tries to create an item from 'scratch' by using the `Protocol`, +`Activity`, `Item` and `ResponseOptions` classes and writes the resulting +`.jsonld` to the disk. + +The file is then read and its content compared to the "expected file" in the +`data` folder. + +When testing the Protocol and Activity classes, the output tries to get very +very close to the `jsonld` found in: + +``` +reproschema/tests/data/activities/items/activity1_total_score +``` + +Ideally this would avoided having 2 sets of `.jsonld` to test against. + +## Running the tests + +Requires `pytest` you can `pip install`. + +If you are developping the code, also make sure you have installed the +reproschema package locally and not from pypi. + +Run this from the root folder of where you cloned the reproschema package: + +``` +pip install -e . +``` + +More [here](../../README.md) + +## TODO + +- a lot of repeats in the test code base can be refactored + - especially for `test_items.py` a lot of those tests can be parametrized + (since apparently pytest allows this). + - the "clean up" after each passed test could be handle by a pytest fixture + - the helper functions are also nearly identical in all 3 test modules and + should be refactored + - some of the methods of the base class should probably be test on their own + rather than having tests for the sub classes that also "test" them. diff --git a/reproschema/models/tests/data/activities/activity1_schema.jsonld b/reproschema/models/tests/data/activities/activity1_schema.jsonld new file mode 100644 index 0000000..e3c4aee --- /dev/null +++ b/reproschema/models/tests/data/activities/activity1_schema.jsonld @@ -0,0 +1,60 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Activity", + "@id": "activity1_schema.jsonld", + "prefLabel": { + "en": "Example 1" + }, + "description": "Activity example 1", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "citation": "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC1495268/", + "image": { + "@type": "AudioObject", + "contentUrl": "http://example.com/sample-image.png" + }, + "preamble": { + "en": "Over the last 2 weeks, how often have you been bothered by any of the following problems?" + }, + "compute": [ + { + "variableName": "activity1_total_score", + "jsExpression": "item1 + item2" + } + ], + "ui": { + "addProperties": [ + { + "isAbout": "items/item1.jsonld", + "variableName": "item1", + "requiredValue": true, + "isVis": true + }, + { + "variableName": "item_two", + "isAbout": "../other_dir/item_two.jsonld", + "requiredValue": true, + "isVis": true, + "allow": [ + "reproschema:Skipped" + ] + }, + { + "isAbout": "items/activity1_total_score", + "variableName": "activity1_total_score", + "requiredValue": true, + "isVis": false + } + ], + "order": [ + "items/item1.jsonld", + "../other_dir/item_two.jsonld", + "items/activity1_total_score" + ], + "shuffle": false, + "allow": [ + "reproschema:AutoAdvance", + "reproschema:AllowExport" + ] + } +} diff --git a/reproschema/models/tests/data/activities/default_schema.jsonld b/reproschema/models/tests/data/activities/default_schema.jsonld new file mode 100644 index 0000000..9efd947 --- /dev/null +++ b/reproschema/models/tests/data/activities/default_schema.jsonld @@ -0,0 +1,23 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Activity", + "@id": "default_schema.jsonld", + "prefLabel": { + "en": "default" + }, + "description": "default", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "preamble": { + "en": "" + }, + "ui": { + "shuffle": false, + "order": [], + "addProperties": [], + "allow": [ + "reproschema:AutoAdvance", + "reproschema:AllowExport" + ] + } +} diff --git a/reproschema/models/tests/data/items/country.jsonld b/reproschema/models/tests/data/items/country.jsonld new file mode 100644 index 0000000..15c0677 --- /dev/null +++ b/reproschema/models/tests/data/items/country.jsonld @@ -0,0 +1,22 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "country.jsonld", + "prefLabel": { + "en": "country" + }, + "description": "country", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "question": { + "en": "select a country" + }, + "ui": { + "inputType": "selectCountry" + }, + "responseOptions": { + "valueType": "xsd:string", + "maxLength": 50, + "choices": "https://raw.githubusercontent.com/samayo/country-json/master/src/country-by-name.json" + } +} diff --git a/reproschema/models/tests/data/items/date.jsonld b/reproschema/models/tests/data/items/date.jsonld new file mode 100644 index 0000000..85efd8f --- /dev/null +++ b/reproschema/models/tests/data/items/date.jsonld @@ -0,0 +1,20 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "date.jsonld", + "prefLabel": { + "en": "date" + }, + "description": "date", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "question": { + "en": "input a date" + }, + "ui": { + "inputType": "date" + }, + "responseOptions": { + "valueType": "xsd:date" + } +} diff --git a/reproschema/models/tests/data/items/default.jsonld b/reproschema/models/tests/data/items/default.jsonld new file mode 100644 index 0000000..f4bbbfe --- /dev/null +++ b/reproschema/models/tests/data/items/default.jsonld @@ -0,0 +1,19 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "default.jsonld", + "prefLabel": { + "en": "default" + }, + "description": "default", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "ui": { + "inputType": "text" + }, + "question": {}, + "responseOptions": { + "valueType": "xsd:string", + "maxLength": 300 + } +} diff --git a/reproschema/models/tests/data/items/email.jsonld b/reproschema/models/tests/data/items/email.jsonld new file mode 100644 index 0000000..1d4540e --- /dev/null +++ b/reproschema/models/tests/data/items/email.jsonld @@ -0,0 +1,20 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "email.jsonld", + "prefLabel": { + "en": "email" + }, + "description": "email", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "question": { + "en": "input email address" + }, + "ui": { + "inputType": "email" + }, + "responseOptions": { + "valueType": "xsd:string" + } +} diff --git a/reproschema/models/tests/data/items/float.jsonld b/reproschema/models/tests/data/items/float.jsonld new file mode 100644 index 0000000..e7200b5 --- /dev/null +++ b/reproschema/models/tests/data/items/float.jsonld @@ -0,0 +1,20 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "float.jsonld", + "prefLabel": { + "en": "float" + }, + "description": "This is a float item.", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "question": { + "en": "This is an item where the user can input a float." + }, + "ui": { + "inputType": "float" + }, + "responseOptions": { + "valueType": "xsd:float" + } +} diff --git a/reproschema/models/tests/data/items/integer.jsonld b/reproschema/models/tests/data/items/integer.jsonld new file mode 100644 index 0000000..b184085 --- /dev/null +++ b/reproschema/models/tests/data/items/integer.jsonld @@ -0,0 +1,20 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "integer.jsonld", + "prefLabel": { + "en": "integer" + }, + "description": "This is a integer item.", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "question": { + "en": "This is an item where the user can input a integer." + }, + "ui": { + "inputType": "number" + }, + "responseOptions": { + "valueType": "xsd:integer" + } +} diff --git a/reproschema/models/tests/data/items/language.jsonld b/reproschema/models/tests/data/items/language.jsonld new file mode 100644 index 0000000..71a0dac --- /dev/null +++ b/reproschema/models/tests/data/items/language.jsonld @@ -0,0 +1,22 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "language.jsonld", + "prefLabel": { + "en": "language" + }, + "description": "language", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "question": { + "en": "This is an item where the user can select several language." + }, + "ui": { + "inputType": "selectLanguage" + }, + "responseOptions": { + "valueType": "xsd:string", + "multipleChoice": true, + "choices": "https://raw.githubusercontent.com/ReproNim/reproschema-library/master/resources/languages.json" + } +} diff --git a/reproschema/models/tests/data/items/multitext.jsonld b/reproschema/models/tests/data/items/multitext.jsonld new file mode 100644 index 0000000..04329b9 --- /dev/null +++ b/reproschema/models/tests/data/items/multitext.jsonld @@ -0,0 +1,21 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "multitext.jsonld", + "prefLabel": { + "en": "multitext" + }, + "description": "multitext", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "question": { + "en": "This is an item where the user can input several text field." + }, + "ui": { + "inputType": "multitext" + }, + "responseOptions": { + "valueType": "xsd:string", + "maxLength": 50 + } +} diff --git a/reproschema/models/tests/data/items/participant_id.jsonld b/reproschema/models/tests/data/items/participant_id.jsonld new file mode 100644 index 0000000..3d3ad12 --- /dev/null +++ b/reproschema/models/tests/data/items/participant_id.jsonld @@ -0,0 +1,20 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "participant_id.jsonld", + "prefLabel": { + "en": "participant id" + }, + "description": "participant id", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "question": { + "en": "input the participant id number" + }, + "ui": { + "inputType": "pid" + }, + "responseOptions": { + "valueType": "xsd:string" + } +} diff --git a/reproschema/models/tests/data/items/radio.jsonld b/reproschema/models/tests/data/items/radio.jsonld new file mode 100644 index 0000000..5ead18c --- /dev/null +++ b/reproschema/models/tests/data/items/radio.jsonld @@ -0,0 +1,37 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "radio.jsonld", + "prefLabel": { + "en": "radio" + }, + "description": "radio", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "question": { + "en": "question for radio item" + }, + "ui": { + "inputType": "radio" + }, + "responseOptions": { + "valueType": "xsd:integer", + "minValue": 0, + "maxValue": 1, + "multipleChoice": false, + "choices": [ + { + "name": { + "en": "Not at all" + }, + "value": 0 + }, + { + "name": { + "en": "Several days" + }, + "value": 1 + } + ] + } +} diff --git a/reproschema/models/tests/data/items/radio_multiple.jsonld b/reproschema/models/tests/data/items/radio_multiple.jsonld new file mode 100644 index 0000000..694e099 --- /dev/null +++ b/reproschema/models/tests/data/items/radio_multiple.jsonld @@ -0,0 +1,37 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "radio_multiple.jsonld", + "prefLabel": { + "en": "radio multiple" + }, + "description": "radio multiple", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "question": { + "en": "question for radio item with multiple responses" + }, + "ui": { + "inputType": "radio" + }, + "responseOptions": { + "valueType": "xsd:integer", + "minValue": 0, + "maxValue": 1, + "multipleChoice": true, + "choices": [ + { + "name": { + "en": "Not at all" + }, + "value": 0 + }, + { + "name": { + "en": "Several days" + }, + "value": 1 + } + ] + } +} diff --git a/reproschema/models/tests/data/items/select.jsonld b/reproschema/models/tests/data/items/select.jsonld new file mode 100644 index 0000000..b7e63f8 --- /dev/null +++ b/reproschema/models/tests/data/items/select.jsonld @@ -0,0 +1,43 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "select.jsonld", + "prefLabel": { + "en": "select" + }, + "description": "select", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "question": { + "en": "question for select item" + }, + "ui": { + "inputType": "select" + }, + "responseOptions": { + "valueType": "xsd:integer", + "minValue": 0, + "maxValue": 2, + "multipleChoice": false, + "choices": [ + { + "name": { + "en": "Response option 1" + }, + "value": 0 + }, + { + "name": { + "en": "Response option 2" + }, + "value": 1 + }, + { + "name": { + "en": "Response option 3" + }, + "value": 2 + } + ] + } +} diff --git a/reproschema/models/tests/data/items/select_multiple.jsonld b/reproschema/models/tests/data/items/select_multiple.jsonld new file mode 100644 index 0000000..ab855ee --- /dev/null +++ b/reproschema/models/tests/data/items/select_multiple.jsonld @@ -0,0 +1,43 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "select_multiple.jsonld", + "prefLabel": { + "en": "select multiple" + }, + "description": "select multiple", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "question": { + "en": "question for select item with multiple responses" + }, + "ui": { + "inputType": "select" + }, + "responseOptions": { + "valueType": "xsd:integer", + "minValue": 0, + "maxValue": 2, + "multipleChoice": true, + "choices": [ + { + "name": { + "en": "Response option 1" + }, + "value": 0 + }, + { + "name": { + "en": "Response option 2" + }, + "value": 1 + }, + { + "name": { + "en": "Response option 3" + }, + "value": 2 + } + ] + } +} diff --git a/reproschema/models/tests/data/items/slider.jsonld b/reproschema/models/tests/data/items/slider.jsonld new file mode 100644 index 0000000..80114dd --- /dev/null +++ b/reproschema/models/tests/data/items/slider.jsonld @@ -0,0 +1,55 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "slider.jsonld", + "prefLabel": { + "en": "slider" + }, + "description": "slider", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "question": { + "en": "question for slider item" + }, + "ui": { + "inputType": "slider" + }, + "responseOptions": { + "valueType": "xsd:integer", + "minValue": 0, + "maxValue": 4, + "multipleChoice": false, + "choices": [ + { + "name": { + "en": "not at all" + }, + "value": 0 + }, + { + "name": { + "en": "a bit" + }, + "value": 1 + }, + { + "name": { + "en": "so so" + }, + "value": 2 + }, + { + "name": { + "en": "a lot" + }, + "value": 3 + }, + { + "name": { + "en": "very much" + }, + "value": 4 + } + ] + } +} diff --git a/reproschema/models/tests/data/items/state.jsonld b/reproschema/models/tests/data/items/state.jsonld new file mode 100644 index 0000000..b09c421 --- /dev/null +++ b/reproschema/models/tests/data/items/state.jsonld @@ -0,0 +1,21 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "state.jsonld", + "prefLabel": { + "en": "state" + }, + "description": "state", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "question": { + "en": "select a USA state" + }, + "ui": { + "inputType": "selectState" + }, + "responseOptions": { + "valueType": "xsd:string", + "choices": "https://gist.githubusercontent.com/mshafrir/2646763/raw/8b0dbb93521f5d6889502305335104218454c2bf/states_hash.json" + } +} diff --git a/reproschema/models/tests/data/items/text.jsonld b/reproschema/models/tests/data/items/text.jsonld new file mode 100644 index 0000000..d27c641 --- /dev/null +++ b/reproschema/models/tests/data/items/text.jsonld @@ -0,0 +1,21 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "text.jsonld", + "prefLabel": { + "en": "text" + }, + "description": "text", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "ui": { + "inputType": "text" + }, + "question": { + "en": "question for text item" + }, + "responseOptions": { + "valueType": "xsd:string", + "maxLength": 100 + } +} diff --git a/reproschema/models/tests/data/items/time_range.jsonld b/reproschema/models/tests/data/items/time_range.jsonld new file mode 100644 index 0000000..14c1350 --- /dev/null +++ b/reproschema/models/tests/data/items/time_range.jsonld @@ -0,0 +1,20 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "time_range.jsonld", + "prefLabel": { + "en": "time range" + }, + "description": "time range", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "question": { + "en": "input a time range" + }, + "ui": { + "inputType": "timeRange" + }, + "responseOptions": { + "valueType": "xsd:datetime" + } +} diff --git a/reproschema/models/tests/data/items/year.jsonld b/reproschema/models/tests/data/items/year.jsonld new file mode 100644 index 0000000..e4e3039 --- /dev/null +++ b/reproschema/models/tests/data/items/year.jsonld @@ -0,0 +1,20 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Field", + "@id": "year.jsonld", + "prefLabel": { + "en": "year" + }, + "description": "year", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "question": { + "en": "input a year" + }, + "ui": { + "inputType": "year" + }, + "responseOptions": { + "valueType": "xsd:date" + } +} diff --git a/reproschema/models/tests/data/protocols/default_schema.jsonld b/reproschema/models/tests/data/protocols/default_schema.jsonld new file mode 100644 index 0000000..7612d91 --- /dev/null +++ b/reproschema/models/tests/data/protocols/default_schema.jsonld @@ -0,0 +1,27 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Protocol", + "@id": "default_schema.jsonld", + "prefLabel": { + "en": "default" + }, + "description": "default", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "landingPage": { + "@id": "README-en.md", + "inLanguage": "en" + }, + "preamble": { + "en": "" + }, + "ui": { + "allow": [ + "reproschema:AutoAdvance", + "reproschema:AllowExport" + ], + "shuffle": false, + "order": [], + "addProperties": [] + } +} diff --git a/reproschema/models/tests/data/protocols/protocol1_schema.jsonld b/reproschema/models/tests/data/protocols/protocol1_schema.jsonld new file mode 100644 index 0000000..38426ce --- /dev/null +++ b/reproschema/models/tests/data/protocols/protocol1_schema.jsonld @@ -0,0 +1,43 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@type": "reproschema:Protocol", + "@id": "protocol1_schema.jsonld", + "prefLabel": { + "en": "Protocol1" + }, + "description": "example Protocol", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "landingPage": { + "@id": "http://example.com/sample-readme.md", + "inLanguage": "en" + }, + "preamble": { + "en": "" + }, + "ui": { + "addProperties": [ + { + "isAbout": "../activities/activity1_schema.jsonld", + "variableName": "activity1", + "prefLabel": { + "en": "Screening" + }, + "isVis": true, + "requiredValue": false, + "allow": [ + "reproschema:Skipped" + ] + } + ], + "order": [ + "../activities/activity1_schema.jsonld" + ], + "shuffle": false, + "allow": [ + "reproschema:AutoAdvance", + "reproschema:AllowExport", + "reproschema:DisableBack" + ] + } +} diff --git a/reproschema/models/tests/data/response_options/example.jsonld b/reproschema/models/tests/data/response_options/example.jsonld new file mode 100644 index 0000000..7dfe3bc --- /dev/null +++ b/reproschema/models/tests/data/response_options/example.jsonld @@ -0,0 +1,46 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@id": "example.jsonld", + "@type": "reproschema:ResponseOption", + "valueType": "xsd:integer", + "minValue": 0, + "maxValue": 6, + "choices": [ + { + "name": { + "en": "Not at all" + }, + "value": 0 + }, + { + "name": { + "en": "" + }, + "value": 1 + }, + { + "name": { + "en": "" + }, + "value": 2 + }, + { + "name": { + "en": "" + }, + "value": 3 + }, + { + "name": { + "en": "" + }, + "value": 4 + }, + { + "name": { + "en": "Completely" + }, + "value": 6 + } + ] +} diff --git a/reproschema/models/tests/data/response_options/valueConstraints.jsonld b/reproschema/models/tests/data/response_options/valueConstraints.jsonld new file mode 100644 index 0000000..b935e9a --- /dev/null +++ b/reproschema/models/tests/data/response_options/valueConstraints.jsonld @@ -0,0 +1,10 @@ +{ + "@context": "https://raw.githubusercontent.com/ReproNim/reproschema/1.0.0-rc4/contexts/generic", + "@id": "valueConstraints.jsonld", + "@type": "reproschema:ResponseOption", + "valueType": "", + "minValue": 0, + "maxValue": 0, + "choices": [], + "multipleChoice": false +} diff --git a/reproschema/models/tests/test_activity.py b/reproschema/models/tests/test_activity.py new file mode 100644 index 0000000..b4c72d0 --- /dev/null +++ b/reproschema/models/tests/test_activity.py @@ -0,0 +1,125 @@ +import os, sys, json + +from ..activity import Activity +from ..item import Item + +my_path = os.path.dirname(os.path.abspath(__file__)) + +# Left here in case Remi and python path or import can't be friends once again. +# sys.path.insert(0, my_path + "/../") + +# TODO +# refactor across the different test modules +activity_dir = os.path.join(my_path, "activities") +if not os.path.exists(activity_dir): + os.makedirs(os.path.join(activity_dir)) + +""" +Only for the few cases when we want to check against some of the files in +reproschema/tests/data +""" +reproschema_test_data = os.path.join(my_path, "..", "..", "tests", "data") + + +def test_default(): + + """ + FYI: The default activity does not conform to the schema + so `reproschema validate` will complain if you run it in this + """ + + activity = Activity() + activity.set_defaults() + + activity.write(activity_dir) + activity_content, expected = load_jsons(activity) + assert activity_content == expected + + clean_up(activity) + + +def test_activity(): + + activity = Activity() + activity.set_defaults("activity1") + activity.set_description("Activity example 1") + activity.set_pref_label("Example 1") + activity.set_preamble( + "Over the last 2 weeks, how often have you been bothered by any of the following problems?" + ) + activity.set_citation("https://www.ncbi.nlm.nih.gov/pmc/articles/PMC1495268/") + activity.set_image( + {"@type": "AudioObject", "contentUrl": "http://example.com/sample-image.png"} + ) + activity.set_compute("activity1_total_score", "item1 + item2") + + item_1 = Item() + item_1.set_defaults("item1") + # TODO + # probably want to have items/item_name be a default + item_1.set_URI(os.path.join("items", item_1.get_filename())) + # TODO + # We probably want a method to change those values rather that modifying + # the instance directly + item_1.skippable = False + item_1.required = True + """ + Items are appended and this updates the the ``ui`` ``order`` and ``addProperties`` + """ + activity.append_item(item_1) + + item_2 = Item() + item_2.set_defaults("item2") + item_2.set_filename("item_two") + + """ + In this case the URI is relative to where the activity file will be saved + """ + item_2.set_URI(os.path.join("..", "other_dir", item_2.get_filename())) + item_2.required = True + activity.append_item(item_2) + + item_3 = Item() + item_3.set_defaults("activity1_total_score") + """ + By default all files are save with a json.ld extension but this can be changed + """ + file_ext = "" + item_3.set_filename("activity1_total_score", file_ext) + item_3.set_URI(os.path.join("items", item_3.get_filename())) + item_3.skippable = False + item_3.required = True + item_3.visible = False + activity.append_item(item_3) + + activity.write(activity_dir) + activity_content, expected = load_jsons(activity) + assert activity_content == expected + + clean_up(activity) + + +""" +HELPER FUNCTIONS +""" + + +def load_jsons(obj): + + output_file = os.path.join(activity_dir, obj.get_filename()) + content = read_json(output_file) + + data_file = os.path.join(my_path, "data", "activities", obj.get_filename()) + expected = read_json(data_file) + + return content, expected + + +def read_json(file): + + with open(file, "r") as ff: + return json.load(ff) + + +def clean_up(obj): + os.remove(os.path.join(activity_dir, obj.get_filename())) diff --git a/reproschema/models/tests/test_item.py b/reproschema/models/tests/test_item.py new file mode 100644 index 0000000..a5157d8 --- /dev/null +++ b/reproschema/models/tests/test_item.py @@ -0,0 +1,401 @@ +import os, sys, json + +from ..item import Item, ResponseOption + +my_path = os.path.dirname(os.path.abspath(__file__)) + +# Left here in case Remi and python path or import can't be friends once again. +# sys.path.insert(0, my_path + "/../") + +# TODO +# refactor across the different test modules +item_dir = os.path.join(my_path, "items") +if not os.path.exists(item_dir): + os.makedirs(os.path.join(item_dir)) + +""" +Only for the few cases when we want to check against some of the files in +reproschema/tests/data +""" +reproschema_test_data = os.path.join(my_path, "..", "..", "tests", "data") + + +def test_default(): + + item = Item() + item.set_defaults() + + item.write(item_dir) + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + +# TODO +# many items types (audio ones for examples) are not yet tested because the code cannot yet generate them. + +""" +text items +""" + + +def test_text(): + + text_length = 100 + + item = Item("1.0.0-rc4") + item.set_defaults("text") + item.set_input_type_as_text(text_length) + + item.set_question("question for text item") + + item.write(item_dir) + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + +def test_multitext(): + + text_length = 50 + + item = Item("1.0.0-rc4") + item.set_defaults("multitext") + item.set_input_type_as_multitext(text_length) + + item.set_question("This is an item where the user can input several text field.") + + item.write(item_dir) + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + +""" +items with a specific "type" +""" + + +def test_email(): + + item = Item("1.0.0-rc4") + item.set_defaults("email") + item.set_input_type_as_email() + + item.set_question("input email address") + + item.write(item_dir) + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + +def test_participant_id(): + + item = Item("1.0.0-rc4") + item.set_defaults("participant id") + item.set_input_type_as_id() + + item.set_question("input the participant id number") + + item.write(item_dir) + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + +def test_date(): + + item = Item("1.0.0-rc4") + item.set_defaults("date") + item.set_input_type_as_date() + + item.set_question("input a date") + + item.write(item_dir) + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + +def test_time_range(): + + item = Item("1.0.0-rc4") + item.set_defaults("time range") + item.set_input_type_as_time_range() + + item.set_question("input a time range") + + item.write(item_dir) + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + +def test_year(): + + item = Item("1.0.0-rc4") + item.set_defaults("year") + item.set_input_type_as_year() + + item.set_question("input a year") + + item.write(item_dir) + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + +""" +Items that refer to a preset list of responses choices +""" + + +def test_language(): + + item = Item() + item.set_defaults("language") + item.set_input_type_as_language() + item.set_question("This is an item where the user can select several language.") + + item.write(item_dir) + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + +def test_country(): + + item = Item() + item.set_defaults("country") + item.set_input_type_as_country() + item.set_question("select a country") + + item.write(item_dir) + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + +def test_state(): + + item = Item() + item.set_defaults("state") + item.set_input_type_as_state() + item.set_question("select a USA state") + + item.write(item_dir) + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + +""" +NUMERICAL ITEMS +""" + + +def test_float(): + + item = Item("1.0.0-rc4") + item.set_defaults("float") + item.set_description("This is a float item.") + item.set_input_type_as_float() + item.set_question("This is an item where the user can input a float.") + + item.write(item_dir) + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + +def test_integer(): + + item = Item() + item.set_defaults("integer") + item.set_description("This is a integer item.") + item.set_input_type_as_int() + item.set_question("This is an item where the user can input a integer.") + + item.write(item_dir) + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + +""" +SELECTION ITEMS: radio and select +tested both with: +- only one response allowed +- multiple responses allowed +""" + + +def test_radio(): + + item = Item("1.0.0-rc4") + item.set_defaults("radio") + + item.set_question("question for radio item", "en") + + response_options = ResponseOption() + response_options.add_choice("Not at all", 0, "en") + response_options.add_choice("Several days", 1, "en") + # TODO + # set_min and set_max cold probably be combined into a single method that gets + # those values from the content of the choice key + response_options.set_max(1) + + item.set_input_type_as_radio(response_options) + + item.write(item_dir) + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + item.set_filename("radio multiple") + item.set_description("radio multiple") + item.set_pref_label("radio multiple") + item.set_question("question for radio item with multiple responses") + response_options.set_multiple_choice(True) + item.set_input_type_as_radio(response_options) + item.write(item_dir) + + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + +def test_select(): + + item = Item() + item.set_defaults("select") + item.set_question("question for select item") + + response_options = ResponseOption() + response_options.add_choice("Response option 1", 0) + response_options.add_choice("Response option 2", 1) + response_options.add_choice("Response option 3", 2) + response_options.set_max(2) + + item.set_input_type_as_select(response_options) + + item.write(item_dir) + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + item.set_filename("select multiple") + item.set_description("select multiple") + item.set_pref_label("select multiple") + item.set_question("question for select item with multiple responses") + response_options.set_multiple_choice(True) + item.set_input_type_as_select(response_options) + item.write(item_dir) + + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + +def test_slider(): + + item = Item() + item.set_defaults("slider") + item.set_question("question for slider item", "en") + + response_options = ResponseOption() + response_options.add_choice("not at all", 0) + response_options.add_choice("a bit", 1) + response_options.add_choice("so so", 2) + response_options.add_choice("a lot", 3) + response_options.add_choice("very much", 4) + response_options.set_max(4) + + item.set_input_type_as_slider(response_options) + + item.write(item_dir) + item_content, expected = load_jsons(item) + assert item_content == expected + + clean_up(item) + + +""" +Just to check that item with read only values + +Tries to recreate the item from +reproschema/tests/data/activities/items/activity1_total_score +""" + + +def test_read_only(): + + item = Item() + item.set_defaults("activity1_total_score") + item.set_context("../../../contexts/generic") + item.set_filename("activity1_total_score", "") + item.schema["prefLabel"] = "activity1_total_score" + item.set_description("Score item for Activity 1") + item.set_read_only_value(True) + item.set_input_type_as_int() + item.response_options.set_max(3) + item.response_options.set_min(0) + item.unset(["question"]) + + item.write(item_dir) + + output_file = os.path.join(item_dir, item.get_filename()) + item_content = read_json(output_file) + + # test against one of the pre existing files + data_file = os.path.join( + reproschema_test_data, "activities", "items", "activity1_total_score" + ) + expected = read_json(data_file) + assert item_content == expected + + clean_up(item) + + +""" +HELPER FUNCTIONS +""" + + +def load_jsons(item): + + output_file = os.path.join(item_dir, item.get_filename()) + item_content = read_json(output_file) + + data_file = os.path.join(my_path, "data", "items", item.get_filename()) + expected = read_json(data_file) + + return item_content, expected + + +def read_json(file): + + with open(file, "r") as ff: + return json.load(ff) + + +def clean_up(obj): + os.remove(os.path.join(item_dir, obj.get_filename())) diff --git a/reproschema/models/tests/test_protocol.py b/reproschema/models/tests/test_protocol.py new file mode 100644 index 0000000..ddc391f --- /dev/null +++ b/reproschema/models/tests/test_protocol.py @@ -0,0 +1,90 @@ +import os, sys, json + +from ..protocol import Protocol +from ..activity import Activity + +my_path = os.path.dirname(os.path.abspath(__file__)) + +# Left here in case Remi and python path or import can't be friends once again. +# sys.path.insert(0, my_path + "/../") + +# TODO +# refactor across the different test modules +protocol_dir = os.path.join(my_path, "protocols") +if not os.path.exists(protocol_dir): + os.makedirs(os.path.join(protocol_dir)) + +""" +Only for the few cases when we want to check against some of the files in +reproschema/tests/data +""" +reproschema_test_data = os.path.join(my_path, "..", "..", "tests", "data") + + +def test_default(): + + """ + FYI: The default protocol does not conform to the schema + so `reproschema validate` will complain if you run it in this + """ + + protocol = Protocol() + protocol.set_defaults() + + protocol.write(protocol_dir) + protocol_content, expected = load_jsons(protocol) + assert protocol_content == expected + + clean_up(protocol) + + +def test_protocol(): + + protocol = Protocol() + protocol.set_defaults("protocol1") + protocol.set_pref_label("Protocol1") + protocol.set_description("example Protocol") + protocol.set_landing_page("http://example.com/sample-readme.md") + + auto_advance = True + allow_export = True + disable_back = True + protocol.set_ui_allow(auto_advance, allow_export, disable_back) + + activity_1 = Activity() + activity_1.set_defaults("activity1") + activity_1.set_pref_label("Screening") + activity_1.set_URI(os.path.join("..", "activities", activity_1.get_filename())) + protocol.append_activity(activity_1) + + protocol.write(protocol_dir) + protocol_content, expected = load_jsons(protocol) + assert protocol_content == expected + + clean_up(protocol) + + +""" +HELPER FUNCTIONS +""" + + +def load_jsons(obj): + + output_file = os.path.join(protocol_dir, obj.get_filename()) + content = read_json(output_file) + + data_file = os.path.join(my_path, "data", "protocols", obj.get_filename()) + expected = read_json(data_file) + + return content, expected + + +def read_json(file): + + with open(file, "r") as ff: + return json.load(ff) + + +def clean_up(obj): + os.remove(os.path.join(protocol_dir, obj.get_filename())) diff --git a/reproschema/models/tests/test_response_options.py b/reproschema/models/tests/test_response_options.py new file mode 100644 index 0000000..5cf13cc --- /dev/null +++ b/reproschema/models/tests/test_response_options.py @@ -0,0 +1,70 @@ +import os, sys, json + +from ..item import ResponseOption + +my_path = os.path.dirname(os.path.abspath(__file__)) + +# Left here in case Remi and python path or import can't be friends once again. +# sys.path.insert(0, my_path + "/../") + +# TODO +# refactor across the different test modules +response_options_dir = os.path.join(my_path, "response_options") +if not os.path.exists(response_options_dir): + os.makedirs(os.path.join(response_options_dir)) + +""" +Only for the few cases when we want to check against some of the files in +reproschema/tests/data +""" +reproschema_test_data = os.path.join(my_path, "..", "..", "tests", "data") + + +def test_default(): + + response_options = ResponseOption() + response_options.set_defaults() + + response_options.write(response_options_dir) + content, expected = load_jsons(response_options) + assert content == expected + + +def test_example(): + + response_options = ResponseOption() + response_options.set_defaults() + response_options.set_filename("example") + response_options.set_type("integer") + response_options.unset("multipleChoice") + response_options.add_choice("Not at all", 0) + for i in range(1, 5): + response_options.add_choice("", i) + response_options.add_choice("Completely", 6) + response_options.set_max(6) + + response_options.write(response_options_dir) + content, expected = load_jsons(response_options) + assert content == expected + + +""" +HELPER FUNCTIONS +""" + + +def load_jsons(obj): + + output_file = os.path.join(response_options_dir, obj.get_filename()) + content = read_json(output_file) + + data_file = os.path.join(my_path, "data", "response_options", obj.get_filename()) + expected = read_json(data_file) + + return content, expected + + +def read_json(file): + + with open(file, "r") as ff: + return json.load(ff) diff --git a/reproschema/tests/contexts/generic b/reproschema/tests/contexts/generic index 2989fc0..8a9ba1b 100644 --- a/reproschema/tests/contexts/generic +++ b/reproschema/tests/contexts/generic @@ -12,14 +12,27 @@ "@container": "@language" }, "value": { - "@id": "reproschema:value", - "@type": "@id", - "@container": "@language" + "@id": "reproschema:value" }, "image": { + "@id": "schema:image" + }, + "imageUrl": { "@id": "schema:image", "@type": "@id" }, + "audio": { + "@id": "schema:audio" + }, + "video": { + "@id": "schema:video" + }, + "contentUrl": { + "@id": "schema:contentUrl", + "@type": "@id" + }, + "VideoObject": "schema:VideoObject", + "AudioObject": "schema:AudioObject", "inLanguage": { "@id": "schema:inLanguage" }, @@ -32,12 +45,10 @@ "@container": "@language" }, "version": { - "@id": "schema:version", - "@container": "@language" + "@id": "schema:version" }, "schemaVersion": { - "@id": "schema:schemaVersion", - "@container": "@language" + "@id": "schema:schemaVersion" }, "prefLabel": { "@id": "skos:prefLabel", @@ -58,6 +69,7 @@ }, "landingPage": { "@id": "reproschema:landingPage", + "@type": "@id", "@container": "@set" }, "question": { @@ -139,11 +151,11 @@ }, "compute": { "@id": "reproschema:compute", - "@container": "@index" + "@container": "@set" }, "messages": { "@id": "reproschema:messages", - "@container": "@index" + "@container": "@set" }, "jsExpression": { "@id": "reproschema:jsExpression" @@ -181,6 +193,7 @@ }, "additionalNotesObj": { "@id": "reproschema:additionalNotesObj", + "@type": "reproschema:AdditionalNoteObj", "@container": "@set" }, "source": { diff --git a/reproschema/tests/data/activities/activity1.jsonld b/reproschema/tests/data/activities/activity1.jsonld index a199ca2..6a533ee 100644 --- a/reproschema/tests/data/activities/activity1.jsonld +++ b/reproschema/tests/data/activities/activity1.jsonld @@ -4,25 +4,29 @@ "@id": "activity1.jsonld", "prefLabel": "Example 1", "description": "Activity example 1", - "schemaVersion": "1.0.0-rc2", + "schemaVersion": "1.0.0-rc4", "version": "0.0.1", "citation": "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC1495268/", + "image": { + "@type": "AudioObject", + "contentUrl": "http://example.com/sample-image.png" + }, "preamble": { "en": "Over the last 2 weeks, how often have you been bothered by any of the following problems?", "es": "Durante las últimas 2 semanas, ¿con qué frecuencia le han molestado los siguintes problemas?" }, - "messages": [ - { - "message": "Test message: Triggered when item1 value is greater than 1", - "jsExpression": "item1 > 1" - } - ], "compute": [ { "variableName": "activity1_total_score", "jsExpression": "item1 + item2" } ], + "messages": [ + { + "message": "Test message: Triggered when item1 value is greater than 1", + "jsExpression": "item1 > 1" + } + ], "ui": { "addProperties": [ { "isAbout": "items/item1.jsonld", @@ -36,13 +40,20 @@ { "isAbout": "items/item2.jsonld", "variableName": "item2", "requiredValue": true, - "isVis": true + "isVis": true, + "allow": ["reproschema:Skipped"] + }, + { "isAbout": "items/activity1_total_score", + "variableName": "activity1_total_score", + "requiredValue": true, + "isVis": false } ], "order": [ "items/item1.jsonld", - "items/item2.jsonld" + "items/item2.jsonld", + "items/activity1_total_score" ], "shuffle": false } -} \ No newline at end of file +} diff --git a/reproschema/tests/data/activities/activity1_embed.jsonld b/reproschema/tests/data/activities/activity1_embed.jsonld index 1a08595..f732505 100644 --- a/reproschema/tests/data/activities/activity1_embed.jsonld +++ b/reproschema/tests/data/activities/activity1_embed.jsonld @@ -4,7 +4,7 @@ "@id": "activity1.jsonld", "prefLabel": "Example 1", "description": "Activity example 1", - "schemaVersion": "1.0.0-rc2", + "schemaVersion": "1.0.0-rc4", "version": "0.0.1", "citation": "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC1495268/", "preamble": { @@ -24,7 +24,7 @@ "@id": "items/item1.jsonld", "prefLabel": "item1", "description": "Q1 of example 1", - "schemaVersion": "1.0.0-rc2", + "schemaVersion": "1.0.0-rc4", "version": "0.0.1", "question": { "en": "Little interest or pleasure in doing things", diff --git a/reproschema/tests/data/activities/items/activity1_total_score b/reproschema/tests/data/activities/items/activity1_total_score new file mode 100644 index 0000000..5ed7f84 --- /dev/null +++ b/reproschema/tests/data/activities/items/activity1_total_score @@ -0,0 +1,18 @@ +{ + "@context": "../../../contexts/generic", + "@type": "reproschema:Field", + "@id": "activity1_total_score", + "prefLabel": "activity1_total_score", + "description": "Score item for Activity 1", + "schemaVersion": "1.0.0-rc4", + "version": "0.0.1", + "ui": { + "inputType": "number", + "readonlyValue": true + }, + "responseOptions": { + "valueType": "xsd:integer", + "minValue": 0, + "maxValue": 3 + } +} diff --git a/reproschema/tests/data/activities/items/item1.jsonld b/reproschema/tests/data/activities/items/item1.jsonld index 8f77c04..50f73cc 100644 --- a/reproschema/tests/data/activities/items/item1.jsonld +++ b/reproschema/tests/data/activities/items/item1.jsonld @@ -4,8 +4,16 @@ "@id": "item1.jsonld", "prefLabel": "item1", "description": "Q1 of example 1", - "schemaVersion": "1.0.0-rc2", + "schemaVersion": "1.0.0-rc4", "version": "0.0.1", + "audio": { + "@type": "AudioObject", + "contentUrl": "http://media.freesound.org/sample-file.mp4" + }, + "image": { + "@type": "ImageObject", + "contentUrl": "http://example.com/sample-image.jpg" + }, "question": { "en": "Little interest or pleasure in doing things", "es": "Poco interés o placer en hacer cosas" @@ -31,21 +39,21 @@ "en": "Several days", "es": "Varios días" }, - "value": 1 + "value": "a" }, { "name": { "en": "More than half the days", "es": "Más de la mitad de los días" }, - "value": 2 + "value": {"@id": "http://example.com/choice3" } }, { "name": { "en": "Nearly everyday", "es": "Casi todos los días" }, - "value": 3 + "value": {"@value": "choice-with-lang", "@language": "en"} } ] }, @@ -54,6 +62,11 @@ "source": "redcap", "column": "notes", "value": "some extra note" + }, + { + "source": "redcap", + "column": "notes", + "value": {"@id": "http://example.com/iri-example"} } ] diff --git a/reproschema/tests/data/activities/items/item2.jsonld b/reproschema/tests/data/activities/items/item2.jsonld index f7f23ef..cf1b37a 100644 --- a/reproschema/tests/data/activities/items/item2.jsonld +++ b/reproschema/tests/data/activities/items/item2.jsonld @@ -4,12 +4,17 @@ "@id": "item2.jsonld", "prefLabel": "item2", "description": "Q2 of example 1", - "schemaVersion": "1.0.0-rc2", + "schemaVersion": "1.0.0-rc4", "version": "0.0.1", "question": { "en": "Current temperature.", "es": "Fiebre actual." }, + "video": { + "@type": "VideoObject", + "contentUrl": "http://media.freesound.org/data/0/previews/719__elmomo__12oclock_girona_preview.mp4" + }, + "imageUrl": "http://example.com/sample-image.jpg", "ui": { "inputType": "float" }, @@ -33,6 +38,3 @@ ] } } - - - diff --git a/reproschema/tests/data/protocols/protocol1.jsonld b/reproschema/tests/data/protocols/protocol1.jsonld index c9fc432..dc79103 100644 --- a/reproschema/tests/data/protocols/protocol1.jsonld +++ b/reproschema/tests/data/protocols/protocol1.jsonld @@ -7,8 +7,10 @@ "es": "Protocol1_es" }, "description": "example Protocol", - "schemaVersion": "1.0.0-rc2", + "schemaVersion": "1.0.0-rc4", "version": "0.0.1", + "landingPage": {"@id": "http://example.com/sample-readme.md", + "inLanguage": "en"}, "messages": [ { "message": "Test message: Triggered when item1 value is greater than 0", diff --git a/reproschema/tests/data/protocols/protocol1_embed.jsonld b/reproschema/tests/data/protocols/protocol1_embed.jsonld index 3681040..69ac9cb 100644 --- a/reproschema/tests/data/protocols/protocol1_embed.jsonld +++ b/reproschema/tests/data/protocols/protocol1_embed.jsonld @@ -7,7 +7,7 @@ "es": "Protocol1_es" }, "description": "example Protocol", - "schemaVersion": "1.0.0-rc2", + "schemaVersion": "1.0.0-rc4", "version": "0.0.1", "ui": { "addProperties": [ @@ -27,7 +27,7 @@ "@id": "../activities/activity1.jsonld", "prefLabel": "Example 1", "description": "Activity example 1", - "schemaVersion": "1.0.0-rc2", + "schemaVersion": "1.0.0-rc4", "version": "0.0.1", "citation": "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC1495268/", "preamble": { @@ -49,7 +49,7 @@ "@id": "../activities/items/item1.jsonld", "prefLabel": "item1", "description": "Q1 of example 1", - "schemaVersion": "1.0.0-rc2", + "schemaVersion": "1.0.0-rc4", "version": "0.0.1", "question": { "en": "Little interest or pleasure in doing things", diff --git a/reproschema/tests/reproschema-shacl.ttl b/reproschema/tests/reproschema-shacl.ttl index b7ccc3f..c504503 100644 --- a/reproschema/tests/reproschema-shacl.ttl +++ b/reproschema/tests/reproschema-shacl.ttl @@ -1,71 +1,58 @@ @prefix dash: . +@prefix nidm: . +@prefix prov: . @prefix rdf: . @prefix reproschema: . @prefix schema: . @prefix sh: . @prefix skos: . @prefix xsd: . -@prefix nidm: . -@prefix uuid: . -@prefix prov: . reproschema:ActivityShape a sh:NodeShape ; sh:closed true ; sh:ignoredProperties ( rdf:type ) ; sh:property [ sh:datatype rdf:langString ; sh:path schema:description ], - [ sh:datatype rdf:langString ; sh:path schema:schemaVersion ], - [ sh:datatype rdf:langString ; sh:path schema:version ], - [ sh:datatype rdf:langString ; sh:path schema:citation ], - - [ sh:nodeKind sh:IRI ; + [ sh:or ( [ sh:nodeKind sh:IRI ] [ sh:node reproschema:MediaObjectShape ] ) ; sh:path schema:image ], - + [ sh:node reproschema:MediaObjectShape ; + sh:path schema:audio ], + [ sh:node reproschema:MediaObjectShape ; + sh:path schema:video ], + [ sh:nodeKind sh:IRI ; + sh:path schema:about ], [ sh:datatype rdf:langString ; sh:minCount 1 ; sh:path skos:prefLabel ], - [ sh:datatype rdf:langString ; sh:path skos:altLabel ], - [ sh:datatype rdf:langString ; sh:path reproschema:preamble ], - [ sh:node reproschema:ComputeSpecificationShape ; sh:path reproschema:compute ], - [ sh:node reproschema:MessageSpecificationShape ; sh:path reproschema:messages ], - [ sh:maxCount 1 ; sh:node dash:ListShape ; sh:path reproschema:order ; sh:property [ sh:minCount 1 ; sh:nodeKind sh:IRI ; sh:path ( [ sh:zeroOrMorePath rdf:rest ] rdf:first ) ] ], - [ sh:node reproschema:AddPropertiesShape ; sh:path reproschema:addProperties ], - [ sh:node reproschema:OverridePropertiesShape ; sh:path reproschema:overrideProperties ], - - [ sh:datatype xsd:string ; - sh:path reproschema:inputType ], - [ sh:datatype schema:Boolean ; sh:path reproschema:shuffle ], - - [ sh:nodeKind sh:IRI ; - sh:in ( reproschema:AllowExport reproschema:DisableBack reproschema:AutoAdvance reproschema:AllowReplay reproschema:Skipped reproschema:DontKnow reproschema:TimedOut ) ; + [ sh:in ( reproschema:AllowExport reproschema:DisableBack reproschema:AutoAdvance reproschema:AllowReplay reproschema:Skipped reproschema:DontKnow reproschema:TimedOut ) ; + sh:nodeKind sh:IRI ; sh:path reproschema:allow ], - [ sh:node reproschema:CronTableShape ; sh:path reproschema:cronTable ] ; sh:targetClass reproschema:Activity . @@ -74,45 +61,42 @@ reproschema:FieldShape a sh:NodeShape ; sh:closed true ; sh:ignoredProperties ( rdf:type ) ; sh:property [ sh:datatype rdf:langString ; + sh:path reproschema:preamble ], + [ sh:or ( [ sh:nodeKind sh:IRI ] [ sh:node reproschema:MediaObjectShape ] ) ; + sh:path schema:image ], + [ sh:node reproschema:MediaObjectShape ; + sh:path schema:audio ], + [ sh:node reproschema:MediaObjectShape ; + sh:path schema:video ], + [ sh:datatype xsd:string ; + sh:maxCount 1 ; + sh:minCount 1 ; + sh:path reproschema:inputType ], + [ sh:datatype xsd:boolean ; + sh:maxCount 1 ; + sh:path schema:readonlyValue ], + [ sh:maxCount 1 ; + sh:nodeKind sh:IRI ; + sh:path schema:about ], + [ sh:nodeKind sh:IRI ; + sh:path schema:isPartOf ], + [ sh:node reproschema:AdditionalNoteObjShape ; + sh:path reproschema:additionalNotesObj ], + [ sh:or ( [ sh:nodeKind sh:IRI ] [ sh:node reproschema:ResponseOptionsShape ] ) ; + sh:path reproschema:responseOptions ], + [ sh:datatype rdf:langString ; sh:minCount 1 ; sh:path skos:prefLabel ], - [ sh:datatype rdf:langString ; sh:path skos:altLabel ], - [ sh:datatype rdf:langString ; sh:path schema:description ], - [ sh:datatype rdf:langString ; sh:path schema:schemaVersion ], - [ sh:datatype rdf:langString ; sh:path schema:version ], - - [ sh:datatype rdf:langString ; - sh:path schema:question ], - - [ sh:nodeKind sh:IRI ; - sh:maxCount 1 ; - sh:path schema:image ], - [ sh:datatype rdf:langString ; - sh:path reproschema:preamble ], - - [ sh:datatype xsd:string ; - sh:maxCount 1 ; - sh:minCount 1 ; - sh:path reproschema:inputType ], - - [ sh:datatype xsd:boolean ; - sh:maxCount 1 ; - sh:path schema:readonlyValue ], - - [ sh:node reproschema:AdditionalNoteObj ; - sh:path reproschema:additionalNotesObj ], - - [ sh:or ( [ sh:nodeKind sh:IRI ] [ sh:node reproschema:ResponseOptionsShape ] ) ; - sh:path reproschema:responseOptions ] ; + sh:path schema:question ] ; sh:targetClass reproschema:Field . reproschema:ProtocolShape a sh:NodeShape ; @@ -120,59 +104,108 @@ reproschema:ProtocolShape a sh:NodeShape ; sh:ignoredProperties ( rdf:type ) ; sh:property [ sh:datatype rdf:langString ; sh:path schema:description ], - [ sh:datatype rdf:langString ; sh:path schema:schemaVersion ], - [ sh:datatype rdf:langString ; sh:path schema:version ], - - [ sh:nodeKind sh:IRI ; - sh:maxCount 1 ; + [ sh:maxCount 1 ; + sh:nodeKind sh:IRI ; + sh:path schema:about ], + [ sh:or ( [ sh:nodeKind sh:IRI ] [ sh:node reproschema:MediaObjectShape ] ) ; sh:path schema:image ], - - [ sh:nodeKind sh:IRI ; + [ sh:node reproschema:MediaObjectShape ; + sh:path schema:audio ], + [ sh:node reproschema:MediaObjectShape ; + sh:path schema:video ], + [ sh:node reproschema:LandingPageShape ; sh:path reproschema:landingPage ], - [ sh:datatype rdf:langString ; sh:minCount 1 ; sh:path skos:prefLabel ], - [ sh:datatype rdf:langString ; sh:path skos:altLabel ], - [ sh:datatype rdf:langString ; sh:path reproschema:preamble ], - [ sh:node reproschema:ComputeSpecificationShape ; sh:path reproschema:compute ], - [ sh:node reproschema:MessageSpecificationShape ; sh:path reproschema:messages ], - [ sh:minCount 1 ; sh:node dash:ListShape ; sh:path reproschema:order ; sh:property [ sh:minCount 1 ; sh:nodeKind sh:IRI ; sh:path ( [ sh:zeroOrMorePath rdf:rest ] rdf:first ) ] ], - [ sh:node reproschema:AddPropertiesShape ; sh:path reproschema:addProperties ], - [ sh:node reproschema:OverridePropertiesShape ; sh:path reproschema:overrideProperties ], - [ sh:datatype schema:Boolean ; sh:path reproschema:shuffle ], + [ sh:in ( reproschema:AllowExport reproschema:DisableBack reproschema:AutoAdvance reproschema:AllowReplay reproschema:Skipped reproschema:DontKnow reproschema:TimedOut ) ; + sh:nodeKind sh:IRI ; + sh:path reproschema:allow ] ; + sh:targetClass reproschema:Protocol . + +reproschema:ResponseActivityShape a sh:NodeShape ; + sh:closed true ; + sh:ignoredProperties ( rdf:type ) ; + sh:property [ sh:nodeKind sh:IRI ; + sh:path prov:used ], + [ sh:datatype rdf:langString ; + sh:path schema:inLanguage ], + [ sh:datatype xsd:dateTime ; + sh:path prov:startedAtTime ], + [ sh:datatype xsd:dateTime ; + sh:path prov:endedAtTime ], + [ sh:node prov:SoftwareAgentShape ; + sh:path prov:wasAssociatedWith ], + [ sh:datatype rdf:langString ; + sh:path prov:generated ] ; + sh:targetClass reproschema:ResponseActivity . +reproschema:ResponseShape a sh:NodeShape ; + sh:closed true ; + sh:ignoredProperties ( rdf:type ) ; + sh:property [ sh:node prov:ParticipantShape ; + sh:path prov:wasAttributedTo ], [ sh:nodeKind sh:IRI ; - sh:in ( reproschema:AllowExport reproschema:DisableBack reproschema:AutoAdvance reproschema:AllowReplay reproschema:Skipped reproschema:DontKnow reproschema:TimedOut ) ; - sh:path reproschema:allow ], + sh:path reproschema:isAbout ], + [ sh:or ( [ sh:datatype xsd:integer ] [ sh:nodeKind sh:IRI ] [ sh:datatype schema:Boolean ] [ sh:datatype schema:StructuredValue ] [ sh:datatype schema:Text ] ) ; + sh:path reproschema:value ] ; + sh:targetClass reproschema:Response . - [ sh:node reproschema:CronTableShape ; - sh:path reproschema:cronTable ] ; - sh:targetClass reproschema:Protocol . +reproschema:AdditionalNoteObjShape a sh:NodeShape ; + sh:closed true ; + sh:ignoredProperties ( rdf:type ) ; + sh:property [ sh:datatype xsd:string ; + sh:minCount 1 ; + sh:path reproschema:source ], + [ sh:datatype xsd:string ; + sh:minCount 1 ; + sh:path reproschema:column ], + [ sh:or ( [ sh:datatype xsd:integer ] [ sh:nodeKind sh:IRI ] [ sh:datatype schema:Boolean ] [ sh:datatype schema:StructuredValue ] [ sh:datatype rdf:langString ] ) ; + sh:path reproschema:value ] ; + sh:targetClass reproschema:AdditionalNoteObj . + +reproschema:ChoicesShape a sh:NodeShape ; + sh:closed true ; + sh:ignoredProperties ( rdf:type ) ; + sh:property [ sh:or ( [ sh:nodeKind sh:IRI ] [ sh:node reproschema:MediaObjectShape ] ) ; + sh:path schema:image ], + [ sh:datatype rdf:langString ; + sh:path schema:name ], + [ sh:or ( [ sh:datatype xsd:integer ] [ sh:nodeKind sh:IRI ] [ sh:datatype schema:Boolean ] [ sh:datatype schema:StructuredValue ] [ sh:datatype rdf:langString ] ) ; + sh:path reproschema:value ] ; + sh:targetClass reproschema:Choice . + +reproschema:LandingPageShape a sh:NodeShape ; + sh:closed true ; + sh:ignoredProperties ( rdf:type ) ; + sh:property [ sh:datatype rdf:langString ; + sh:maxCount 1 ; + sh:minCount 1 ; + sh:path schema:inLanguage ] . reproschema:ResponseOptionsShape a sh:NodeShape ; sh:closed true ; @@ -180,147 +213,91 @@ reproschema:ResponseOptionsShape a sh:NodeShape ; sh:property [ sh:datatype schema:Boolean ; sh:maxCount 1 ; sh:path reproschema:multipleChoice ], - [ sh:nodeKind sh:IRI ; sh:path reproschema:valueType ], - [ sh:datatype xsd:integer ; sh:maxCount 1 ; sh:path schema:minValue ], - [ sh:datatype xsd:integer ; sh:maxCount 1 ; sh:path schema:maxValue ], - [ sh:datatype xsd:string ; sh:maxCount 1 ; sh:path reproschema:datumType ], - [ sh:or ( [ sh:datatype schema:URL ] [ sh:datatype rdf:langString ] ) ; sh:path schema:unitCode ], - [ sh:node reproschema:UnitOptionsShape ; - sh:path reproschema:unitOptions ] , - + sh:path reproschema:unitOptions ], [ sh:node reproschema:ChoicesShape ; sh:path reproschema:choices ] ; sh:targetClass reproschema:ResponseOption . -reproschema:AddPropertiesShape a sh:NodeShape ; +reproschema:UnitOptionsShape a sh:NodeShape ; sh:closed true ; sh:ignoredProperties ( rdf:type ) ; sh:property [ sh:datatype rdf:langString ; - sh:minCount 1 ; - sh:path reproschema:variableName ], - - [ sh:minCount 1 ; - sh:nodeKind sh:IRI ; - sh:path reproschema:isAbout ], - - [ sh:or ( [ sh:datatype rdf:langString ] [ sh:datatype xsd:boolean ] [ sh:node reproschema:IsVisShape ] ) ; - sh:path reproschema:isVis ], - - [ sh:datatype rdf:langString ; sh:path skos:prefLabel ], + [ sh:or ( [ sh:nodeKind sh:IRI ] [ sh:datatype rdf:langString ] ) ; + sh:path reproschema:value ] ; + sh:targetClass reproschema:UnitOption . - [ sh:datatype xsd:boolean ; - sh:path schema:valueRequired ], +prov:ParticipantShape a sh:NodeShape ; + sh:closed true ; + sh:ignoredProperties ( rdf:type ) ; + sh:property [ sh:datatype rdf:langString ; + sh:path nidm:subject_id ] ; + sh:targetClass reproschema:Participant . +prov:SoftwareAgentShape a sh:NodeShape ; + sh:closed true ; + sh:ignoredProperties ( rdf:type ) ; + sh:property [ sh:datatype rdf:langString ; + sh:path schema:version ], [ sh:nodeKind sh:IRI ; - sh:in ( reproschema:AllowExport reproschema:DisableBack reproschema:AutoAdvance reproschema:AllowReplay reproschema:Skipped reproschema:DontKnow reproschema:TimedOut ) ; - sh:path reproschema:allow ], - - [ sh:datatype rdf:langString ; - sh:pattern "^P(?!$)(\\d+(?:\\.\\d+)?Y)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?W)?(\\d+(?:\\.\\d+)?D)?(T(?=\\d)(\\d+(?:\\.\\d+)?H)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?S)?)?$" ; - sh:path reproschema:randomMaxDelay ], - - # Patterns extracted from: https://gist.githubusercontent.com/philipashlock/8830168/raw/e65bee7b2c1c9908fd62d064b2451e2cf29aa43c/index.html - [ sh:datatype rdf:langString ; - sh:or ( [sh:pattern "^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ] - [sh:pattern "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" ] - [sh:pattern "^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?(\\/)([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ] - [sh:pattern "^(R\\d*\\/)?([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\4([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\18[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?(\\/)P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" ] - [sh:pattern "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?\\/([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\4([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\18[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ] - ) ; - sh:path reproschema:schedule ], - - [ sh:datatype rdf:langString ; - sh:or ( [sh:pattern "^P(?!$)(\\d+(?:\\.\\d+)?Y)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?W)?(\\d+(?:\\.\\d+)?D)?(T(?=\\d)(\\d+(?:\\.\\d+)?H)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?S)?)?$"] - [sh:pattern "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?\\/([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\4([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\18[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ] - ) ; - sh:path reproschema:limit ], - - [ sh:datatype xsd:integer ; - sh:path reproschema:maxRetakes ] ; - - sh:targetClass reproschema:AdditionalProperty . + sh:path schema:url ] ; + sh:targetClass reproschema:SoftwareAgent . -reproschema:OverridePropertiesShape a sh:NodeShape ; +reproschema:AddPropertiesShape a sh:NodeShape ; sh:closed true ; sh:ignoredProperties ( rdf:type ) ; sh:property [ sh:datatype rdf:langString ; sh:minCount 1 ; sh:path reproschema:variableName ], - [ sh:minCount 1 ; sh:nodeKind sh:IRI ; sh:path reproschema:isAbout ], - [ sh:or ( [ sh:datatype rdf:langString ] [ sh:datatype xsd:boolean ] [ sh:node reproschema:IsVisShape ] ) ; sh:path reproschema:isVis ], - [ sh:datatype rdf:langString ; sh:path skos:prefLabel ], - [ sh:datatype xsd:boolean ; sh:path schema:valueRequired ], - + [ sh:in ( reproschema:AllowExport reproschema:DisableBack reproschema:AutoAdvance reproschema:AllowReplay reproschema:Skipped reproschema:DontKnow reproschema:TimedOut ) ; + sh:nodeKind sh:IRI ; + sh:path reproschema:allow ], [ sh:datatype rdf:langString ; - sh:pattern "^P(?!$)(\\d+(?:\\.\\d+)?Y)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?W)?(\\d+(?:\\.\\d+)?D)?(T(?=\\d)(\\d+(?:\\.\\d+)?H)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?S)?)?$" ; - sh:path reproschema:randomMaxDelay ], - - # Patterns extracted from: https://gist.githubusercontent.com/philipashlock/8830168/raw/e65bee7b2c1c9908fd62d064b2451e2cf29aa43c/index.html + sh:path reproschema:randomMaxDelay ; + sh:pattern "^P(?!$)(\\d+(?:\\.\\d+)?Y)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?W)?(\\d+(?:\\.\\d+)?D)?(T(?=\\d)(\\d+(?:\\.\\d+)?H)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?S)?)?$" ], [ sh:datatype rdf:langString ; - sh:or ( [sh:pattern "^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ] - [sh:pattern "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" ] - [sh:pattern "^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?(\\/)([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ] - [sh:pattern "^(R\\d*\\/)?([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\4([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\18[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?(\\/)P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" ] - [sh:pattern "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?\\/([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\4([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\18[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ] - ) ; + sh:or ( [ sh:pattern "^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ] [ sh:pattern "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" ] [ sh:pattern "^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?(\\/)([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ] [ sh:pattern "^(R\\d*\\/)?([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\4([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\18[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?(\\/)P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" ] [ sh:pattern "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?\\/([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\4([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\18[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ] ) ; sh:path reproschema:schedule ], - [ sh:datatype rdf:langString ; - sh:or ( [sh:pattern "^P(?!$)(\\d+(?:\\.\\d+)?Y)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?W)?(\\d+(?:\\.\\d+)?D)?(T(?=\\d)(\\d+(?:\\.\\d+)?H)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?S)?)?$"] - [sh:pattern "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?\\/([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\4([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\18[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ] - ) ; + sh:or ( [ sh:pattern "^P(?!$)(\\d+(?:\\.\\d+)?Y)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?W)?(\\d+(?:\\.\\d+)?D)?(T(?=\\d)(\\d+(?:\\.\\d+)?H)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?S)?)?$" ] [ sh:pattern "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?\\/([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\4([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\18[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ] ) ; sh:path reproschema:limit ], - [ sh:datatype xsd:integer ; sh:path reproschema:maxRetakes ] ; - sh:targetClass reproschema:OverrideProperty . - -reproschema:ChoicesShape a sh:NodeShape ; - sh:closed true ; - sh:ignoredProperties ( rdf:type ) ; - sh:property [ sh:nodeKind sh:IRI ; - sh:path schema:image ], - - [ sh:datatype rdf:langString ; - sh:path schema:name ], - - [ sh:or ( [ sh:datatype xsd:integer ] [ sh:nodeKind sh:IRI ] [ sh:datatype schema:Boolean ] [ sh:datatype schema:StructuredValue ] [ sh:datatype schema:Text ]) ; - sh:path reproschema:value ] ; - sh:targetClass reproschema:Choice . + sh:targetClass reproschema:AdditionalProperty . -reproschema:UnitOptionsShape a sh:NodeShape ; +reproschema:ComputeSpecificationShape a sh:NodeShape ; sh:closed true ; sh:ignoredProperties ( rdf:type ) ; sh:property [ sh:datatype rdf:langString ; - sh:path skos:prefLabel ], - - [ sh:or ( [ sh:nodeKind sh:IRI ] [ sh:datatype schema:Text ]) ; - sh:path reproschema:value ] ; - sh:targetClass reproschema:UnitOption . + sh:minCount 1 ; + sh:path reproschema:variableName ], + [ sh:datatype rdf:langString ; + sh:minCount 1 ; + sh:path reproschema:jsExpression ] ; + sh:targetClass reproschema:ComputeSpecification . reproschema:IsVisShape a sh:NodeShape ; sh:closed true ; @@ -328,101 +305,58 @@ reproschema:IsVisShape a sh:NodeShape ; sh:property [ sh:datatype rdf:langString ; sh:minCount 1 ; sh:path schema:method ], - - [ sh:datatype rdf:langString ; - sh:minCount 1 ; + [ sh:minCount 1 ; + sh:nodeKind sh:IRI ; sh:path schema:url ], - [ sh:datatype rdf:langString ; sh:minCount 1 ; sh:path reproschema:payload ] . -reproschema:ComputeSpecificationShape a sh:NodeShape ; - sh:closed true ; - sh:ignoredProperties ( rdf:type ) ; - sh:property [ sh:datatype rdf:langString ; - sh:minCount 1 ; - sh:path reproschema:variableName ], - [ sh:datatype rdf:langString ; - sh:minCount 1 ; - sh:path reproschema:jsExpression ] ; - sh:targetClass reproschema:ComputeSpecification . - reproschema:MessageSpecificationShape a sh:NodeShape ; sh:closed true ; sh:ignoredProperties ( rdf:type ) ; sh:property [ sh:datatype rdf:langString ; sh:minCount 1 ; - sh:path reproschema:message ] , - - [ sh:or ( [ sh:datatype rdf:langString ] [ sh:datatype xsd:boolean ] ) ; - sh:minCount 1 ; + sh:path reproschema:message ], + [ sh:minCount 1 ; + sh:or ( [ sh:datatype rdf:langString ] [ sh:datatype xsd:boolean ] ) ; sh:path reproschema:jsExpression ] ; sh:targetClass reproschema:MessageSpecification . -reproschema:AdditionalNoteObjShape a sh:NodeShape ; +reproschema:OverridePropertiesShape a sh:NodeShape ; sh:closed true ; sh:ignoredProperties ( rdf:type ) ; sh:property [ sh:datatype rdf:langString ; sh:minCount 1 ; - sh:path reproschema:source ], - - [ sh:datatype rdf:langString ; - sh:minCount 1 ; - sh:path reproschema:column ], - - [ sh:or ( [ sh:datatype xsd:integer ] [ sh:nodeKind sh:IRI ] [ sh:datatype schema:Boolean ] [ sh:datatype schema:StructuredValue ] [ sh:datatype schema:Text ]) ; - sh:path reproschema:value ] ; - sh:targetClass reproschema:AdditionalNoteObj . - -reproschema:ResponseShape a sh:NodeShape ; - sh:closed true ; - sh:ignoredProperties ( rdf:type ) ; - sh:property [ sh:node prov:ParticipantShape ; - sh:path prov:wasAttributedTo ], - - [ sh:nodeKind sh:IRI ; + sh:path reproschema:variableName ], + [ sh:minCount 1 ; + sh:nodeKind sh:IRI ; sh:path reproschema:isAbout ], - - [ sh:or ( [ sh:datatype xsd:integer ] [ sh:nodeKind sh:IRI ] [ sh:datatype schema:Boolean ] [ sh:datatype schema:StructuredValue ] [ sh:datatype schema:Text ]) ; - sh:path reproschema:value ] ; - sh:targetClass reproschema:Response . - -prov:ParticipantShape a sh:NodeShape ; - sh:closed true ; - sh:ignoredProperties ( rdf:type ) ; - sh:property [ sh:datatype rdf:langString ; - sh:path nidm:subject_id ] ; - sh:targetClass reproschema:Participant . - -reproschema:ResponseActivityShape a sh:NodeShape ; - sh:closed true ; - sh:ignoredProperties ( rdf:type ) ; - sh:property [ sh:nodeKind sh:IRI ; - sh:path prov:used ], - + [ sh:or ( [ sh:datatype rdf:langString ] [ sh:datatype xsd:boolean ] [ sh:node reproschema:IsVisShape ] ) ; + sh:path reproschema:isVis ], [ sh:datatype rdf:langString ; - sh:path schema:inLanguage ], - - [ sh:datatype xsd:dateTime ; - sh:path prov:startedAtTime ], - - [ sh:datatype xsd:dateTime ; - sh:path prov:endedAtTime ], - - [ sh:node prov:SoftwareAgentShape ; - sh:path prov:wasAssociatedWith ], - + sh:path skos:prefLabel ], + [ sh:datatype xsd:boolean ; + sh:path schema:valueRequired ], [ sh:datatype rdf:langString ; - sh:path prov:generated ] ; - sh:targetClass reproschema:ResponseActivity . + sh:path reproschema:randomMaxDelay ; + sh:pattern "^P(?!$)(\\d+(?:\\.\\d+)?Y)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?W)?(\\d+(?:\\.\\d+)?D)?(T(?=\\d)(\\d+(?:\\.\\d+)?H)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?S)?)?$" ], + [ sh:datatype rdf:langString ; + sh:or ( [ sh:pattern "^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ] [ sh:pattern "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" ] [ sh:pattern "^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?(\\/)([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ] [ sh:pattern "^(R\\d*\\/)?([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\4([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\18[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?(\\/)P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" ] [ sh:pattern "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?\\/([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\4([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\18[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ] ) ; + sh:path reproschema:schedule ], + [ sh:datatype rdf:langString ; + sh:or ( [ sh:pattern "^P(?!$)(\\d+(?:\\.\\d+)?Y)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?W)?(\\d+(?:\\.\\d+)?D)?(T(?=\\d)(\\d+(?:\\.\\d+)?H)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?S)?)?$" ] [ sh:pattern "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?\\/([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\4([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\18[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ] ) ; + sh:path reproschema:limit ], + [ sh:datatype xsd:integer ; + sh:path reproschema:maxRetakes ] ; + sh:targetClass reproschema:OverrideProperty . -prov:SoftwareAgentShape a sh:NodeShape ; +reproschema:MediaObjectShape a sh:NodeShape ; sh:closed true ; sh:ignoredProperties ( rdf:type ) ; - sh:property [ sh:datatype rdf:langString ; - sh:path schema:version ], - - [ sh:nodeKind sh:IRI ; - sh:path schema:url ] ; - sh:targetClass reproschema:SoftwareAgent . + sh:property [ sh:minCount 1 ; + sh:nodeKind sh:IRI ; + sh:path schema:contentUrl ], + [ sh:datatype rdf:langString ; + sh:maxCount 1 ; + sh:path schema:inLanguage ] . diff --git a/setup.cfg b/setup.cfg index e58354d..ef22d32 100644 --- a/setup.cfg +++ b/setup.cfg @@ -18,6 +18,8 @@ classifiers = Operating System :: MacOS :: MacOS X Operating System :: POSIX :: Linux Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 Topic :: Scientific/Engineering [options] diff --git a/setup.py b/setup.py index 9a2b714..b96c493 100755 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- # vi: set ft=python sts=4 ts=4 sw=4 et: -"""Pydra: Dataflow Engine +"""Reproschema """ import sys