diff --git a/docs/source/data-model.rst b/docs/source/data-model.rst index 30787eebb..c6928b103 100644 --- a/docs/source/data-model.rst +++ b/docs/source/data-model.rst @@ -139,6 +139,14 @@ A typical example Time is given in UNIX time (seconds since 1970). Software for looking at the data would, of course, translate that into a more human-readable form. +Projections (Experimental): + +The run_start metadata can include a projections field. +It is intended that a projection is an aid to interacting with external systems using standardized vocabularies. +Projections might be used in a variety of use cases such as providing run data to analysis tools or suitcases. +Each projection represents multiple ways to represent data from the run. Each field in the projection dictionary +is an unique and externally-identifiable string and each value is an instruction for accessing dat from the run. + The run start document formal schema: .. literalinclude:: ../../event_model/schemas/run_start.json diff --git a/event_model/schemas/run_start.json b/event_model/schemas/run_start.json index 5557f51a3..aa5d7b008 100644 --- a/event_model/schemas/run_start.json +++ b/event_model/schemas/run_start.json @@ -4,6 +4,42 @@ "title" : "data_type", "patternProperties": {"^([^./]+)$": {"$ref": "#/definitions/data_type"}}, "additionalProperties": false + }, + "projection": { + "description": "Where to get the data from", + "type": "object", + "properties" : { + "type": {"type": "string", "enum": ["linked", "calculated", "static"], "description": "linked: a value linked from the data set, calculated: a value that requires calculation, static: a value defined here in the projection "}, + "stream": {"type": "string"}, + "location": {"enum" : ["event", "configuration"], "description": "event comes from event, configuration comes from fields inthe run_start document"}, + "field": {"type": "string"}, + "calculation": { + "title" : "calculation properties", + "properties": { + "callable": {"type": "string", "description": "callable function to perform calculation"}, + "args": {"type": "array", "decription": "args for calculation callable"}, + "kwargs": {"type": "object", "description": "kwargs for calcalation callable"} + }, + "required": ["callable"] + } + }, + "required" : ["type"], + "additionalProperties": false + }, + "projections": { + "title" : "Describe how to interperet this run as the given projection", + "properties":{ + "name": {"type": "string", "description": "The name of the projection"}, + "version": {"type": "string", "description": "The version of the projection spec. Can specify the version of an external specification."}, + "configuration" : {"type": "object", "description": "Static information about projection"}, + "projection" : { + "type": "object", + "patternProperties": {".": {"$ref": "#/definitions/projection"}}, + "additionalProperties": false + } + }, + "additionalProperties": false, + "required" : ["projection", "version", "configuration"] } }, "properties": { @@ -35,6 +71,10 @@ "type": "string", "description": "Unix owner to associate this data with" }, + "projections": { + "type": "array", + "items": {"$ref": "#/definitions/projections"} + }, "hints": { "type": "object", "description": "Start-level hints", diff --git a/event_model/tests/test_projections.py b/event_model/tests/test_projections.py new file mode 100644 index 000000000..49f038d59 --- /dev/null +++ b/event_model/tests/test_projections.py @@ -0,0 +1,62 @@ +import event_model +import pytest +from jsonschema.exceptions import ValidationError + + +def test_projection_start_doc(): + run_bundle = event_model.compose_run(uid="42", metadata={"projections": valid_projections}) + start_doc = run_bundle.start_doc + assert start_doc['projections'] == valid_projections + + +def test_projection_schema(): + start_doc['projections'] = valid_projections + event_model.schema_validators[event_model.DocumentNames.start].validate(start_doc) + + with pytest.raises(ValidationError): + start_doc['projections'] = invalid_projections + event_model.schema_validators[event_model.DocumentNames.start].validate(start_doc) + + +valid_projections = [ + { + "name": "test", + "version": "42.0.0", + "configuration": {}, + "projection": { + 'entry/instrument/detector/data': { + 'type': 'linked', + 'location': 'event', + 'stream': 'primary', + 'field': 'ccd', + }, + '/entry/instrument/wavelength': { + 'type': 'calculated', + 'calculation': { + 'callable': 'pizza.order:slice', + 'kwargs': {'toppings': 'cheese'} + } + } + }, + } + ] + +invalid_projections = [ + { + "name": "test", + "version": "42.0.0", + "configuration": {}, + "projection": { + 'entry/instrument/detector/data': { + 'location': 'THIS IS NOT VALID', + 'stream': 'primary', + 'field': 'ccd', + }, + }, + } + ] + +start_doc = { + "uid": "abc", + "time": 0, +}