From 99eaa5d14d78f8c1bfc8925550098e774ba73e70 Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Tue, 22 Oct 2024 14:33:20 +0100 Subject: [PATCH 01/25] Added pycharm .venv hidden folder --- .gcloudignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gcloudignore b/.gcloudignore index b69d3d16..8dfecb33 100644 --- a/.gcloudignore +++ b/.gcloudignore @@ -41,6 +41,7 @@ # Virtual env /venv/ +.venv # Other /rest_api/fixtures/ From 0cbad71784923c80244ba6cf845d817f05013694 Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Tue, 22 Oct 2024 14:37:43 +0100 Subject: [PATCH 02/25] New validator app backbone --- pgs_web/settings.py | 3 ++- validator/__init__.py | 0 validator/admin.py | 3 +++ validator/apps.py | 6 ++++++ validator/migrations/__init__.py | 0 validator/models.py | 3 +++ validator/tests.py | 3 +++ 7 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 validator/__init__.py create mode 100644 validator/admin.py create mode 100644 validator/apps.py create mode 100644 validator/migrations/__init__.py create mode 100644 validator/models.py create mode 100644 validator/tests.py diff --git a/pgs_web/settings.py b/pgs_web/settings.py index 3782389c..545c3b3a 100644 --- a/pgs_web/settings.py +++ b/pgs_web/settings.py @@ -55,10 +55,11 @@ # Application definition # #------------------------# INSTALLED_APPS = [ - 'catalog.apps.CatalogConfig', + 'catalog.apps.CatalogConfig', 'rest_api.apps.RestApiConfig', 'search.apps.SearchConfig', 'benchmark.apps.BenchmarkConfig', + 'validator.apps.ValidatorConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', diff --git a/validator/__init__.py b/validator/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/validator/admin.py b/validator/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/validator/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/validator/apps.py b/validator/apps.py new file mode 100644 index 00000000..49e5efce --- /dev/null +++ b/validator/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ValidatorConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'validator' diff --git a/validator/migrations/__init__.py b/validator/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/validator/models.py b/validator/models.py new file mode 100644 index 00000000..71a83623 --- /dev/null +++ b/validator/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/validator/tests.py b/validator/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/validator/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. From 386576327a945cc19400f4f8721d0224b90c995a Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Tue, 22 Oct 2024 16:22:21 +0100 Subject: [PATCH 03/25] Added validator app scripts and static files --- .../static/validator/js/metadata_consumer.js | 116 ++++++++++++++++ validator/static/validator/js/py-worker.js | 31 +++++ .../static/validator/js/scores_consumer.js | 129 ++++++++++++++++++ validator/static/validator/js/webworker.js | 61 +++++++++ .../validator/python/bin/test_services.py | 16 +++ .../python/bin/validation_metadata.py | 91 ++++++++++++ .../validator/python/bin/validation_scores.py | 79 +++++++++++ ..._template_validator-1.1.0-py3-none-any.whl | Bin 0 -> 24486 bytes .../pgscatalog_validate-0.1-py3-none-any.whl | Bin 0 -> 23731 bytes .../template/TemplateColumns2Models.xlsx | Bin 0 -> 17012 bytes .../validator/validate_metadata_client.html | 60 ++++++++ .../validate_scoring_files_client.html | 53 +++++++ validator/urls.py | 7 + validator/views.py | 9 ++ 14 files changed, 652 insertions(+) create mode 100644 validator/static/validator/js/metadata_consumer.js create mode 100644 validator/static/validator/js/py-worker.js create mode 100644 validator/static/validator/js/scores_consumer.js create mode 100644 validator/static/validator/js/webworker.js create mode 100644 validator/static/validator/python/bin/test_services.py create mode 100644 validator/static/validator/python/bin/validation_metadata.py create mode 100644 validator/static/validator/python/bin/validation_scores.py create mode 100644 validator/static/validator/python/wheels/pgs_template_validator-1.1.0-py3-none-any.whl create mode 100644 validator/static/validator/python/wheels/pgscatalog_validate-0.1-py3-none-any.whl create mode 100644 validator/static/validator/template/TemplateColumns2Models.xlsx create mode 100644 validator/templates/validator/validate_metadata_client.html create mode 100644 validator/templates/validator/validate_scoring_files_client.html create mode 100644 validator/urls.py create mode 100644 validator/views.py diff --git a/validator/static/validator/js/metadata_consumer.js b/validator/static/validator/js/metadata_consumer.js new file mode 100644 index 00000000..3e2fa1d8 --- /dev/null +++ b/validator/static/validator/js/metadata_consumer.js @@ -0,0 +1,116 @@ +import "https://cdn.datatables.net/2.0.3/js/dataTables.js" +import { asyncRun } from "./py-worker.js"; + + +const validate_metadata = await fetch(new URL('../python/bin/validation_metadata.py', import.meta.url, null)).then(response => response.text()); +let dirHandle; + + +async function validateFile() { + const fileInput = document.getElementById('myfile'); + const file = fileInput.files[0]; + + if (file) { + const spinner = document.getElementById('pgs_loading'); + spinner.style.visibility = "visible"; + + const reader = new FileReader(); + reader.onload = async function(event) { + const fileContent = new Uint8Array(event.target.result); + let context = { + file_content: fileContent, + file_name: file.name, + dirHandle: dirHandle + } + const { results, error } = await asyncRun(validate_metadata, context); + if(results){ + console.log(results); + spinner.style.visibility = "hidden"; + showResults(results); + } + if(error){ + console.error(error); + spinner.style.visibility = "hidden"; + showSystemError(error); + } + }; + reader.readAsArrayBuffer(file); + } +} + +function report_items_2_html(reports_list) { + let report = '
    '; + $.each(reports_list, function(index, report_item){ + let lines = '' + if (report_item.lines) { + let lines_label = (report_item.lines.length > 1) ? 'Lines' : 'Line'; + lines = lines_label+": "+report_item.lines.join(',')+ ' → '; + } + let message = report_item.message; + // Value highlighting + message = message.replace(/\"(.+?)\"/g, "\"$1\""); + // Leading space + message = message.replace(/\"\s+/g, "\"_"); + // Trailing space + message = message.replace(/\s+<\/b>\"/g, "_\""); + // Column highlighting + message = message.replace(/\'(.+?)\'/g, "\'$1\'"); + report += "
  • "+lines+message+"
  • "; + }); + report += '
