diff --git a/hepdata/config.py b/hepdata/config.py index cbbc39de3..9eb482177 100644 --- a/hepdata/config.py +++ b/hepdata/config.py @@ -331,9 +331,7 @@ def _(x): 'description': 'MadAnalysis 5 analysis' }, 'SModelS': { - 'endpoint_url': 'https://zenodo.org/records/13952092/files/smodels-analyses.hepdata.json?download=1', - 'url_template': '{0}', - 'description': 'SModelS analysis', + 'endpoint_url': 'https://smodels.github.io/docs/smodels-analyses.hepdata.json', 'subscribe_user_id': 7766 }, 'CheckMATE': { diff --git a/hepdata/modules/records/utils/analyses.py b/hepdata/modules/records/utils/analyses.py index 188949e0e..a33f3328c 100644 --- a/hepdata/modules/records/utils/analyses.py +++ b/hepdata/modules/records/utils/analyses.py @@ -23,11 +23,14 @@ # as an Intergovernmental Organization or submit itself to any jurisdiction. import logging +import os from celery import shared_task from flask import current_app from invenio_db import db import requests +import json +import jsonschema from hepdata.ext.opensearch.api import index_record_ids from hepdata.modules.submission.api import get_latest_hepsubmission, is_resource_added_to_submission @@ -40,6 +43,11 @@ logging.basicConfig() log = logging.getLogger(__name__) +def test_analyses_schema(json_file, schema_version="1.0.0"): + schema_path = os.path.join("hepdata", "templates", "analyses_schema", schema_version, "analyses_schema.json") + with open(schema_path) as f: + schema = json.load(f) + jsonschema.validate(instance=json_file, schema=schema) @shared_task def update_analyses(endpoint=None): @@ -50,6 +58,7 @@ def update_analyses(endpoint=None): :param endpoint: either "rivet" or "MadAnalysis" or "SModelS" or "CheckMATE" or "HackAnalysis" or "Combine" or None (default) for all """ + endpoints = current_app.config["ANALYSES_ENDPOINTS"] for analysis_endpoint in endpoints: @@ -64,62 +73,130 @@ def update_analyses(endpoint=None): if response and response.status_code == 200: - analyses = response.json() - analysis_resources = DataResource.query.filter_by(file_type=analysis_endpoint).all() - # Check for missing analyses. - for record in analyses: - submission = get_latest_hepsubmission(inspire_id=record, overall_status='finished') - - if submission: - num_new_resources = 0 - - for analysis in analyses[record]: - _resource_url = endpoints[analysis_endpoint]["url_template"].format(analysis) - - if not is_resource_added_to_submission(submission.publication_recid, submission.version, - _resource_url): - - log.info('Adding {} analysis to ins{} with URL {}'.format( - analysis_endpoint, record, _resource_url) - ) - new_resource = DataResource( - file_location=_resource_url, - file_type=analysis_endpoint) - - if "description" in endpoints[analysis_endpoint]: - new_resource.file_description = str(endpoints[analysis_endpoint]["description"]) + r_json = response.json() - if "license" in endpoints[analysis_endpoint]: - resource_license = get_license(endpoints[analysis_endpoint]["license"]) - new_resource.file_license = resource_license.id + schema_version = "0.1.0" # default to 0.1.0 for backward compatibility when schema_version field is missing + if "schema_version" in r_json: + schema_version = r_json["schema_version"] - submission.resources.append(new_resource) - num_new_resources += 1 + # Validate analyses JSON file against the schema. + try: + test_analyses_schema(r_json, schema_version=schema_version) + except jsonschema.exceptions.ValidationError as e: + log.error("Validation error for analyses schema {0} in {1}: {2}".format(schema_version, analysis_endpoint, e)) + continue - else: - - # Remove resources from 'analysis_resources' list. - resources = list(filter(lambda a: a.file_location == _resource_url, analysis_resources)) - for resource in resources: - analysis_resources.remove(resource) - - if num_new_resources: - - try: - db.session.add(submission) - db.session.commit() - latest_submission = get_latest_hepsubmission(inspire_id=record) - if submission.version == latest_submission.version: - index_record_ids([submission.publication_recid]) - except Exception as e: - db.session.rollback() - log.error(e) - - else: - log.debug("An analysis is available in {0} but with no equivalent in HEPData (ins{1}).".format( - analysis_endpoint, record)) + if schema_version == "0.1.0": + analyses = r_json + + # Check for missing analyses. + for record in analyses: + submission = get_latest_hepsubmission(inspire_id=record, overall_status='finished') + + if submission: + num_new_resources = 0 + + for analysis in analyses[record]: + _resource_url = endpoints[analysis_endpoint]["url_template"].format(analysis) + + if not is_resource_added_to_submission(submission.publication_recid, submission.version, + _resource_url): + + log.info('Adding {} analysis to ins{} with URL {}'.format( + analysis_endpoint, record, _resource_url) + ) + new_resource = DataResource( + file_location=_resource_url, + file_type=analysis_endpoint) + + if "description" in endpoints[analysis_endpoint]: + new_resource.file_description = str(endpoints[analysis_endpoint]["description"]) + + if "license" in endpoints[analysis_endpoint]: + resource_license = get_license(endpoints[analysis_endpoint]["license"]) + new_resource.file_license = resource_license.id + + submission.resources.append(new_resource) + num_new_resources += 1 + + else: + + # Remove resources from 'analysis_resources' list. + resources = list(filter(lambda a: a.file_location == _resource_url, analysis_resources)) + for resource in resources: + analysis_resources.remove(resource) + + if num_new_resources: + + try: + db.session.add(submission) + db.session.commit() + latest_submission = get_latest_hepsubmission(inspire_id=record) + if submission.version == latest_submission.version: + index_record_ids([submission.publication_recid]) + except Exception as e: + db.session.rollback() + log.error(e) + + else: + log.debug("An analysis is available in {0} but with no equivalent in HEPData (ins{1}).".format( + analysis_endpoint, record)) + + else: # schema_version >= "1.0.0" + # Check for missing analyses. + for ana in r_json["analyses"]: + inspire_id = str(ana["inspire_id"]) # inspire_id is stored as a string in the database + submission = get_latest_hepsubmission(inspire_id=inspire_id, overall_status='finished') + + if submission: + num_new_resources = 0 + + for implementation in ana["implementations"]: + _resource_url = r_json["url_templates"]["main_url"].format(**implementation) + + if not is_resource_added_to_submission(submission.publication_recid, submission.version, + _resource_url): + + log.info('Adding {} analysis to ins{} with URL {}'.format( + analysis_endpoint, inspire_id, _resource_url) + ) + new_resource = DataResource( + file_location=_resource_url, + file_type=analysis_endpoint, + file_description=r_json["implementations_description"] + ) + + if "implementations_license" in r_json: + resource_license = get_license(r_json["implementations_license"]) + new_resource.file_license = resource_license.id + + submission.resources.append(new_resource) + num_new_resources += 1 + + else: + + # Remove resources from 'analysis_resources' list. + resources = list(filter(lambda a: a.file_location == _resource_url, analysis_resources)) + for resource in resources: + analysis_resources.remove(resource) + + if num_new_resources: + + try: + db.session.add(submission) + db.session.commit() + latest_submission = get_latest_hepsubmission(inspire_id=inspire_id) + if submission.version == latest_submission.version: + index_record_ids([submission.publication_recid]) + except Exception as e: + db.session.rollback() + log.error(e) + + else: + log.debug("An analysis is available in {0} but with no equivalent in HEPData (ins{1}).".format( + analysis_endpoint, inspire_id)) if analysis_resources: # Extra resources that were not found in the analyses JSON file. @@ -154,10 +231,19 @@ def update_analyses(endpoint=None): if "subscribe_user_id" in endpoints[analysis_endpoint]: user = get_user_from_id(endpoints[analysis_endpoint]["subscribe_user_id"]) if user: - for record in analyses: - submission = get_latest_hepsubmission(inspire_id=record, overall_status='finished') - if submission and not is_current_user_subscribed_to_record(submission.publication_recid, user): - subscribe(submission.publication_recid, user) + # Check for missing analyses. + if schema_version == "0.1.0": + for record in analyses: + submission = get_latest_hepsubmission(inspire_id=record, overall_status='finished') + if submission and not is_current_user_subscribed_to_record(submission.publication_recid, user): + subscribe(submission.publication_recid, user) + + else: # schema_version >= "1.0.0" + for ana in r_json["analyses"]: + submission = get_latest_hepsubmission(inspire_id=str(ana["inspire_id"]), overall_status='finished') + if submission and not is_current_user_subscribed_to_record(submission.publication_recid, user): + subscribe(submission.publication_recid, user) + else: - log.debug("No endpoint url configured for {0}".format(analysis_endpoint)) + log.debug("No endpoint_url configured for {0}".format(analysis_endpoint)) diff --git a/hepdata/modules/theme/views.py b/hepdata/modules/theme/views.py index 5ecc16035..99ccc9ddb 100644 --- a/hepdata/modules/theme/views.py +++ b/hepdata/modules/theme/views.py @@ -25,8 +25,9 @@ """Theme blueprint in order for template and static files to be loaded.""" import re +import json -from flask import Blueprint, render_template, current_app, redirect, request, url_for +from flask import Blueprint, render_template, current_app, redirect, request, url_for, jsonify from hepdata_validator import LATEST_SCHEMA_VERSION, RAW_SCHEMAS_URL from hepdata.modules.email.utils import send_flask_message_email @@ -84,6 +85,13 @@ def submission_schema(jsonschema): return redirect(RAW_SCHEMAS_URL + '/' + jsonschema) +@blueprint.route('/analyses/schemas/') +def analyses_schema(jsonschema): + with current_app.open_resource('templates/analyses_schema/' + jsonschema) as jsonfile: + schema = json.load(jsonfile) + return jsonify(schema) + + @blueprint.route('/cookies') def cookie_policy(): return render_template('hepdata_theme/pages/cookies.html') diff --git a/hepdata/templates/analyses_schema/0.1.0/analyses_schema.json b/hepdata/templates/analyses_schema/0.1.0/analyses_schema.json new file mode 100644 index 000000000..b71529ada --- /dev/null +++ b/hepdata/templates/analyses_schema/0.1.0/analyses_schema.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hepdata.net/analyses/schemas/0.1.0/analyses_schema.json", + "title": "HEPData analysis tool schema", + "description": "A JSON schema for tracking implementations of HEPData analyses in different tools", + "type": "object", + "patternProperties": { + "^[0-9]+$": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + } + }, + "additionalProperties": false +} diff --git a/hepdata/templates/analyses_schema/0.1.0/readme.md b/hepdata/templates/analyses_schema/0.1.0/readme.md new file mode 100644 index 000000000..40f80d227 --- /dev/null +++ b/hepdata/templates/analyses_schema/0.1.0/readme.md @@ -0,0 +1,38 @@ +# About the analyses JSON schema + +This readme details a JSON schema which is used by reinterpretation tools to communicate to HEPData which analyses are implemented in that tool and where to find the implementations. + +## The standard + +The standard is quite simple: the whole file is basically a dictionary where the keys are the different INSPIRE IDs for the analyses implemented in the tool and the values are lists of tool-internal names for the reimplentations, i.e. +```JSON +{ + "": ["", ""] +} +``` + +No other fields are allowed. + +## Example +A minimal example for an analyses JSON adhering to the standard looks like this: +```JSON +{ + "100592": ["MARKI_1975_I100592", "MARKI_ALTERNATIVE_IMPLEMENTATION"], + "1081268": ["LHCB_2013_I1081268"] +} +``` + +## Testing an implementation + +Whether an analyses JSON file adheres to the standard defined here, can be checked with python as follows: +```python +import json +import jsonschema + +with open("analyses_schema.json") as f: + schema = json.load(f) +with open("analyses_example.json") as f: + test = json.load(f) + +jsonschema.validate(instance=test, schema=schema) +``` \ No newline at end of file diff --git a/hepdata/templates/analyses_schema/1.0.0/analyses_schema.json b/hepdata/templates/analyses_schema/1.0.0/analyses_schema.json new file mode 100644 index 000000000..d86771cbd --- /dev/null +++ b/hepdata/templates/analyses_schema/1.0.0/analyses_schema.json @@ -0,0 +1,141 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hepdata.net/analyses/schemas/1.0.0/analyses_schema.json", + "title": "HEPData analysis tool schema", + "description": "A JSON schema for tracking implementations of HEPData analyses in different tools", + "type": "object", + "required": ["schema_version", "tool", "version", "date_created", "implementations_description", "url_templates", "analyses"], + + "properties": { + "schema_version": { + "description": "The version of the JSON schema applying to this file", + "const": "1.0.0" + }, + + "tool": { + "description": "The name of the tool used to implement the analyses", + "type": "string" + }, + + "version": { + "description": "The version of the tool used to implement the analyses", + "type": "string" + }, + + "date_created": { + "description": "The date at which the JSON file was created, formatted as RFC 3339, section 5.6 (https://json-schema.org/understanding-json-schema/reference/type#dates-and-times), e.g. 2018-11-13T20:20:39+00:00", + "type": "string", + "format": "date-time" + }, + + "implementations_description": { + "description": "The type of information provided for the analyses by the tool", + "type": "string" + }, + + "url_templates": { + "description": "Templates for URLs to the main repository and important other pages", + "type": "object", + "required": ["main_url"], + + "properties": { + "main_url": { + "description": "The URL template for the main repository. Should contain e.g. a {name} placeholder for the analysis name.", + "type": "string" + }, + "val_url": { + "description": "The URL template for the validation page. Should contain e.g. a {name} placeholder for the analysis name.", + "type": "string" + } + } + }, + + "analyses": { + "description": "The analyses implemented in the tool", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "object", + "$ref": "#/$defs/Analysis" + } + }, + + "implementations_license": { + "description": "The license for the implementations of the analyses in the tool. Taken to be CC0 if not specified.", + "type": "object", + "required": ["name", "url"], + "additionalProperties": false, + + "properties": { + "name": { + "description": "The name of the license", + "type": "string", + "maxLength": 256 + }, + "url": { + "description": "The URL to the license", + "type": "string", + "maxLength": 256 + }, + "description": { + "description": "A description of the license", + "type": "string" + } + } + } + }, + + "$defs": { + + "Analysis": { + "description": "An analysis, identified by the INSPIRE ID, implemented at least once in the tool", + "type": "object", + "required": ["inspire_id", "implementations"], + + "properties": { + "inspire_id": { + "description": "The INSPIRE ID of the analysis", + "type": "number" + }, + "implementations":{ + "description": "The implementations of the analysis in the tool", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "object", + "$ref": "#/$defs/Implementation" + } + }, + "signature_type": { + "description": "The signature of the analysis, e.g. 'prompt', 'displaced'", + "type": "string" + }, + "pretty_name": { + "description": "A pretty name for the analysis", + "type": "string" + } + } + }, + + "Implementation": { + "description": "An implementation of an analysis in the tool, giving the internal name to retrieve information", + "type": "object", + "required": ["name"], + + "properties": { + "name": { + "description": "Internal name of the implementation", + "type": "string" + }, + "path": { + "description": "The path to the implementation in the tool", + "type": "string" + } + } + } + + } + +} diff --git a/hepdata/templates/analyses_schema/1.0.0/readme.md b/hepdata/templates/analyses_schema/1.0.0/readme.md new file mode 100644 index 000000000..0a3132242 --- /dev/null +++ b/hepdata/templates/analyses_schema/1.0.0/readme.md @@ -0,0 +1,154 @@ +# About the analyses JSON schema + +This readme details a JSON schema which is used by reinterpretation tools to communicate to HEPData which analyses are implemented in that tool and where to find the implementations. + +## Goals +- **Self-descriptiveness**: the JSON format includes information about the tool and tool version it's valid for as well as basic information of the analyses implemented in the tool. + It also allows tools to include very rough human-readable information instead of just bare identifiers. +- **Standardisation**: a common standard for everyone ensures easy exchange and findability of information. +- **Future-proofness**: the standard aims to foresee future needs such that it doesn't require frequent updates. +- **Redundancy reduction**: the JSON format allows to codify URLs such that the URL stem doesn't have to be repeated. + This makes it more compact, better human-readable and better maintainable. + +## The standard + +### Required fields +The following fields are required by the analyses JSON standard: +- **schema_version** (`const`): the version of the analyses JSON schema applying the the file. + Currently 1.0.0. +- **tool** (`string`): the name of the tool used to implement the analyses. +- **version** (`string`): the version of the tool used to implement the analyses. +- **date_created** (`string` in `date-time` format): the date at which the JSON file was created, formatted as [RFC 3339, section 5.6](https://json-schema.org/understanding-json-schema/reference/type#dates-and-times), e.g. "2018-11-13T20:20:39+00:00". +- **implementations_description** (`string`): the type of information provided for the analyses by the tool. + This information is used to provide text describing links to the analysis implementation on HEPData and INSPIRE. +- **url_templates** (`dict`): a dictionary of templates for URLs to the main tool repository and important other pages. + + It has to include the following fields: + - **main_url** (`string`): the URL template for the main repository. + Should contain e.g. a "{name}" placeholder for the analysis name. + Placeholders are retrieved from the "analyses/implementations" field and are not allowed to reference one another, i.e. + ```JSON + { + [...] + "url_templates": { + "main_url": "https://hepdata.net/{name}", + }, + "analyses": [ + { + [...] + "implementations": [ + { + "name": "ANA1", + "path": "13TeV/EXP1" + } + ] + } + ] + } + ``` + is allowed but + ```JSON + { + [...] + "url_templates": { + "main_url": "https://hepdata.net/{path}", + }, + "analyses": [ + { + [...] + "implementations": [ + { + "name": "ANA1", + "path": "13TeV/EXP1/{name}" + } + ] + } + ] + } + ``` + is not. +- **analyses** (`array`): an array of analyses implemented in the tool. + All entries have to be unique. + Needs at least one entry. + Each array item has to have the following fields: + - **inspire_id** (`number`): the INSPIRE ID of the analysis. + - **implementations** (`array`): an array of the various implementations of the analysis in the tool. + All entries have to be unique. + Needs at least one entry. + + Each array item has to have the following fields: + - **name** (`string`): the internal name of the implementation used to retrieve information. + Is used to fill placeholders for URLs. + +### Additional standardised fields +The following fields are included in the standard but not required: + +- **url_templates** (`dict`): the URL templates dict can also have the following fields: + - **val_url** (`string`): the URL template for the validation page. + Should contain e.g. a `{name}` placeholder for the analysis name. +- **analyses** (`array`): the analyses array can also have the following fields: + - **signature_type** (`string`): the signature of the analysis, e.g. 'prompt', 'displaced'. + - **pretty_name** (`string`): a pretty name for the analysis. + - **implementations** (`array`): the implementations array can also have the following fields: + - **path** (`string`): the path to the implementation in the tool. + Is used to fill placeholders for URLs but must not reference other fields for this implementation. +- **implementations_license** (`dict`): a dictionary describing the license for the implementations of the analyses in the tool. + Taken to be CC0 if not specified. + + It *has to* include the following fields: + - **name** (`string`): the name of the license. + The maximum length for this field is 256 characters. + - **url** (`string`): the URL to the license. + The maximum length for this field is 256 characters. + + It *can* include the following fields: + - **description** (`string`): a description of the license + + No other fields are allowed. + + +### Additional unknown fields +Apart from the fields mentioned above, the standard allows for any number of additional fields. +These are however not standardised and not checked by the schema. + + +## Examples +A minimal example for an analyses JSON adhering to the standard looks like this: +```JSON +{ + "schema_version": "1.0.0", + "tool": "SModelS", + "version": "3.0.0", + "date_created": "2018-11-13T20:20:39+00:00", + "implementations_description": "SModelS analysis", + "url_templates": { + "main_url": "https://github.com/SModelS/smodels-database-release/tree/main/{name}" + }, + "analyses": [ + { + "inspire_id": 1795076, + "implementations": [ + { + "name": "ATLAS-EXOT-2018-48", + } + ] + } + ] +} +``` +See [here](../../../../tests/test_data/analyses_example.json) for a more elaborate example. + +## Testing an implementation + +Whether an analyses JSON file adheres to the standard defined here, can be checked with python as follows: +```python +import json +import jsonschema + +with open("analyses_schema.json") as f: + schema = json.load(f) +with open("analyses_example.json") as f: + test = json.load(f) + +jsonschema.validate(instance=test, schema=schema) +``` \ No newline at end of file diff --git a/hepdata/version.py b/hepdata/version.py index b4655d31a..7e09878b0 100644 --- a/hepdata/version.py +++ b/hepdata/version.py @@ -28,4 +28,4 @@ and parsed by ``setup.py``. """ -__version__ = "0.9.4dev20250903" +__version__ = "0.9.4dev20250905" diff --git a/tests/analyses_schema_test.py b/tests/analyses_schema_test.py new file mode 100644 index 000000000..8a6190d87 --- /dev/null +++ b/tests/analyses_schema_test.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# +# This file is part of HEPData. +# Copyright (C) 2016 CERN. +# +# HEPData is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# HEPData is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with HEPData; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307, USA. +# +# In applying this license, CERN does not +# waive the privileges and immunities granted to it by virtue of its status +# as an Intergovernmental Organization or submit itself to any jurisdiction. +import json +import os + +import hepdata.modules.records.utils.analyses as analyses + +def test_analyses_json_schema(): + base_dir = os.path.dirname(os.path.realpath(__file__)) + test_file_name = os.path.join(base_dir, "test_data", "analyses_example.json") + + with open(test_file_name) as f: + analyses.test_analyses_schema(json.load(f)) + +if __name__ == "__main__": + test_analyses_json_schema() diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py index 7973ca477..8757f3e84 100644 --- a/tests/e2e/conftest.py +++ b/tests/e2e/conftest.py @@ -247,11 +247,16 @@ def finalizer(): # Filter out error message for: # WARNING: security - Error with Permissions-Policy header: # Origin trial controlled feature not enabled: 'interest-cohort' - temp_log = [t for t in log if 'interest-cohort' not in t['message']] + log = [t for t in log if 'interest-cohort' not in t['message']] - assert len(temp_log) == 0, \ + # Filter out error message for: + # SEVERE: http://localhost:5555/favicon.ico - Failed to load resource: + # the server responded with a status of 404 (Not Found) + log = [t for t in log if 'favicon.ico' not in t['message']] + + assert len(log) == 0, \ "Errors in browser log:\n" + \ - "\n".join([f"{line['level']}: {line['message']}" for line in temp_log]) + "\n".join([f"{line['level']}: {line['message']}" for line in log]) @pytest.fixture() diff --git a/tests/e2e/test_general.py b/tests/e2e/test_general.py index c31c6e738..2dee10b84 100644 --- a/tests/e2e/test_general.py +++ b/tests/e2e/test_general.py @@ -37,6 +37,7 @@ from hepdata.ext.opensearch.api import reindex_all from hepdata.modules.submission.api import get_latest_hepsubmission from hepdata.modules.records.utils.submission import unload_submission +from hepdata_validator import LATEST_SCHEMA_VERSION, RAW_SCHEMAS_URL def test_home(app, live_server, env_browser, e2e_identifiers): @@ -219,21 +220,37 @@ def test_general_pages(live_server, env_browser): """Test general pages can be loaded without errors""" browser = env_browser - browser.get(flask.url_for('hepdata_theme.about', _external=True)) - assert (flask.url_for('hepdata_theme.about', _external=True) in - browser.current_url) + url = flask.url_for('hepdata_theme.about', _external=True) + browser.get(url) + assert url in browser.current_url - browser.get(flask.url_for('hepdata_theme.submission_help', _external=True)) - assert (flask.url_for('hepdata_theme.submission_help', _external=True) in - browser.current_url) + url = flask.url_for('hepdata_theme.submission_help', _external=True) + browser.get(url) + assert url in browser.current_url - browser.get(flask.url_for('hepdata_theme.terms', _external=True)) - assert (flask.url_for('hepdata_theme.terms', _external=True) in - browser.current_url) + url = flask.url_for('hepdata_theme.terms', _external=True) + browser.get(url) + assert url in browser.current_url - browser.get(flask.url_for('hepdata_theme.cookie_policy', _external=True)) - assert (flask.url_for('hepdata_theme.cookie_policy', _external=True) in - browser.current_url) + url = flask.url_for('hepdata_theme.cookie_policy', _external=True) + browser.get(url) + assert url in browser.current_url + + url = flask.url_for('hepdata_theme.submission_schema', jsonschema='submission_schema.json', _external=True) + browser.get(url) + assert RAW_SCHEMAS_URL + '/' + LATEST_SCHEMA_VERSION + '/submission_schema.json' in browser.current_url + + url = flask.url_for('hepdata_theme.analyses_schema', jsonschema='1.0.0/analyses_schema.json', _external=True) + browser.get(url) + assert url in browser.current_url + + url = flask.url_for('hepdata_theme.formats', _external=True) + browser.get(url) + assert url in browser.current_url + + url = flask.url_for('hepdata_theme.ping', _external=True) + browser.get(url) + assert url in browser.current_url def test_accept_headers(app, live_server, e2e_identifiers): diff --git a/tests/records_test.py b/tests/records_test.py index 1f6e2cc6f..3f422da99 100644 --- a/tests/records_test.py +++ b/tests/records_test.py @@ -33,6 +33,7 @@ import tempfile import datetime +from flask import current_app from flask_login import login_user from invenio_accounts.models import User from invenio_db import db @@ -1075,11 +1076,18 @@ def test_update_analyses(app): db.session.commit() update_analyses('SModelS') analysis_resources = DataResource.query.filter_by(file_type='SModelS').all() - assert len(analysis_resources) == 1 - assert analysis_resources[0].file_location == 'https://smodels.github.io/docs/ListOfAnalyses#ATLAS-EXOT-2018-06' + assert len(analysis_resources) == 2 + assert analysis_resources[0].file_location == 'https://github.com/SModelS/smodels-database-release/tree/main/13TeV/ATLAS/ATLAS-EXOT-2018-06/' + assert License.query.filter_by(id=analysis_resources[0].file_license).first().name == 'cc-by-4.0' submission = get_latest_hepsubmission(inspire_id='1847779', overall_status='finished') assert is_current_user_subscribed_to_record(submission.publication_recid, user) + # Call update_analyses() again: should be no further changes (but covers more lines of code) + update_analyses('SModelS') + analysis_resources = DataResource.query.filter_by(file_type='SModelS').all() + assert len(analysis_resources) == 2 + assert analysis_resources[0].file_location == 'https://github.com/SModelS/smodels-database-release/tree/main/13TeV/ATLAS/ATLAS-EXOT-2018-06/' + # ins1847779 also has a CheckMATE analysis, so don't need to import another record analysis_resources = DataResource.query.filter_by(file_type='CheckMATE').all() assert len(analysis_resources) == 0 @@ -1123,6 +1131,14 @@ def test_update_analyses(app): assert license_data.name == 'cc-by-4.0' assert license_data.url == 'https://creativecommons.org/licenses/by/4.0' + # Call update_analysis using an endpoint with no endpoint_url + current_app.config["ANALYSES_ENDPOINTS"]["TestAnalysis"] = {} + update_analyses('TestAnalysis') + + # Call update_analyses using an endpoint_url that will fail validation. + current_app.config["ANALYSES_ENDPOINTS"]["TestAnalysis"]['endpoint_url'] = 'https://www.hepdata.net/search/?format=json&size=1' + update_analyses('TestAnalysis') + def test_generate_license_data_by_id(app): """ @@ -1359,7 +1375,7 @@ def test_version_related_functions(app): expected_backward_sub_relations = [] # Finished records will have other record references appear - if test["overall_status"] is not "todo": + if test["overall_status"] != "todo": expected_backward_sub_relations.append(test["other_recid"]) assert [sub.publication_recid for sub in backward_sub_relations] == expected_backward_sub_relations @@ -1382,7 +1398,7 @@ def test_version_related_functions(app): expected_backward_dt_relations = [] # We expect unfinished records to NOT have `other_recid` tables - if test["overall_status"] is not "todo": + if test["overall_status"] != "todo": expected_backward_dt_relations.append(f"10.17182/hepdata.{test['other_recid']}.v2/t{table_number}") # Here we expect the second table to reference ITS OWN table one diff --git a/tests/test_data/analyses_example.json b/tests/test_data/analyses_example.json new file mode 100644 index 000000000..6ede0ad1f --- /dev/null +++ b/tests/test_data/analyses_example.json @@ -0,0 +1,40 @@ +{ + "schema_version": "1.0.0", + "tool": "SModelS", + "version": "3.0.0", + "date_created": "2018-11-13T20:20:39+00:00", + "implementations_description": "SModelS analysis", + "url_templates": { + "main_url": "https://github.com/SModelS/smodels-database-release/tree/main/{path}", + "val_url": "https://smodels.github.io/docs/Validation#{name}_ul" + }, + "analyses": [ + { + "inspire_id": 1795075, + "implementations": [ + { + "name": "ATLAS-EXOT-2018-47" + } + ] + }, + { + "inspire_id": 1795076, + "signature_type": "prompt", + "pretty_name": "di-top resonance", + "implementations": [ + { + "name": "ATLAS-EXOT-2018-48", + "path": "13TeV/ATLAS/ATLAS-EXOT-2018-48/" + }, + { + "name": "ATLAS-EXOT-2018-48b", + "path": "13TeV/ATLAS/ATLAS-EXOT-2018-48/" + } + ] + } + ], + "implementations_license": { + "name": "cc-by-4.0", + "url": "https://creativecommons.org/licenses/by/4.0" + } +} \ No newline at end of file