From b44c60434aea7f3748d427c19b7802e8b02ceb41 Mon Sep 17 00:00:00 2001 From: Frank Sauerburger <frank@sauerburger.com> Date: Fri, 8 Jan 2021 16:58:03 +0100 Subject: [PATCH] Validate JSON schema --- uhepp_org/uhepp_api/serializers.py | 21 ++ uhepp_org/uhepp_api/uhepp.schema.json | 277 ++++++++++++++++++++++++++ uhepp_org/uhepp_org/settings.py | 2 + uhepp_org/uhepp_vault/urls.py | 4 +- uhepp_org/uhepp_vault/views.py | 4 - 5 files changed, 303 insertions(+), 5 deletions(-) create mode 100644 uhepp_org/uhepp_api/uhepp.schema.json diff --git a/uhepp_org/uhepp_api/serializers.py b/uhepp_org/uhepp_api/serializers.py index 031ae0a..ca3d4f4 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 0000000..ce4c5f4 --- /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 ac8e75d..c90dbdd 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 123e930..e437ebf 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 8414f4c..05cdc64 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") -- GitLab