'; + return report; +} + +function makeReportTable(data_spreadsheet_items, items_header){ + let table_html = ''+ + ''+ + ''; + $.each(data_spreadsheet_items, function(spreadsheet, reports_list){ + table_html += "'; + }); + table_html += '
Spreadsheet'+items_header+'
"+spreadsheet+""; + table_html += report_items_2_html(reports_list); + table_html += '
'; + return table_html; +} + +function showResults(results){ + let data = JSON.parse(results); + let status_style = (data.status === 'failed') ? ' Failed' : ' Passed'; + let status_html = ''+ + ' '+ + '
File validation'+status_style+'
'; + $('#check_status').html(status_html); + // Error messages + if (data.error) { + let report = '
Error report
' + + makeReportTable(data.error, 'Error message(s)'); + $('#report_error').html(report); + } else { + $('#report_error').html(''); + } + // Warning messages + if (data.warning) { + let report = '
Warning report
' + + makeReportTable(data.warning, 'Warning message(s)'); + $('#report_warning').html(report); + } else { + $('#report_warning').html(''); + } +} + +function showSystemError(errors){ + let status_html = '
File validation: Failed
'; + $('#check_status').html(status_html); + let error_msg = (errors && errors != '') ? errors : 'Internal error'; + let error_html = '
'+ + '
'+ + '
Error: '+error_msg+'
'+ + '
'+ + '
'; + $('#report_error').html(error_html); +} + +document.querySelector('#upload_btn').addEventListener('click', async () => { + await validateFile(); +}); diff --git a/validator/static/validator/js/py-worker.js b/validator/static/validator/js/py-worker.js new file mode 100644 index 00000000..4fddbfde --- /dev/null +++ b/validator/static/validator/js/py-worker.js @@ -0,0 +1,31 @@ +// This script is setting up a way to run Python scripts asynchronously in a web worker. It sends the Python script to the worker and sets up a callback to handle the result when the worker has finished executing the script. +const pyodideWorker = new Worker(new URL("webworker.js", import.meta.url, null)); + +const callbacks = {}; + +pyodideWorker.onmessage = (event) => { + const { id, ...data } = event.data; + const onSuccess = callbacks[id]; + delete callbacks[id]; + onSuccess(data); +}; +//This id is incremented each time the function is invoked and is kept within the safe integer limit. + + +const asyncRun = (() => { + let id = 0; // identify a Promise + return (script, context) => { + // the id could be generated more carefully + id = (id + 1) % Number.MAX_SAFE_INTEGER; + return new Promise((onSuccess) => { + callbacks[id] = onSuccess; + pyodideWorker.postMessage({ + ...context, + python: script, + id, + }); + }); + }; +})(); + +export { asyncRun }; \ No newline at end of file diff --git a/validator/static/validator/js/scores_consumer.js b/validator/static/validator/js/scores_consumer.js new file mode 100644 index 00000000..b864d755 --- /dev/null +++ b/validator/static/validator/js/scores_consumer.js @@ -0,0 +1,129 @@ +import "https://cdn.datatables.net/2.0.3/js/dataTables.js" +import { asyncRun } from "./py-worker.js"; + + +const validate_scores = await fetch(new URL('../python/bin/validation_scores.py', import.meta.url, null)).then(response => response.text()); + +let dirHandle; +let validateFileHandle; + +function startLoading(){ + const spinner = document.getElementById('pgs_loading'); + spinner.style.visibility = "visible"; +} + +function finishLoading(){ + const spinner = document.getElementById('pgs_loading'); + spinner.style.visibility = "hidden"; +} + +function successMount(dirName){ + document.getElementById('grant_message').innerHTML = 'Authorization granted on directory \"'+dirName+"\"."; +} + + +async function mountLocalDirectory() { + // use the same ID crypt4gh to open pickers in the same directory + dirHandle = await showDirectoryPicker(); + + if ((await dirHandle.queryPermission({ mode: "read" })) !== "granted") { + if ( + (await dirHandle.requestPermission({ mode: "read" })) !== "granted" + ) { + throw Error("Unable to read and write directory"); + } + } + return dirHandle.name; +} + +async function validation(validateFileHandle) { + let context; + if (!validateFileHandle) { + console.log('No single scoring file defined'); + context = { + dirHandle: dirHandle, + outputFileName: null, + }; + } else { + context = { + dirHandle: dirHandle, + outputFileName: validateFileHandle.name, + }; + } + + try { + const { results, error } = await asyncRun(validate_scores, context); + if (results) { + let data = JSON.parse(results); + if(data.status === 'success'){ + validation_out.value = data.response; + console.log("pyodideWorker return results: ", data.response); + } else if (data.status === 'error'){ + validation_out.value = ''; + console.error("pyodideWorker returned error: ", data.error); + appendAlertToElement("error",'Error: '+data.error,'danger') + } + return results; + } else if (error) { + validation_out.value = ''; + console.log(typeof error); + console.log("pyodideWorker error: ", error); + appendAlertToElement("error",'Error: '+error,'danger') + } + } catch (e) { + validation_out.value =`Error in pyodideWorker at ${e.filename}, Line: ${e.lineno}, ${e.message}`; + console.log( + `Error in pyodideWorker at ${e.filename}, Line: ${e.lineno}, ${e.message}`, + ); + } +} + +async function appendAlertToElement(elementId, message, type) { + const alertPlaceholder = document.getElementById(elementId); + if (!alertPlaceholder) { + console.error("Element with ID '" + elementId + "' not found."); + return; + } + + const wrapper = document.createElement('div'); + wrapper.innerHTML = [ + `' + ].join(''); + + alertPlaceholder.append(wrapper); +} + +document.querySelector('#validate_directory').addEventListener('click', async () => { + validation_out.value = "Initializing validation...\n"; + //$('#validate').html(' Validating...'); + startLoading(); + await validation(null); + finishLoading(); + //$('#validate').html(''); +}); + +document.querySelector('#mountvalidate').addEventListener('click', async () => { + if (!('showDirectoryPicker' in window)) { + alert('Your browser does not support the File System Access API. Please use a supported browser.'); + return; // Stop execution if the API is not supported + } + else { + let dirName = await mountLocalDirectory(); + //appendAlertToElement('validatediv','Nice, you have granted the permission to the local directory '+dirHandle.name,'success' ) + //document.querySelector('#mount').disabled = true; + successMount(dirName); + document.querySelector('#validate_single').disabled = false; + document.querySelector('#validate_directory').disabled = false; + } +}); + +document.querySelector('#validate_single').addEventListener('click', async () => { + [validateFileHandle] = await window.showOpenFilePicker(); + startLoading(); + await validation(validateFileHandle); + finishLoading(); +}); diff --git a/validator/static/validator/js/webworker.js b/validator/static/validator/js/webworker.js new file mode 100644 index 00000000..f1078ebe --- /dev/null +++ b/validator/static/validator/js/webworker.js @@ -0,0 +1,61 @@ +// webworker.js + +// Setup your project to serve `py-worker.js`. You should also serve +// `pyodide.js`, and all its associated `.asm.js`, `.json`, +// and `.wasm` files as well: +importScripts("https://cdn.jsdelivr.net/pyodide/v0.26.0/full/pyodide.js"); + +const wheels_base_url = "/static/validator/python/wheels/"; + +async function loadPyodideAndPackages() { + self.pyodide = await loadPyodide(); + await pyodide.loadPackage("micropip"); + const micropip = pyodide.pyimport("micropip"); + await micropip.install(['openpyxl','requests','httpx==0.26.0','tenacity','pyliftover', + 'xopen==1.8.0','zstandard','tqdm','natsort','pandas','pandas-schema']); + await micropip.install(wheels_base_url+"pgs_template_validator-1.1.0-py3-none-any.whl", keep_going=true); + await micropip.install(wheels_base_url+"pgscatalog_validate-0.1-py3-none-any.whl", keep_going=true) + await pyodide.FS.createLazyFile('/home/pyodide/', 'TemplateColumns2Models.xlsx', + '/static/validator/template/TemplateColumns2Models.xlsx', true, false); +} +let pyodideReadyPromise = loadPyodideAndPackages(); + + +//This event is fired when the worker receives a message from the main thread via the postMessage method. +self.onmessage = async (event) => { + // make sure loading is done + await pyodideReadyPromise; + // Don't bother yet with this line, suppose our API is built in such a way: + const { id, python, ...context } = event.data; + // The worker copies the context in its own "memory" (an object mapping name to values) + for (const key of Object.keys(context)) { + self[key] = context[key]; + } + // Now is the easy part, the one that is similar to working in the main thread: + try { + let startTime = performance.now(); + + await self.pyodide.loadPackagesFromImports(python); + // mount local directory, make the nativefs as a global vaiable. + if (! self.fsmounted && self.dirHandle){ + self.nativefs = await self.pyodide.mountNativeFS("/data", self.dirHandle); + self.fsmounted = true; + } + // run python script + self.pyodide.globals.set('print', s => console.log(s)) + let results = await self.pyodide.runPythonAsync(python); + // flush new files to disk + if(self.nativefs){ + await self.nativefs.syncfs(); + } + + + let endTime = performance.now(); + + console.log(`Encryption web worker took ${(endTime - startTime) / 1000} seconds`) + self.postMessage({ results, id }); + } catch (error) { + console.log(error); + self.postMessage({ error: error.message, id }); + } + }; diff --git a/validator/static/validator/python/bin/test_services.py b/validator/static/validator/python/bin/test_services.py new file mode 100644 index 00000000..5946a858 --- /dev/null +++ b/validator/static/validator/python/bin/test_services.py @@ -0,0 +1,16 @@ +# from validator.config import OLS_EFO_URL, EUROPMC_URL, GWAS_REST_URL +# from pyodide.http import pyfetch +# +# +# async def test_resource(resource_url): +# response = await pyfetch(resource_url) +# return response.ok +# +# +# resources = { +# 'OLS_EFO': OLS_EFO_URL +# } +# +# for resource in resources.keys(): +# is_ok = await test_resource(resources.get(resource)) +# print(is_ok) \ No newline at end of file diff --git a/validator/static/validator/python/bin/validation_metadata.py b/validator/static/validator/python/bin/validation_metadata.py new file mode 100644 index 00000000..66005faa --- /dev/null +++ b/validator/static/validator/python/bin/validation_metadata.py @@ -0,0 +1,91 @@ +import io +import js +import json +from pyodide.http import open_url +from validator.main_validator import PGSMetadataValidator +from validator.request.connector import Connector, UnknownError, NotFound, Logger + +# Need a proxy for OLS as Pyodide causes a cross-origin issue with the OLS url +OLS_URL = "https://ols-proxy-dot-pgs-catalog.appspot.com/ols-proxy/efo/%s" + +file = io.BytesIO(bytes(js.file_content)) +file_name = js.file_name + + +class PyodideLogger(Logger): + def debug(self, message, name): + print(f'ERROR: {message}') + + +class PyodideConnector(Connector): + def __init__(self): + super().__init__(logger=PyodideLogger()) + + def request(self, url, payload=None) -> dict: + if payload: + query = '&'.join([f"{k}={v}" for k, v in payload.items()]) + url = url + '?' + query + query_result_io = open_url(url) + query_result = query_result_io.read() + + result_json = json.loads(query_result) + return result_json + + def get_efo_trait(self, efo_id) -> dict: + url = OLS_URL % efo_id.replace('_', ':') + response = self.request(url) + # If not found the response should return 404. + if '_embedded' in response and 'terms' in response['_embedded'] and len(response['_embedded']['terms']) == 1: + return response['_embedded']['terms'][0] + elif 'status' in response and response['status'] == 404: + raise NotFound(message=response['error'], url=url) + else: + raise UnknownError(message="Unexpected response from URL: %s" % url, url=url) + + +def validate(): + metadata_validator = PGSMetadataValidator(file, False, PyodideConnector()) + metadata_validator.template_columns_schema_file = '/home/pyodide/TemplateColumns2Models.xlsx' + metadata_validator.parse_spreadsheets() + metadata_validator.parse_publication() + metadata_validator.parse_scores() + metadata_validator.parse_cohorts() + metadata_validator.parse_performances() + metadata_validator.parse_samples() + metadata_validator.post_parsing_checks() + + report_text = 'No error' + + response = {} + + status = 'success' + if metadata_validator.report['error']: + status = 'failed' + response['error'] = {} + error_report = metadata_validator.report['error'] + for error_spreadsheet in error_report: + response['error'][error_spreadsheet] = [] + for error_msg in error_report[error_spreadsheet]: + error_entry = {'message': error_msg} + if error_report[error_spreadsheet][error_msg][0] != None: + error_entry['lines'] = error_report[error_spreadsheet][error_msg] + response['error'][error_spreadsheet].append(error_entry) + + if metadata_validator.report['warning']: + response['warning'] = {} + warning_report = metadata_validator.report['warning'] + for warning_spreadsheet in warning_report: + response['warning'][warning_spreadsheet] = [] + for warning_msg in warning_report[warning_spreadsheet]: + warning_entry = {'message': warning_msg} + if warning_report[warning_spreadsheet][warning_msg][0] != None: + warning_entry['lines'] = warning_report[warning_spreadsheet][warning_msg] + response['warning'][warning_spreadsheet].append(warning_entry) + + response['status'] = status + return response + + +response = validate() + +json.dumps(response) # Is returned by pyodide.runPythonAsync() diff --git a/validator/static/validator/python/bin/validation_scores.py b/validator/static/validator/python/bin/validation_scores.py new file mode 100644 index 00000000..fa32c1b4 --- /dev/null +++ b/validator/static/validator/python/bin/validation_scores.py @@ -0,0 +1,79 @@ +import glob +import json +import tempfile +import os.path +from pathlib import Path + +from js import outputFileName +# from pgscatalog.validate.cli.validate_scorefile import validate_scorefile +from pgscatalog.validate.cli.validate_scorefile import _check_args, _validate_scorefile + + +# local file system is mounted in /data +#input_path = Path("/data") / outputFileName + + +class Args: + dir: str + log_dir: str + t: str + f: str + score_dir: str + check_filename: bool + + +response = '' +error = None + +try: + + # At the moment the results of score validation are stored in individual log files in log_dir + with tempfile.TemporaryDirectory() as log_dir: + + args = Args() + args.t = 'formatted' + args.check_filename = False + args.dir = None + args.f = None + args.log_dir = Path(log_dir) + args.score_dir = None + + if outputFileName: + filename = str(Path("/data") / outputFileName) + if not os.path.exists(filename): + raise FileNotFoundError(filename) + args.f = filename + else: + args.dir = str(Path("/data")) + + # Unconventional use of private functions but temporary + _check_args(args) + _validate_scorefile(args) + + # Getting the validation results from the log files + for log_file in glob.glob(str(log_dir)+'/*_log.txt'): + file_name = log_file.split('/')[-1].removesuffix('_log.txt') + with open(log_file, 'r') as f: + content = f.read() + response = response + file_name + ":\n" + response = response + content + "\n" + +except FileNotFoundError as e: + error = "Could not read input file. Is the selected file in the in the directory with granted rights?" +except Exception as e: + error = str(e) + + +data = {} +if error: + data = { + 'status': 'error', + 'error': error + } +else: + data = { + 'status': 'success', + 'response': response + } + +json.dumps(data) diff --git a/validator/static/validator/python/wheels/pgs_template_validator-1.1.0-py3-none-any.whl b/validator/static/validator/python/wheels/pgs_template_validator-1.1.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..bf0bbb84dc5f547cc308ebcf9b42683bb653193d GIT binary patch literal 24486 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 literal 0 HcmV?d00001 diff --git a/validator/static/validator/python/wheels/pgscatalog_validate-0.1-py3-none-any.whl b/validator/static/validator/python/wheels/pgscatalog_validate-0.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..41041c2510943e6b2af61d9f32b193cc275392fa GIT binary patch literal 23731 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≤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 NAEApQXQLnA{s&wiR#^Z5 literal 0 HcmV?d00001 diff --git a/validator/static/validator/template/TemplateColumns2Models.xlsx b/validator/static/validator/template/TemplateColumns2Models.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8957f4f03dd26afe07ad20f60837d65704222dc5 GIT binary patch literal 17012 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 +{% endblock %} + +{% block content %} + + + + +

