diff --git a/uhepp_org/uhepp_api/serializers.py b/uhepp_org/uhepp_api/serializers.py index 031ae0a8cf501eebd306549fa14654aab7f8b0b9..ca3d4f4f5fa10f619ffc8e125b2685eb8b914199 100644 --- a/uhepp_org/uhepp_api/serializers.py +++ b/uhepp_org/uhepp_api/serializers.py @@ -1,7 +1,10 @@ +import json +import os from django.contrib.auth.models import User from django.db.models import Q from django.urls import reverse from rest_framework import serializers +import jsonschema from uhepp_vault.models import PUBLIC_LEVEL, INTERNAL_LEVEL, PUBLIC_LEVEL, \ VISIBILITY_LEVELS, Plot, Collection from .masks import filter_collections @@ -70,6 +73,24 @@ class PlotSerializer(serializers.HyperlinkedModelSerializer): 'collection-detail', queryset=Collection.objects ) + + def validate_uhepp(self, value): + """Check if the JSON data conforms to the uhepp schema""" + try: + schema_path = os.path.join( + os.path.dirname(__file__), + "uhepp.schema.json" + ) + + with open(schema_path) as schema_file: + schema = json.load(schema_file) + + jsonschema.validate(value, schema=schema) + except Exception as e: + raise serializers.ValidationError("JSON schema validation error: %s" % e) + + return value + class Meta: model = Plot fields = ["uuid", "collection", "url", "uhepp"] diff --git a/uhepp_org/uhepp_api/uhepp.schema.json b/uhepp_org/uhepp_api/uhepp.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..ce4c5f4e8740e2803bc13db98298f9a44d4fb70c --- /dev/null +++ b/uhepp_org/uhepp_api/uhepp.schema.json @@ -0,0 +1,277 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://uhepp.org/uhepp_v0_1.schema.json", + "title": "UHepp", + "description": "Universal high-energy physics plots", + "type": "object", + "definitions": { + "style": { + "type": "object", + "patternProperties": { + "^": {"type": ["string", "number"]} + }, + "additionalProperties": false + }, + "error": { + "type": "string", + "enum": ["no", "stat", "syst", "stat+syst", "env", "stat+env", "stat+syst+env"] + }, + "num_data": { + "type": "array", + "items": {"type": "number"}, + "minItems": 1 + }, + "edges": { + "type": "array", + "items": {"type": "number"}, + "minItems": 2 + }, + "pair": { + "type": "array", + "items": {"type": "number"}, + "minItems": 2, + "maxItems": 2 + }, + "yield_list": { + "type": "array", + "items": {"type": "string"}, + "minItems": 1 + } + }, + "properties": { + "version": { + "type": "string", + "enum": ["0.1"] + }, + "type": { + "type": "string", + "enum": ["histogram"] + }, + + "metadata": { + "description": "Additional information about the plot not affecting rendering", + "type": "object", + "properties": { + "filename": {"type": "string"}, + "date": {"type": "string"}, + "Ecm_TeV": {"type": "number"}, + "lumi_ifb": {"type": "number"}, + "author": {"type": "string"}, + "producer": {"type": "string"}, + "code_revision": {"type": "string"}, + "event_selection": {"type": "string"}, + "tags": { + "type": "object", + "patternProperties": { + "^": {"type": ["string", "null"]} + }, + "additionalProperties": false + } + }, + "required": ["filename", "date"], + "additionalProperties": false + }, + "stacks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["step", "stepfilled", "points"] + }, + "error": {"$ref": "#/definitions/error"}, + "content": { + "type": "array", + "items": { + "type": "object", + "properties": { + "yield": {"$ref": "#/definitions/yield_list"}, + "label": {"type": "string"}, + "style": {"$ref": "#/definitions/style"} + }, + "required": ["yield", "label"], + "additionalProperties": false + } + } + }, + "required": ["type"], + "additionalProperties": false + } + }, + + "ratio": { + "description": "Specification of the ratio plots", + "type": "array", + "items": { + "type": "object", + "properties": { + "numerator": {"$ref": "#/definitions/yield_list"}, + "denominator": {"$ref": "#/definitions/yield_list"}, + "style": {"$ref": "#/definitions/style"}, + "type": { + "type": "string", + "enum": ["step", "stepfilled", "points"] + }, + "error": {"$ref": "#/definitions/error"} + }, + "required": ["numerator"], + "additionalProperties": false + } + }, + + "y_axis": { + "description": "Options regarding the y-axis of the histogram", + "type": "object", + "properties": { + "min": {"type": "number"}, + "max": {"type": "number"}, + "label": {"type": "string"}, + "log": {"type": "boolean"} + }, + "additionalProperties": false + }, + + "ratio_axis": { + "description": "Options regarding the y-axis of the ratio part of the histogram", + "type": "object", + "properties": { + "min": {"type": "number"}, + "max": {"type": "number"}, + "label": {"type": "string"}, + "log": {"type": "boolean"}, + "diff": {"type": "boolean"} + }, + "additionalProperties": false + }, + + "badge": { + "description": "Options related to branding and labeling of the plot", + "type": "object", + "properties": { + "brand": {"type": ["string", "null"]}, + "label": {"type": "string"}, + "subtext": {"type": "string"} + }, + "required": ["brand"], + "additionalProperties": false + }, + + "variable": { + "description": "Specification of the x-axis of the histogram", + "type": "object", + "properties": { + "symbol": {"type": "string"}, + "unit": {"type": "string"}, + "name": {"type": "string"}, + "log": {"type": "boolean"} + }, + "required": ["symbol"], + "additionalProperties": false + }, + + "bins": { + "description": "Specification of the original and target bin edges", + "type": "object", + "properties": { + "edges": {"$ref": "#/definitions/edges"}, + "rebin": {"$ref": "#/definitions/edges"}, + "include_underflow": {"type": "boolean"}, + "include_overflow": {"type": "boolean"}, + "density_width": {"type": "boolean"} + }, + "required": ["edges"], + "additionalProperties": false + }, + + "yields": { + "description": "Raw data storage", + "type": "object", + "patternProperties": { + "^": { + "type": "object", + "properties": { + "base": {"$ref": "#/definitions/num_data"}, + "syst": {"$ref": "#/definitions/num_data"}, + "stat": {"$ref": "#/definitions/num_data"}, + "var_up": { + "type": "object", + "patternProperties": { + "^": {"$ref": "#/definitions/num_data"} + } + }, + "var_down": { + "type": "object", + "patternProperties": { + "^": {"$ref": "#/definitions/num_data"} + } + } + }, + "required": ["base"], + "additionalProperties": false + } + } + }, + + "h_lines": { + "description": "Specification of horizontal line", + "type": "array", + "items": { + "type": "object", + "properties": { + "y": {"type": "number"}, + "range": {"$ref": "#/definitions/pair"}, + "style": {"$ref": "#/definitions/style"} + }, + "additionalProperties": false, + "required": ["y"] + } + }, + + "v_lines": { + "description": "Specification of vertical line", + "type": "array", + "items": { + "type": "object", + "properties": { + "x": {"type": "number"}, + "range": {"$ref": "#/definitions/pair"}, + "style": {"$ref": "#/definitions/style"} + }, + "additionalProperties": false, + "required": ["x"] + } + }, + + "graphs": { + "description": "Data and style of unbinned x-y-graphs", + "type": "array", + "items": { + "type": "object", + "properties": { + "x": {"$ref": "#/definitions/num_data"}, + "y": {"$ref": "#/definitions/num_data"}, + "x_errors": {"$ref": "#/definitions/num_data"}, + "y_errors": {"$ref": "#/definitions/num_data"}, + "type": {"type": ["number", "string"]}, + "label": {"type": "string"}, + "style": {"$ref": "#/definitions/style"} + }, + "additionalProperties": false, + "required": ["x", "y"] + } + }, + + "layout": { + "description": "Specification of the canvas and its layout", + "type": "object", + "properties": { + "size": {"$ref": "#/definitions/pair"}, + "ratio_fraction": {"type": "number"} + }, + "additionalProperties": false + } + }, + "required": ["version", "type", "bins", "variable", "metadata", "stacks", "yields"], + "additionalProperties": false +} diff --git a/uhepp_org/uhepp_org/settings.py b/uhepp_org/uhepp_org/settings.py index ac8e75db1322eccb78915b7f0b6156385d1e3433..c90dbdd5bb781d1e509707edc82ff379a1fd4f9a 100644 --- a/uhepp_org/uhepp_org/settings.py +++ b/uhepp_org/uhepp_org/settings.py @@ -200,3 +200,5 @@ USE_TZ = True STATIC_URL = '/static/' DATETIME_INPUT_FORMATS = '%Y-%m-%d %H:%M' + +SCHEMA_URL = "https://gitlab.cern.ch/fsauerbu/uhepp/-/raw/master/uhepp.schema.json" diff --git a/uhepp_org/uhepp_vault/urls.py b/uhepp_org/uhepp_vault/urls.py index 123e930fed5e7f19d7e9e6ee3c0d374fd206f415..e437ebf1c0a4e366c42eafcbbe277b9b9ed72eef 100644 --- a/uhepp_org/uhepp_vault/urls.py +++ b/uhepp_org/uhepp_vault/urls.py @@ -1,12 +1,14 @@ from django.urls import path, include from django.contrib.auth import views as auth_views +from django.views.generic.base import RedirectView +from django.conf import settings from . import views app_name = 'uhepp_vault' urlpatterns = [ path('', views.home, name='home'), - path('getting-started', views.GettingStartedView.as_view(), name='getting-started'), + path('uhepp_v0_1.schema.json', RedirectView.as_view(url=settings.SCHEMA_URL)), path('account/', views.AccountEditView.as_view(), name='account-detail'), path('login/', auth_views.LoginView.as_view()), path('logout/', views.logout, name='logout'), diff --git a/uhepp_org/uhepp_vault/views.py b/uhepp_org/uhepp_vault/views.py index 8414f4cb581b2d82bd5f28b2bb64e4f2f1a2b798..05cdc64f398a18bd3931c3962dc25f17fd4fda1f 100644 --- a/uhepp_org/uhepp_vault/views.py +++ b/uhepp_org/uhepp_vault/views.py @@ -245,10 +245,6 @@ def plot_download(request, uuid): response["Content-Disposition"] = f'attachment; filename="{plot}.json"' return response -class GettingStartedView(generic.TemplateView): - template_name = "uhepp_vault/getting_started.html" - - def logout(request): if request.user.is_authenticated: return render(request, "uhepp_vault/logout.html")