Metadata Template Validation

+ +
+
+
+ + +
+ + +
+
More information about the Template file here.
+
+ +
+ + +
+
+ + +{% endblock %} diff --git a/validator/templates/validator/validate_scoring_files_client.html b/validator/templates/validator/validate_scoring_files_client.html new file mode 100644 index 00000000..2d9b62d7 --- /dev/null +++ b/validator/templates/validator/validate_scoring_files_client.html @@ -0,0 +1,53 @@ +{% extends 'catalog/base.html' %} +{% load static %} + +{% block title %}Metadata Template Validation{% endblock %} + +{% block desc %} + +{% endblock %} + +{% block content %} + + + + +

Scoring Files Validationbeta

+ +
+
+ 1) Please grant the permission to read your local directory that contains your scores to validate: +
+
+
+
+ 2) Then click on to select only one file to validate in this directory, + or click on to validate all scoring files located in this directory. +
+
+ + +
+ +
+ + +{% endblock %} diff --git a/validator/urls.py b/validator/urls.py new file mode 100644 index 00000000..a21f9917 --- /dev/null +++ b/validator/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path("validate_metadata_client/", views.validate_metadata_template_client, name="Metadata Template Validation"), + path("validate_scoring_files_client/", views.validate_scoring_files_client, name="Scoring Files Validation"), +] \ No newline at end of file diff --git a/validator/views.py b/validator/views.py new file mode 100644 index 00000000..695c6e80 --- /dev/null +++ b/validator/views.py @@ -0,0 +1,9 @@ +from django.shortcuts import render + + +def validate_metadata_template_client(request): + return render(request, 'validator/validate_metadata_client.html', {}) + + +def validate_scoring_files_client(request): + return render(request, 'validator/validate_scoring_files_client.html', {}) From 1c0a38af1d40f6a2aedd6a737e50093eb7a8ab78 Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Tue, 22 Oct 2024 16:22:40 +0100 Subject: [PATCH 04/25] Added validator urls --- pgs_web/urls.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pgs_web/urls.py b/pgs_web/urls.py index 3709dd12..5946617c 100755 --- a/pgs_web/urls.py +++ b/pgs_web/urls.py @@ -18,9 +18,10 @@ from search import views as search_views urlpatterns = [ - path('', include('catalog.urls')), + path('', include('catalog.urls')), path('', include('rest_api.urls')), path('', include('benchmark.urls')), + path('', include('validator.urls')), re_path(r'^search/', search_views.search, name="PGS Catalog Search"), re_path(r'^autocomplete/', search_views.autocomplete, name="PGS Catalog Autocomplete") ] From 8cc2d309b0586b736459b384aba5683d9830b74b Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Tue, 22 Oct 2024 16:22:56 +0100 Subject: [PATCH 05/25] Added validator static folder --- .gcloudignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gcloudignore b/.gcloudignore index 8dfecb33..900cce52 100644 --- a/.gcloudignore +++ b/.gcloudignore @@ -18,6 +18,7 @@ /catalog/static/ /rest_api/static/ /curation_tracker/static/ +/validator/static/ # Curation and release directories (not needed for the website) /curation/ From 9b6416b12b7429e13be474f90d7c5c85530c1bad Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Tue, 22 Oct 2024 17:38:19 +0100 Subject: [PATCH 06/25] Moved score validator to labs --- catalog/static/catalog/pgs.scss | 6 ++++++ catalog/templates/catalog/labs.html | 18 ++++++++++++------ validator/urls.py | 2 +- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/catalog/static/catalog/pgs.scss b/catalog/static/catalog/pgs.scss index b29de8e0..c6f99aef 100644 --- a/catalog/static/catalog/pgs.scss +++ b/catalog/static/catalog/pgs.scss @@ -188,6 +188,12 @@ h2 { font-weight:900; } +.score_validator_logo:before { + font-family: 'Font Awesome 6 Free'; + content:"\e5a0"; + font-weight:900; +} + /* Navbar and horizontal menu */ .navbar { diff --git a/catalog/templates/catalog/labs.html b/catalog/templates/catalog/labs.html index 4cd3e765..0bac0132 100644 --- a/catalog/templates/catalog/labs.html +++ b/catalog/templates/catalog/labs.html @@ -20,16 +20,22 @@

Labs
- -
+ {% endblock %} diff --git a/validator/urls.py b/validator/urls.py index a21f9917..1a572a95 100644 --- a/validator/urls.py +++ b/validator/urls.py @@ -3,5 +3,5 @@ urlpatterns = [ path("validate_metadata_client/", views.validate_metadata_template_client, name="Metadata Template Validation"), - path("validate_scoring_files_client/", views.validate_scoring_files_client, name="Scoring Files Validation"), + path("labs/validate_scoring_files_client/", views.validate_scoring_files_client, name="Scoring Files Validation"), ] \ No newline at end of file From 8a08cf782d1317fa024c773f7566a7c68a7442c8 Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Tue, 22 Oct 2024 18:02:31 +0100 Subject: [PATCH 07/25] Code cleaning and commenting --- validator/static/validator/js/metadata_consumer.js | 10 +++++----- validator/static/validator/js/scores_consumer.js | 1 - .../static/validator/python/bin/validation_metadata.py | 4 ++++ .../static/validator/python/bin/validation_scores.py | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/validator/static/validator/js/metadata_consumer.js b/validator/static/validator/js/metadata_consumer.js index 3e2fa1d8..9825afe4 100644 --- a/validator/static/validator/js/metadata_consumer.js +++ b/validator/static/validator/js/metadata_consumer.js @@ -48,13 +48,13 @@ function report_items_2_html(reports_list) { } let message = report_item.message; // Value highlighting - message = message.replace(/\"(.+?)\"/g, "\"$1\""); + message = message.replace(/"(.+?)"/g, "\"$1\""); // Leading space - message = message.replace(/\"\s+/g, "\"_"); + message = message.replace(/"\s+/g, "\"_"); // Trailing space - message = message.replace(/\s+<\/b>\"/g, "_\""); + message = message.replace(/\s+<\/b>"/g, "_\""); // Column highlighting - message = message.replace(/\'(.+?)\'/g, "\'$1\'"); + message = message.replace(/'(.+?)'/g, "\'$1\'"); report += "
  • "+lines+message+"
  • "; }); report += ''; @@ -102,7 +102,7 @@ function showResults(results){ function showSystemError(errors){ let status_html = '
    File validation: Failed
    '; $('#check_status').html(status_html); - let error_msg = (errors && errors != '') ? errors : 'Internal error'; + let error_msg = (errors && errors !== '') ? errors : 'Internal error'; let error_html = '
    '+ '
    '+ '
    Error: '+error_msg+'
    '+ diff --git a/validator/static/validator/js/scores_consumer.js b/validator/static/validator/js/scores_consumer.js index b864d755..b5c9db7b 100644 --- a/validator/static/validator/js/scores_consumer.js +++ b/validator/static/validator/js/scores_consumer.js @@ -66,7 +66,6 @@ async function validation(validateFileHandle) { return results; } else if (error) { validation_out.value = ''; - console.log(typeof error); console.log("pyodideWorker error: ", error); appendAlertToElement("error",'Error: '+error,'danger') } diff --git a/validator/static/validator/python/bin/validation_metadata.py b/validator/static/validator/python/bin/validation_metadata.py index 66005faa..6683b6f9 100644 --- a/validator/static/validator/python/bin/validation_metadata.py +++ b/validator/static/validator/python/bin/validation_metadata.py @@ -18,6 +18,9 @@ def debug(self, message, name): class PyodideConnector(Connector): + """This customised connector is necessary as the 'requests' python module is not supported in WebAssembly. + Moreover, the requests for EFO traits must be redirected to a proxy to avoid cross-origin errors.""" + def __init__(self): super().__init__(logger=PyodideLogger()) @@ -25,6 +28,7 @@ def request(self, url, payload=None) -> dict: if payload: query = '&'.join([f"{k}={v}" for k, v in payload.items()]) url = url + '?' + query + # Using pyodide open_url instead of python requests.get() query_result_io = open_url(url) query_result = query_result_io.read() diff --git a/validator/static/validator/python/bin/validation_scores.py b/validator/static/validator/python/bin/validation_scores.py index fa32c1b4..f33c986f 100644 --- a/validator/static/validator/python/bin/validation_scores.py +++ b/validator/static/validator/python/bin/validation_scores.py @@ -76,4 +76,4 @@ class Args: 'response': response } -json.dumps(data) +json.dumps(data) # Is returned by pyodide.runPythonAsync() From d28671530da452663ca62c24a845fcba81235bee Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Wed, 23 Oct 2024 11:16:55 +0100 Subject: [PATCH 08/25] Removed unused script --- .../static/validator/python/bin/test_services.py | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 validator/static/validator/python/bin/test_services.py diff --git a/validator/static/validator/python/bin/test_services.py b/validator/static/validator/python/bin/test_services.py deleted file mode 100644 index 5946a858..00000000 --- a/validator/static/validator/python/bin/test_services.py +++ /dev/null @@ -1,16 +0,0 @@ -# from validator.config import OLS_EFO_URL, EUROPMC_URL, GWAS_REST_URL -# from pyodide.http import pyfetch -# -# -# async def test_resource(resource_url): -# response = await pyfetch(resource_url) -# return response.ok -# -# -# resources = { -# 'OLS_EFO': OLS_EFO_URL -# } -# -# for resource in resources.keys(): -# is_ok = await test_resource(resources.get(resource)) -# print(is_ok) \ No newline at end of file From 7cf2d8a64da2c8465d3cedd05191fc0fbed68731 Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Wed, 23 Oct 2024 17:15:06 +0100 Subject: [PATCH 09/25] Using minified js versions if gae --- .gitignore | 4 +++- validator/static/validator/js/metadata_consumer.js | 3 ++- validator/static/validator/js/py-worker.js | 2 +- validator/static/validator/js/scores_consumer.js | 3 ++- .../templates/validator/validate_metadata_client.html | 7 +++++++ .../templates/validator/validate_scoring_files_client.html | 7 +++++++ 6 files changed, 22 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index daba40ae..35b3e358 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ app.yaml pgs-catalog-cred.json .idea -.vscode \ No newline at end of file +.vscode +*.min.js +*.min.css \ No newline at end of file diff --git a/validator/static/validator/js/metadata_consumer.js b/validator/static/validator/js/metadata_consumer.js index 9825afe4..0e5058db 100644 --- a/validator/static/validator/js/metadata_consumer.js +++ b/validator/static/validator/js/metadata_consumer.js @@ -1,6 +1,7 @@ import "https://cdn.datatables.net/2.0.3/js/dataTables.js" -import { asyncRun } from "./py-worker.js"; +const pyworker = await import((on_gae)?'./py-worker.min.js':'py-worker'); +const asyncRun = pyworker.asynRun; const validate_metadata = await fetch(new URL('../python/bin/validation_metadata.py', import.meta.url, null)).then(response => response.text()); let dirHandle; diff --git a/validator/static/validator/js/py-worker.js b/validator/static/validator/js/py-worker.js index 4fddbfde..d518a04d 100644 --- a/validator/static/validator/js/py-worker.js +++ b/validator/static/validator/js/py-worker.js @@ -1,5 +1,5 @@ // This script is setting up a way to run Python scripts asynchronously in a web worker. It sends the Python script to the worker and sets up a callback to handle the result when the worker has finished executing the script. -const pyodideWorker = new Worker(new URL("webworker.js", import.meta.url, null)); +const pyodideWorker = new Worker(new URL((on_gae) ? "webworker.min.js" : "webworker.js", import.meta.url, null)); const callbacks = {}; diff --git a/validator/static/validator/js/scores_consumer.js b/validator/static/validator/js/scores_consumer.js index b5c9db7b..e93e4a7f 100644 --- a/validator/static/validator/js/scores_consumer.js +++ b/validator/static/validator/js/scores_consumer.js @@ -1,6 +1,7 @@ import "https://cdn.datatables.net/2.0.3/js/dataTables.js" -import { asyncRun } from "./py-worker.js"; +const pyworker = await import((on_gae)?'./py-worker.min.js':'py-worker'); +const asyncRun = pyworker.asynRun; const validate_scores = await fetch(new URL('../python/bin/validation_scores.py', import.meta.url, null)).then(response => response.text()); diff --git a/validator/templates/validator/validate_metadata_client.html b/validator/templates/validator/validate_metadata_client.html index 99d523ce..f290673f 100644 --- a/validator/templates/validator/validate_metadata_client.html +++ b/validator/templates/validator/validate_metadata_client.html @@ -4,7 +4,14 @@ {% block title %}Metadata Template Validation{% endblock %} {% block desc %} + + {% if is_pgs_app_on_gae %} + + {% else %} + {% endif %} {% endblock %} {% block content %} diff --git a/validator/templates/validator/validate_scoring_files_client.html b/validator/templates/validator/validate_scoring_files_client.html index 2d9b62d7..6408342a 100644 --- a/validator/templates/validator/validate_scoring_files_client.html +++ b/validator/templates/validator/validate_scoring_files_client.html @@ -4,7 +4,14 @@ {% block title %}Metadata Template Validation{% endblock %} {% block desc %} + + {% if is_pgs_app_on_gae %} + + {% else %} + {% endif %} {% endblock %} {% block content %} From 9a531f57a9ec7f6077a65e7f673929e2d10ebc00 Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Wed, 23 Oct 2024 17:38:08 +0100 Subject: [PATCH 10/25] Fixed wrong script name in import --- validator/static/validator/js/metadata_consumer.js | 2 +- validator/static/validator/js/scores_consumer.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/validator/static/validator/js/metadata_consumer.js b/validator/static/validator/js/metadata_consumer.js index 0e5058db..4eb83089 100644 --- a/validator/static/validator/js/metadata_consumer.js +++ b/validator/static/validator/js/metadata_consumer.js @@ -1,6 +1,6 @@ import "https://cdn.datatables.net/2.0.3/js/dataTables.js" -const pyworker = await import((on_gae)?'./py-worker.min.js':'py-worker'); +const pyworker = await import((on_gae)?'./py-worker.min.js':'./py-worker.js'); const asyncRun = pyworker.asynRun; const validate_metadata = await fetch(new URL('../python/bin/validation_metadata.py', import.meta.url, null)).then(response => response.text()); diff --git a/validator/static/validator/js/scores_consumer.js b/validator/static/validator/js/scores_consumer.js index e93e4a7f..205ff118 100644 --- a/validator/static/validator/js/scores_consumer.js +++ b/validator/static/validator/js/scores_consumer.js @@ -1,6 +1,6 @@ import "https://cdn.datatables.net/2.0.3/js/dataTables.js" -const pyworker = await import((on_gae)?'./py-worker.min.js':'py-worker'); +const pyworker = await import((on_gae)?'./py-worker.min.js':'./py-worker.js'); const asyncRun = pyworker.asynRun; const validate_scores = await fetch(new URL('../python/bin/validation_scores.py', import.meta.url, null)).then(response => response.text()); From 5901bdb5cdbc9b31522c2fb47186f6d4471d4b52 Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Wed, 23 Oct 2024 17:43:42 +0100 Subject: [PATCH 11/25] Fixed typo --- validator/static/validator/js/metadata_consumer.js | 2 +- validator/static/validator/js/scores_consumer.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/validator/static/validator/js/metadata_consumer.js b/validator/static/validator/js/metadata_consumer.js index 4eb83089..17d9bc2c 100644 --- a/validator/static/validator/js/metadata_consumer.js +++ b/validator/static/validator/js/metadata_consumer.js @@ -1,7 +1,7 @@ import "https://cdn.datatables.net/2.0.3/js/dataTables.js" const pyworker = await import((on_gae)?'./py-worker.min.js':'./py-worker.js'); -const asyncRun = pyworker.asynRun; +const asyncRun = pyworker.asyncRun; const validate_metadata = await fetch(new URL('../python/bin/validation_metadata.py', import.meta.url, null)).then(response => response.text()); let dirHandle; diff --git a/validator/static/validator/js/scores_consumer.js b/validator/static/validator/js/scores_consumer.js index 205ff118..29a1a077 100644 --- a/validator/static/validator/js/scores_consumer.js +++ b/validator/static/validator/js/scores_consumer.js @@ -1,7 +1,7 @@ import "https://cdn.datatables.net/2.0.3/js/dataTables.js" const pyworker = await import((on_gae)?'./py-worker.min.js':'./py-worker.js'); -const asyncRun = pyworker.asynRun; +const asyncRun = pyworker.asyncRun; const validate_scores = await fetch(new URL('../python/bin/validation_scores.py', import.meta.url, null)).then(response => response.text()); From 7cae096ba37d75a131e8ae7c28be5452436eec80 Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Wed, 23 Oct 2024 17:46:16 +0100 Subject: [PATCH 12/25] Removed unused import --- validator/static/validator/js/metadata_consumer.js | 2 -- validator/static/validator/js/scores_consumer.js | 2 -- 2 files changed, 4 deletions(-) diff --git a/validator/static/validator/js/metadata_consumer.js b/validator/static/validator/js/metadata_consumer.js index 17d9bc2c..1d7f7d03 100644 --- a/validator/static/validator/js/metadata_consumer.js +++ b/validator/static/validator/js/metadata_consumer.js @@ -1,5 +1,3 @@ -import "https://cdn.datatables.net/2.0.3/js/dataTables.js" - const pyworker = await import((on_gae)?'./py-worker.min.js':'./py-worker.js'); const asyncRun = pyworker.asyncRun; diff --git a/validator/static/validator/js/scores_consumer.js b/validator/static/validator/js/scores_consumer.js index 29a1a077..3f716223 100644 --- a/validator/static/validator/js/scores_consumer.js +++ b/validator/static/validator/js/scores_consumer.js @@ -1,5 +1,3 @@ -import "https://cdn.datatables.net/2.0.3/js/dataTables.js" - const pyworker = await import((on_gae)?'./py-worker.min.js':'./py-worker.js'); const asyncRun = pyworker.asyncRun; From 810e7fbe8362b2d8c84288ccb82a05b7ed31e8f8 Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Thu, 24 Oct 2024 10:20:17 +0100 Subject: [PATCH 13/25] Fixed typo --- validator/static/validator/python/bin/validation_scores.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/static/validator/python/bin/validation_scores.py b/validator/static/validator/python/bin/validation_scores.py index f33c986f..755376f3 100644 --- a/validator/static/validator/python/bin/validation_scores.py +++ b/validator/static/validator/python/bin/validation_scores.py @@ -59,7 +59,7 @@ class Args: response = response + content + "\n" except FileNotFoundError as e: - error = "Could not read input file. Is the selected file in the in the directory with granted rights?" + error = "Could not read input file. Is the selected file in the directory with granted rights?" except Exception as e: error = str(e) From 3f8fa88eec1b16373acd70c0f95f42e7189e22bb Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Thu, 24 Oct 2024 10:53:10 +0100 Subject: [PATCH 14/25] Removed unused commented code --- validator/static/validator/js/scores_consumer.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/validator/static/validator/js/scores_consumer.js b/validator/static/validator/js/scores_consumer.js index 3f716223..d158e72b 100644 --- a/validator/static/validator/js/scores_consumer.js +++ b/validator/static/validator/js/scores_consumer.js @@ -97,11 +97,9 @@ async function appendAlertToElement(elementId, message, type) { document.querySelector('#validate_directory').addEventListener('click', async () => { validation_out.value = "Initializing validation...\n"; - //$('#validate').html(' Validating...'); startLoading(); await validation(null); finishLoading(); - //$('#validate').html(''); }); document.querySelector('#mountvalidate').addEventListener('click', async () => { @@ -111,8 +109,6 @@ document.querySelector('#mountvalidate').addEventListener('click', async () => { } else { let dirName = await mountLocalDirectory(); - //appendAlertToElement('validatediv','Nice, you have granted the permission to the local directory '+dirHandle.name,'success' ) - //document.querySelector('#mount').disabled = true; successMount(dirName); document.querySelector('#validate_single').disabled = false; document.querySelector('#validate_directory').disabled = false; From d166fd8e4f40e7b0c1cf97400eea4c01ac7dc441 Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Thu, 24 Oct 2024 14:59:04 +0100 Subject: [PATCH 15/25] Bumped metadata validator version to 1.1.1 --- validator/static/validator/js/webworker.js | 2 +- ...gs_template_validator-1.1.0-py3-none-any.whl | Bin 24486 -> 0 bytes ...gs_template_validator-1.1.1-py3-none-any.whl | Bin 0 -> 24620 bytes 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 validator/static/validator/python/wheels/pgs_template_validator-1.1.0-py3-none-any.whl create mode 100644 validator/static/validator/python/wheels/pgs_template_validator-1.1.1-py3-none-any.whl diff --git a/validator/static/validator/js/webworker.js b/validator/static/validator/js/webworker.js index f1078ebe..06562376 100644 --- a/validator/static/validator/js/webworker.js +++ b/validator/static/validator/js/webworker.js @@ -13,7 +13,7 @@ async function loadPyodideAndPackages() { const micropip = pyodide.pyimport("micropip"); await micropip.install(['openpyxl','requests','httpx==0.26.0','tenacity','pyliftover', 'xopen==1.8.0','zstandard','tqdm','natsort','pandas','pandas-schema']); - await micropip.install(wheels_base_url+"pgs_template_validator-1.1.0-py3-none-any.whl", keep_going=true); + await micropip.install(wheels_base_url+"pgs_template_validator-1.1.1-py3-none-any.whl", keep_going=true); await micropip.install(wheels_base_url+"pgscatalog_validate-0.1-py3-none-any.whl", keep_going=true) await pyodide.FS.createLazyFile('/home/pyodide/', 'TemplateColumns2Models.xlsx', '/static/validator/template/TemplateColumns2Models.xlsx', true, false); diff --git a/validator/static/validator/python/wheels/pgs_template_validator-1.1.0-py3-none-any.whl b/validator/static/validator/python/wheels/pgs_template_validator-1.1.0-py3-none-any.whl deleted file mode 100644 index bf0bbb84dc5f547cc308ebcf9b42683bb653193d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24486 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 diff --git a/validator/static/validator/python/wheels/pgs_template_validator-1.1.1-py3-none-any.whl b/validator/static/validator/python/wheels/pgs_template_validator-1.1.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..364a5b48ea4047a6d00fe2a51aaefaaeb18e988b GIT binary patch literal 24620 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

    kLNS6~Ec9T=?4!u_g zb$VFJB(k}wLDw33oRn*C5G*qd8y;_=+xpuQ3bRcK242sk+WcF;LvQNLvWQtw69Wel zQ?^iS<3*Ffs$Z2Di=g4q+<{Fr23+D0Bb)JJs`Zu&d7WwfcKRlD!t1|je>5dilX)e; zC%5a*QMw#${#8z0+86toVySgyd+6wD08F@WI#K(9mH#T>7i-Ayv+8^MNNrS1+&skC zug(b7S=!{fCPP2w4QLfKDB6s~5g$rp&NwJXN0*|O@@HWlp8BBy=d7;9r5WZ4Lmx{p z91$vix{PrrSx*!HN|6h_WznU33Ro1TsNR{rV6OAJBY-y^o12@rCr_== z*Da|Q(Z|#7yhnwEO`)7r6Z`d89X%`O5DjFt;xK=Gn@M^e3JWykIy7GI5gTJB?VJPb zi|qz!^9TnQQ@Zp*i5r#YmTMveNV-3F1|8sK3|ykD%Q3sq*%n#D>ZU~;tcA|Z?n_&?QR5lqFW32I{sq^u}r5TSy%Dd z&+V}1-rcYhUhLcN=?Dv~EzB?+nfhzRvqPB)n;N{K8XR&?4n(G|(VQ^{ZY0hOHVRW? z6E0U|Fsmr$V@J1RDY$08qK9EE<%We1#7Msci(-2ZGa7#ANp^D?C)8@6#R>7@>vJ1{ zwhfzvb4RXB%O+V*XpfyaagUGlumfU04_8ODE?&Xk$bS316VjsbL`WA3=#|A(zmO5# zbcU;K_e*N*t{W}~CY$5%Bewn=oHr%ICiG@E+dLMntM1fXywDW3OQh-(H3^A|bj;;! zX$VE6wEVr1>~#Ik0(KYN9UvipyG*^;8g6iBJla;u1=-r$4G2Ufe0M?lW;*UJY*NAR zo?hN(yv^DizcV-kQeJJn2W6@#n^xSy8aO*+MU;ei$j2VG=G~gu>O*cHcs5O=@)GRW z>CvFn+e1P)A*}LTu}sg-(T@kveO=j4G@>Mr&h~|4t;O)ZvS&~e|18;<b^)a`oFuZ?sIF&PJn0^$w6qxI*`x$3=PN6Mz#*F%1I%dy!Ak zb&TV%M*uW52^8|+b@ElUNXGhBKLo*Xp5T0+UE@0NNxwUiCgXNtxWZPJXk+t#q@=g!~1BuIP-NOERTYG%-0yt4@za0{UNxL_v__x(gizE$*f^77Uww<;g_Bxs53gm zK^|KBSz!pDl9&x67cJKL3tTRm-9kQ9(cmswylp(f_q*^r%L^<6_UfS{<%0!>6>BEV zeq_B9u>7Qe{Zf%%N~G8$p)rdRTCBDtKEFJTjHP-Q=9rFo4qh{kJ8{+kA>5$nrQtqj zv%|BLxye{dq*)uL&2nh`ER^Z4x@MQ`Vjzj4Z4+3!AytGH-@pe-ql?v$cDmt_Dx5w9qZ0x# zl7ckIS7RX@XLaS=Y^A?7vgCb?f0YOJ>qrXxJ(YZZ z^6EfON4_!Ytt*h0ayO4$HBGyK9-zjBClxd-IZK?J&j<#

    $Eo*T- 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 UFz@+G1jYn#KGZ5}x_|xqKhK6%{{R30 literal 0 HcmV?d00001 From 7cccb4e08675b6480c1b6ed24e6f66aae5f56cdd Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Mon, 28 Oct 2024 15:00:55 +0000 Subject: [PATCH 16/25] Removed "_client" suffix from validators urls --- catalog/templates/catalog/labs.html | 2 +- curation_tracker/urls.py | 2 +- validator/urls.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/catalog/templates/catalog/labs.html b/catalog/templates/catalog/labs.html index 0bac0132..1ce398e3 100644 --- a/catalog/templates/catalog/labs.html +++ b/catalog/templates/catalog/labs.html @@ -30,7 +30,7 @@

    Labs
    - +
    Score Validator
    diff --git a/curation_tracker/urls.py b/curation_tracker/urls.py index e6b265f7..3385ce6d 100644 --- a/curation_tracker/urls.py +++ b/curation_tracker/urls.py @@ -6,6 +6,6 @@ path('curation_tracker/l2_curation', views.browse_l2_waiting, name='L2 Curation'), path('curation_tracker/release_ready', views.browse_release_ready, name='Release Ready'), # e.g. /upload/ - path("validate_metadata/", views.validate_metadata_template, name="Metadata Template Validation"), + path("validate_metadata_legacy/", views.validate_metadata_template, name="Metadata Template Validation"), path('curation_tracker/stats/', views.stats, name='Curation Stats') ] diff --git a/validator/urls.py b/validator/urls.py index 1a572a95..cc6a2bac 100644 --- a/validator/urls.py +++ b/validator/urls.py @@ -2,6 +2,6 @@ from . import views urlpatterns = [ - path("validate_metadata_client/", views.validate_metadata_template_client, name="Metadata Template Validation"), - path("labs/validate_scoring_files_client/", views.validate_scoring_files_client, name="Scoring Files Validation"), + path("validate_metadata/", views.validate_metadata_template_client, name="metadata_template_validation"), + path("labs/validate_scoring_files/", views.validate_scoring_files_client, name="scoring_files_validation"), ] \ No newline at end of file From f06b88ce63f324f50d629d8f7c8b6ed44e2cc7ce Mon Sep 17 00:00:00 2001 From: Florent Yvon Date: Mon, 28 Oct 2024 15:02:53 +0000 Subject: [PATCH 17/25] Fixed title --- .../templates/validator/validate_scoring_files_client.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/templates/validator/validate_scoring_files_client.html b/validator/templates/validator/validate_scoring_files_client.html index 6408342a..c58c7f25 100644 --- a/validator/templates/validator/validate_scoring_files_client.html +++ b/validator/templates/validator/validate_scoring_files_client.html @@ -1,7 +1,7 @@ {% extends 'catalog/base.html' %} {% load static %} -{% block title %}Metadata Template Validation{% endblock %} +{% block title %}Scoring Files Validation{% endblock %} {% block desc %}