From 504fc4c51d9094ee1957838752cc934bbb3c7aa6 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 18 Sep 2019 10:08:54 +0100 Subject: [PATCH 001/218] add wsgi script --- rest_variantValidator/wsgi.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 rest_variantValidator/wsgi.py diff --git a/rest_variantValidator/wsgi.py b/rest_variantValidator/wsgi.py new file mode 100644 index 0000000..018e7a9 --- /dev/null +++ b/rest_variantValidator/wsgi.py @@ -0,0 +1 @@ +from . import app as application From b7e4d5a4bf1faa32032ea8c3c32c8ab297b45cf2 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 18 Sep 2019 11:15:24 +0100 Subject: [PATCH 002/218] update wsgi --- rest_variantValidator/__init__.py | 0 rest_variantValidator/wsgi.py | 2 +- setup.py | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 rest_variantValidator/__init__.py diff --git a/rest_variantValidator/__init__.py b/rest_variantValidator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rest_variantValidator/wsgi.py b/rest_variantValidator/wsgi.py index 018e7a9..f8036d1 100644 --- a/rest_variantValidator/wsgi.py +++ b/rest_variantValidator/wsgi.py @@ -1 +1 @@ -from . import app as application +from app import application as application diff --git a/setup.py b/setup.py index 7c54365..6baf2a2 100644 --- a/setup.py +++ b/setup.py @@ -55,6 +55,7 @@ "flask-mail", "flask-restful", "requests", + "mod_wsgi", "pyliftover", "vvhgvs", ], From 0d83ef1521efe801a364e891971cd14871fb3697 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 18 Sep 2019 11:22:34 +0100 Subject: [PATCH 003/218] add run code to wsgi for testing --- rest_variantValidator/wsgi.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/rest_variantValidator/wsgi.py b/rest_variantValidator/wsgi.py index f8036d1..eb4aefc 100644 --- a/rest_variantValidator/wsgi.py +++ b/rest_variantValidator/wsgi.py @@ -1 +1,14 @@ from app import application as application + +if __name__ == '__main__': + from configparser import ConfigParser + from VariantValidator.settings import CONFIG_DIR + config = ConfigParser() + config.read(CONFIG_DIR) + if config["logging"]["log"] == "True": + application.debug = True + application.config['PROPAGATE_EXCEPTIONS'] = True + else: + application.debug = False + application.config['PROPAGATE_EXCEPTIONS'] = False + application.run(host="0.0.0.0", port=5000) From 8338909d02ecbcc50779adb2170fc6308e8d160f Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 18 Sep 2019 11:52:37 +0100 Subject: [PATCH 004/218] move wsgi and update --- rest_variantValidator/wsgi.py => wsgi.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename rest_variantValidator/wsgi.py => wsgi.py (87%) diff --git a/rest_variantValidator/wsgi.py b/wsgi.py similarity index 87% rename from rest_variantValidator/wsgi.py rename to wsgi.py index eb4aefc..8c0823a 100644 --- a/rest_variantValidator/wsgi.py +++ b/wsgi.py @@ -1,5 +1,4 @@ -from app import application as application - +from rest_variantValidator.app import application as application if __name__ == '__main__': from configparser import ConfigParser from VariantValidator.settings import CONFIG_DIR From cf2f18d6788563f1f8d8921746f4e0c82f92955d Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 19 Sep 2019 09:23:19 +0100 Subject: [PATCH 005/218] creating uta container in uta_docker.df rather than from source. Saving progress --- Dockerfile | 2 +- uta_docker.df | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 uta_docker.df diff --git a/Dockerfile b/Dockerfile index 236c921..19713f2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,4 +19,4 @@ RUN pip install -r REQUIREMENTS.txt RUN pip install -e . COPY configuration/docker.ini /root/.variantvalidator -CMD gunicorn -b 0.0.0.0:8000 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ \ No newline at end of file +CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ \ No newline at end of file diff --git a/uta_docker.df b/uta_docker.df new file mode 100644 index 0000000..76b22bd --- /dev/null +++ b/uta_docker.df @@ -0,0 +1,18 @@ +FROM postgres:9.6 + +RUN apt-get update && apt-get install -y \ + curl + +ENV POSTGRES_PASSWORD=password-login-is-disabled + +# by default uta docker is built with uta_20170707 +# please provide custom versions with the command line parameter +# --build-arg uta_version=uta_MYVERSION +# ARG uta_version=uta_201700707 +ARG uta_version=uta_20180821.pgd.gz + +MAINTAINER reecehart@gmail.com +ENV UTA_VERSION=${uta_version} +LABEL description="PostgreSQL image with $UTA_VERSION installed (https://github.com/biocommons/uta/)" + +ADD load-uta.sh /docker-entrypoint-initdb.d/ \ No newline at end of file From 999a82ebff54d08f283ca6f03d86d6560fce4c61 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Fri, 20 Sep 2019 18:44:46 +0100 Subject: [PATCH 006/218] update progress. UTA now builds and runs. Can run vv pytest but rest vv container not working. Gunicorn erroe query --- Dockerfile | 3 ++- REQUIREMENTS.txt | 3 ++- configuration/docker.ini | 14 +++++++------- docker-compose.yml | 4 +++- setup.py | 10 +++------- uta_docker.df | 30 +++++++++++++++++++----------- vdb_docker.df | 2 +- 7 files changed, 37 insertions(+), 29 deletions(-) diff --git a/Dockerfile b/Dockerfile index 19713f2..a7d7c3e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,4 +19,5 @@ RUN pip install -r REQUIREMENTS.txt RUN pip install -e . COPY configuration/docker.ini /root/.variantvalidator -CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ \ No newline at end of file + +CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index bb7f888..fca05b9 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -1,4 +1,5 @@ -git+https://github.com/openvar/variantValidator@develop_v1#egg=VariantValidator +git+https://github.com/openvar/vv_hgvs.git@master#egg=vvhgvs +git+https://github.com/openvar/variantValidator@master#egg=VariantValidator git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter git+https://github.com/openvar/vv-flask-restful-swagger@master#egg=vv_flask_restful_swagger flask diff --git a/configuration/docker.ini b/configuration/docker.ini index 94b2731..ef7a708 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -1,5 +1,5 @@ [mysql] -host = vdb +host = 127.0.0.1 database = validator user = vvadmin password = var1ant @@ -9,11 +9,11 @@ version = 2018-08-21 location = /usr/local/share/seqrepo [postgres] -host = uta +host = 127.0.0.1 database = uta -version = uta_20171026 -user = anonymous -password = +version = uta_20180821 +user = uta_admin +password = uta_admin [logging] #Levels control verbosity and can be set to "CRITICAL" "ERROR" "WARNING" "INFO" or "DEBUG". @@ -22,8 +22,8 @@ console = ERROR file = ERROR [Entrez] -email = -api_key = +email = +api_key = # diff --git a/docker-compose.yml b/docker-compose.yml index 24b73eb..e8f7bc9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,7 +10,9 @@ services: expose: - "3306" uta: - image: biocommons/uta + build: + context: . + dockerfile: uta_docker.df ports: - "5432:5432" expose: diff --git a/setup.py b/setup.py index af3eeb5..3d52f1f 100644 --- a/setup.py +++ b/setup.py @@ -47,15 +47,11 @@ "flask-mail", "flask-cors", "flask-restful", - "VariantValidator @ git+https://github.com/openvar/variantValidator.git@develop_v1#egg=VariantValidator", + "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@master#egg=vvhgvs", + "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@master#egg=VariantFormatter", "vv_flask_restful_swagger @ git+https://github.com/openvar/vv-flask-restful-swagger.git@master#egg=vv_flask_restful_swagger", - ], - # dependency_links=[ - # "git+https://github.com/openvar/variantValidator.git#egg=VariantValidator", - # "git+https://github.com/openvar/variantFormatter.git#egg=VariantFormatter", - # "git+https://github.com/openvar/vv_flask-restful-swagger#egg=vv_flask_restful_swagger" - # ] + ] ) # diff --git a/uta_docker.df b/uta_docker.df index 76b22bd..dfe2250 100644 --- a/uta_docker.df +++ b/uta_docker.df @@ -1,18 +1,26 @@ -FROM postgres:9.6 +FROM postgres:latest -RUN apt-get update && apt-get install -y \ - curl +# RUN apt-get update && apt-get install -y curl + +ENV POSTGRES_DB=uta + +ENV POSTGRES_USER=uta_admin + +ENV POSTGRES_PASSWORD=uta_admin + +RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* + +RUN wget http://dl.biocommons.org/uta/uta_20180821.pgd.gz -O /docker-entrypoint-initdb.d/uta_20180821.pgd.gz -ENV POSTGRES_PASSWORD=password-login-is-disabled # by default uta docker is built with uta_20170707 # please provide custom versions with the command line parameter # --build-arg uta_version=uta_MYVERSION # ARG uta_version=uta_201700707 -ARG uta_version=uta_20180821.pgd.gz - -MAINTAINER reecehart@gmail.com -ENV UTA_VERSION=${uta_version} -LABEL description="PostgreSQL image with $UTA_VERSION installed (https://github.com/biocommons/uta/)" - -ADD load-uta.sh /docker-entrypoint-initdb.d/ \ No newline at end of file +# ARG uta_version=uta_20180821.pgd.gz +# MAINTAINER reecehart@gmail.com +# ENV UTA_VERSION=${uta_version} +# LABEL description="PostgreSQL image with $UTA_VERSION installed (https://github.com/biocommons/uta/)" +# ADD load-uta.sh /docker-entrypoint-initdb.d/ +# RUN pg_ctl -D /var/lib/postgresql/data -l logfile start +# RUN gzip -cdq uta_20180821.pgd.gz | psql -U uta_admin -v ON_ERROR_STOP=1 -d uta -Eae diff --git a/vdb_docker.df b/vdb_docker.df index 68687d7..06f6593 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -10,4 +10,4 @@ ENV MYSQL_PASSWORD var1ant RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget https://leicester.figshare.com/ndownloader/files/16237784 -O /docker-entrypoint-initdb.d/validator_2019-07-10.sql.gz \ No newline at end of file +RUN wget https://leicester.figshare.com/ndownloader/files/17714429 -O /docker-entrypoint-initdb.d/validator_2019-09-18.sql.gz \ No newline at end of file From 128f4d023d0760d877698ee8aebbcc07bb1f9674 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 1 Oct 2019 07:49:22 +0100 Subject: [PATCH 007/218] Docker install now running on UTA uta_20180821 --- Dockerfile | 16 ++++++---- REQUIREMENTS.txt | 4 +-- VERSION.txt | 2 +- configuration/docker.ini | 4 +-- docker-compose.yml | 3 ++ docs/DOCKER.md | 63 +++++++++++++++++----------------------- setup.py | 1 + uta_docker.df | 17 +---------- vdb_docker.df | 2 +- wsgi.py | 16 ++++++++++ 10 files changed, 63 insertions(+), 65 deletions(-) create mode 100644 wsgi.py diff --git a/Dockerfile b/Dockerfile index a7d7c3e..9750c89 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,27 @@ +# Use an official Python runtime as a parent image FROM python:3.6 -#RUN seqrepo -r ${SEQREPO_DATA_DIR} pull -i ${SEQREPO_DATA_RELEASE} -#RUN touch ${SEQREPO_DATA_DIR}/testing.txt - -#RUN apt update && apt install -y git - +# Set the working directory to /app WORKDIR /app +# Copy the current directory contents into the container's /app directory COPY . /app RUN apt-get update +# Updrade pip RUN pip install --upgrade pip +# Install any needed packages specified in requirements.txt RUN pip install -r REQUIREMENTS.txt +# Install the tool RUN pip install -e . +# Copy the config file into the container home diorectory COPY configuration/docker.ini /root/.variantvalidator -CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ +# Start the application with gunicorn +# CMD gunicorn --preload -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ +CMD gunicorn --preload -b 0.0.0.0:8000 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ \ No newline at end of file diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index fca05b9..5be1521 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -5,6 +5,7 @@ git+https://github.com/openvar/vv-flask-restful-swagger@master#egg=vv_flask_rest flask flask-log flask-mail +flask-cors flask-restful gunicorn biocommons.seqrepo>=0.5.1 @@ -17,6 +18,3 @@ requests pytest>=3.6 pytest-cov codecov - - - diff --git a/VERSION.txt b/VERSION.txt index 60453e6..6a2b0ac 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -v1.0.0 \ No newline at end of file +v1.0.1 \ No newline at end of file diff --git a/configuration/docker.ini b/configuration/docker.ini index ef7a708..1470dbc 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -1,5 +1,5 @@ [mysql] -host = 127.0.0.1 +host = vdb database = validator user = vvadmin password = var1ant @@ -9,7 +9,7 @@ version = 2018-08-21 location = /usr/local/share/seqrepo [postgres] -host = 127.0.0.1 +host = uta database = uta version = uta_20180821 user = uta_admin diff --git a/docker-compose.yml b/docker-compose.yml index e8f7bc9..28038b3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,11 +26,14 @@ services: depends_on: - vdb - uta + - seqrepo volumes: - seqdata:/usr/local/share/seqrepo ports: - "5000:5000" - "8000:8000" + # command: + # gunicorn --preload -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ volumes: seqdata: \ No newline at end of file diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 3b2d8d8..1a49c4a 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -16,6 +16,24 @@ You will need to provide your email address and we recommend generating and usin *Note: configuration can be updated (see below for details)* +## Install and build + +*Note: some of these steps take >>1hr to complete depending on the speed of your internet connection, particularly +compiling SeqRepo* + +```bash +# Build the containers +$ docker-compose up --build -d + +# Load UTA +$ docker-compose run uta + +# Load seqrepo +$ docker-compose run seqrepo + +# Shutdown +ctrl + c +``` ## Launch You can then launch the docker containers and run them using @@ -24,26 +42,27 @@ You can then launch the docker containers and run them using docker-compose up ``` -Note, the first time this is run it will download each of the databases including the pre-populated -validator database and could take up to 30 minutes depending on your connection. We do not recommend -running this in the background as you need to see the logs and therefore when the databases are -ready to be used. +Note: We do not recommend running this in the background as you need to see the logs and therefore when the databases +are ready to be used. ## Access rest_variantValidator In a web browser navigate to -[0.0.0.0:8000](http://0.0.0.0:8000/) +[http://0.0.0.0:8000/webservices/variantvalidator.html](http://0.0.0.0:8000/webservices/variantvalidator.html) ## Stop the app `ctrl+c` -## Stop the remove the containers +***To re-launch the app, go to Launch*** + +## Remove the containers +***Note: This step removes the container database. Effectively an uninstall*** ```bash $ docker-compose down ``` ## Run You can go into the container via bash to use -[VariantValidator](https://github.com/openvar/variantValidator/blob/develop_v1/docs/MANUAL.md) directly. +[VariantValidator](https://github.com/openvar/variantValidator/blob/master/docs/MANUAL.md) directly. ```bash $ docker-compose run restvv bash @@ -66,32 +85,4 @@ The container hosts a full install of VariantValidator. VariantValidator can be run on the commandline from within the container. -Instructions can be found in the VariantValidator [manual](https://github.com/openvar/variantValidator/blob/develop_v1/docs/MANUAL.md) - -## Check which docker containers are running - -```bash -$ docker ps -a -``` - -## List all docker containers -```bash -$ docker container ls -a -``` - -## Stop containers - -```bash -$ docker stop -``` - -## Delete containers - -```bash -$ docker rm -``` - -## Delete images -```bash -$ docker rmi -``` \ No newline at end of file +Instructions can be found in the VariantValidator [manual](https://github.com/openvar/variantValidator/blob/master/docs/MANUAL.md) diff --git a/setup.py b/setup.py index 3d52f1f..139a535 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,7 @@ "flask-mail", "flask-cors", "flask-restful", + "gunicorn", "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@master#egg=vvhgvs", "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@master#egg=VariantFormatter", diff --git a/uta_docker.df b/uta_docker.df index dfe2250..a7a4216 100644 --- a/uta_docker.df +++ b/uta_docker.df @@ -1,7 +1,5 @@ FROM postgres:latest -# RUN apt-get update && apt-get install -y curl - ENV POSTGRES_DB=uta ENV POSTGRES_USER=uta_admin @@ -10,17 +8,4 @@ ENV POSTGRES_PASSWORD=uta_admin RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget http://dl.biocommons.org/uta/uta_20180821.pgd.gz -O /docker-entrypoint-initdb.d/uta_20180821.pgd.gz - - -# by default uta docker is built with uta_20170707 -# please provide custom versions with the command line parameter -# --build-arg uta_version=uta_MYVERSION -# ARG uta_version=uta_201700707 -# ARG uta_version=uta_20180821.pgd.gz -# MAINTAINER reecehart@gmail.com -# ENV UTA_VERSION=${uta_version} -# LABEL description="PostgreSQL image with $UTA_VERSION installed (https://github.com/biocommons/uta/)" -# ADD load-uta.sh /docker-entrypoint-initdb.d/ -# RUN pg_ctl -D /var/lib/postgresql/data -l logfile start -# RUN gzip -cdq uta_20180821.pgd.gz | psql -U uta_admin -v ON_ERROR_STOP=1 -d uta -Eae +RUN wget https://leicester.figshare.com/ndownloader/files/17797259 -O /docker-entrypoint-initdb.d/uta_20180821.sql.gz \ No newline at end of file diff --git a/vdb_docker.df b/vdb_docker.df index 06f6593..d7f7802 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -10,4 +10,4 @@ ENV MYSQL_PASSWORD var1ant RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget https://leicester.figshare.com/ndownloader/files/17714429 -O /docker-entrypoint-initdb.d/validator_2019-09-18.sql.gz \ No newline at end of file +RUN wget https://leicester.figshare.com/ndownloader/files/17714429 -O /docker-entrypoint-initdb.d/validator_2019-09-18.sql.gz diff --git a/wsgi.py b/wsgi.py new file mode 100644 index 0000000..fdecaad --- /dev/null +++ b/wsgi.py @@ -0,0 +1,16 @@ +"""mod_wsgi gateway wsgi file +""" + +from rest_variantValidator.app import application as application +if __name__ == '__main__': + from configparser import ConfigParser + from VariantValidator.settings import CONFIG_DIR + config = ConfigParser() + config.read(CONFIG_DIR) + if config["logging"]["log"] == "True": + application.debug = True + application.config['PROPAGATE_EXCEPTIONS'] = True + else: + application.debug = False + application.config['PROPAGATE_EXCEPTIONS'] = False + application.run(host="0.0.0.0", port=5000) From 7ea265656a376cdde66e9efa54b6b46ca7e55685 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 1 Oct 2019 07:51:30 +0100 Subject: [PATCH 008/218] Docker config update --- configuration/docker.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configuration/docker.ini b/configuration/docker.ini index 1470dbc..d728b04 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -22,8 +22,8 @@ console = ERROR file = ERROR [Entrez] -email = -api_key = +email = +api_key = # From cb3b341916f2bc2e170b6a34e07d88eca3d95529 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 1 Oct 2019 08:06:21 +0100 Subject: [PATCH 009/218] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index d62512e..63bf987 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,10 @@ For installation instructions please see [INSTALLATION.md](./docs/INSTALLATION.m Please see [MANUAL.md](./docs/MANUAL.md) +# Running in docker + +Please see [DOCKER.md](https://github.com/openvar/rest_variantValidator/blob/master/docs/DOCKER.md) + ## License Please see [LICENSE.txt](LICENSE.txt) From 452d186da6b3d0c9044870d4e13dd4c70778df41 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 3 Oct 2019 16:36:30 +0100 Subject: [PATCH 010/218] remove mail servicee because it breaks docker --- rest_variantValidator/app.py | 68 ++++++------------------------------ 1 file changed, 10 insertions(+), 58 deletions(-) diff --git a/rest_variantValidator/app.py b/rest_variantValidator/app.py index 41c5ed5..5f636ef 100644 --- a/rest_variantValidator/app.py +++ b/rest_variantValidator/app.py @@ -1,24 +1,21 @@ # This application uses the flask restful API framework import os -import re -import sys -from datetime import date, datetime, timedelta -import warnings # IMPORT FLASK MODULES -from flask import Flask ,request, jsonify, abort, url_for, g, send_file, redirect, Blueprint #, session, g, redirect, , abort, render_template, flash, make_response, abort -from flask_restful import Resource, Api, reqparse, abort, fields, marshal_with +from flask import Flask ,request, redirect +from flask_restful import Resource, Api from vv_flask_restful_swagger import swagger from flask_log import Logging -from flask_mail import Mail, Message + +# Import variantFormatter +import VariantFormatter +import VariantFormatter.simpleVariantFormatter # Import variant validator code import VariantValidator vval = VariantValidator.Validator() -# Import variantFormatter -import VariantFormatter -import VariantFormatter.simpleVariantFormatter + # Extract API related metadata config_dict = vval.my_config() @@ -51,8 +48,6 @@ # Create Logging instance flask_log = Logging(application) -# Create Mail instance -mail = Mail(application) # Resources @@ -111,54 +106,11 @@ def get(self, genome_build, variant_description, select_transcripts): validate = vval.validate(variant_description, genome_build, select_transcripts) validation = validate.format_as_dict(with_meta=True) except Exception as e: - import traceback - import time - exc_type, exc_value, last_traceback = sys.exc_info() - te = traceback.format_exc() - tbk = str(exc_type) + str(exc_value) + '\n\n' + str(te) + '\n\nVariant = ' + str(variant_description) + ' and selected_assembly = ' + str(genome_build) + '/n' - error = str(tbk) - # Email admin - msg = Message(recipients=["variantvalidator@gmail.com"], - sender='apiValidator', - body=error + '\n\n' + time.ctime(), - subject='Major error recorded') - # Send the email - mail.send(msg) error = {'flag' : ' Major error', 'validation_error': 'A major validation error has occurred. Admin have been made aware of the issue'} return error, 200, {'Access-Control-Allow-Origin': '*'} - - # Look for warnings - for key, val in validation.items(): - if key == 'flag' or key == 'metadata': - if key == 'flag' and str(val) == 'None': - import time - variant = variant_description - error = 'Variant = ' + str(variant_description) + ' and selected_assembly = ' + str(genome_build) + '\n' - # Email admin - msg = Message(recipients=["variantvalidator@gmail.com"], - sender='apiValidator', - body=error + '\n\n' + time.ctime(), - subject='Validation server error recorded') - # Send the email - mail.send(msg) - else: - continue - try: - if val['validation_warnings'] == 'Validation error': - import time - variant = variant_description - error = 'Variant = ' + str(variant_description) + ' and selected_assembly = ' + str(genome_build) + '\n' - # Email admin - msg = Message(recipients=["variantvalidator@gmail.com"], - sender='apiValidator', - body=error + '\n\n' + time.ctime(), - subject='Validation server error recorded') - # Send the email - mail.send(msg) - except TypeError: - pass - return validation, 200, {'Access-Control-Allow-Origin': '*'} + else: + return validation, 200, {'Access-Control-Allow-Origin': '*'} @@ -309,4 +261,4 @@ def get(self, variant_description, genome_build, transcript_model=None, select_t # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -# \ No newline at end of file +# From 994ce9e04ec1448169c3cb33aa61bf9205df9e35 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 30 Oct 2019 07:38:59 +0000 Subject: [PATCH 011/218] Update DOCKER.md --- docs/DOCKER.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 1a49c4a..f24b343 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -3,16 +3,21 @@ To install rest_variantValidator via Docker, first ensure you have both docker and docker-compose installed. See their [documentation](https://docs.docker.com/compose/install/) for information. -Then, clone the repository and move into that directory. +Create a directory collate your cloned repositories. Move into the directory then, clone the repository. ```bash $ git clone https://github.com/openvar/rest_variantValidator -cd rest_variantValidator/ +``` + +Once the repository has been cloned, cd into the rest_variantValidator directory that the clone creates. +```bash +$ cd rest_variantValidator/ ``` ## Configure Edit the file configuration/docker.ini -You will need to provide your email address and we recommend generating and using an [Entrez API key](https://ncbiinsights.ncbi.nlm.nih.gov/2017/11/02/new-api-keys-for-the-e-utilities/) +You will need to provide your email address and we recommend generating and using an +[Entrez API key](https://ncbiinsights.ncbi.nlm.nih.gov/2017/11/02/new-api-keys-for-the-e-utilities/) *Note: configuration can be updated (see below for details)* From 30ae1d99c9cdd666184b8210ae6bbaea0349e65a Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 30 Oct 2019 07:39:51 +0000 Subject: [PATCH 012/218] Update DOCKER.md --- docs/DOCKER.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index f24b343..860e38e 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -33,11 +33,11 @@ $ docker-compose up --build -d # Load UTA $ docker-compose run uta -# Load seqrepo -$ docker-compose run seqrepo - # Shutdown ctrl + c + +# Load seqrepo +$ docker-compose run seqrepo ``` ## Launch From cf220d5ed574aa1b70ed9416c6e96423e5f8bad3 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 30 Oct 2019 09:25:58 +0000 Subject: [PATCH 013/218] Update DOCKER.md --- docs/DOCKER.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 860e38e..6f47af5 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -30,14 +30,14 @@ compiling SeqRepo* # Build the containers $ docker-compose up --build -d +# Load seqrepo +$ docker-compose run seqrepo + # Load UTA $ docker-compose run uta # Shutdown ctrl + c - -# Load seqrepo -$ docker-compose run seqrepo ``` ## Launch From 9af99a9539f012428df44a1f867821ce11f10e0a Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 31 Oct 2019 15:38:26 +0000 Subject: [PATCH 014/218] update instllation instructions for docker --- configuration/docker.ini | 6 ++-- docker-compose.yml | 5 ++-- docs/DOCKER.md | 59 ++++++++++++++++++++++++++++++++++------ 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/configuration/docker.ini b/configuration/docker.ini index d728b04..e11ca5f 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -18,12 +18,12 @@ password = uta_admin [logging] #Levels control verbosity and can be set to "CRITICAL" "ERROR" "WARNING" "INFO" or "DEBUG". log = True -console = ERROR +console = DEBUG file = ERROR [Entrez] -email = -api_key = +email = +api_key = # diff --git a/docker-compose.yml b/docker-compose.yml index 28038b3..c43a8fc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,19 +21,18 @@ services: image: biocommons/seqrepo:2018-08-21 volumes: - seqdata:/usr/local/share/seqrepo + stdin_open: true + tty: true restvv: build: . depends_on: - vdb - uta - - seqrepo volumes: - seqdata:/usr/local/share/seqrepo ports: - "5000:5000" - "8000:8000" - # command: - # gunicorn --preload -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ volumes: seqdata: \ No newline at end of file diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 1a49c4a..7880562 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -3,16 +3,30 @@ To install rest_variantValidator via Docker, first ensure you have both docker and docker-compose installed. See their [documentation](https://docs.docker.com/compose/install/) for information. -Then, clone the repository and move into that directory. +Create a directory collate your cloned repositories. Move into the directory then, clone the repository. ```bash $ git clone https://github.com/openvar/rest_variantValidator -cd rest_variantValidator/ +``` + +Once the repository has been cloned, cd into the rest_variantValidator directory that the clone creates. +```bash +$ cd rest_variantValidator/ ``` +If you have cloned the repository previously, update it + +```bash +$ git pull +``` + ## Configure + +***Essential step*** + Edit the file configuration/docker.ini -You will need to provide your email address and we recommend generating and using an [Entrez API key](https://ncbiinsights.ncbi.nlm.nih.gov/2017/11/02/new-api-keys-for-the-e-utilities/) +You will need to provide an email address and an +[Entrez API key](https://ncbiinsights.ncbi.nlm.nih.gov/2017/11/02/new-api-keys-for-the-e-utilities/) *Note: configuration can be updated (see below for details)* @@ -22,15 +36,18 @@ You will need to provide your email address and we recommend generating and usin compiling SeqRepo* ```bash -# Build the containers -$ docker-compose up --build -d +# Pull images +docker-compose pull -# Load UTA -$ docker-compose run uta +# Build +docker-compose build --no-cache -# Load seqrepo +# Build and load SeqRepo $ docker-compose run seqrepo +# Load UTA +$ docker-compose run uta + # Shutdown ctrl + c ``` @@ -39,7 +56,7 @@ ctrl + c You can then launch the docker containers and run them using ```bash -docker-compose up +$ docker-compose up --no-recreate ``` Note: We do not recommend running this in the background as you need to see the logs and therefore when the databases @@ -86,3 +103,27 @@ The container hosts a full install of VariantValidator. VariantValidator can be run on the commandline from within the container. Instructions can be found in the VariantValidator [manual](https://github.com/openvar/variantValidator/blob/master/docs/MANUAL.md) + +## Updating rest_variantValidator +Update requires that the restvv container is deleted from your system. This is not achieved by removing the container + +If you are only running rest_variantValidator in docker, we recommend deleting and re-building all containers + +```bash +# Delete all containers +$ docker system prune -a +$ docker volume rm $(docker volume ls -qf dangling=true) +``` + +***Once you have deleted the containers, got to Install and Build*** + +Alternatively, you may wish to try and force the containers to re-build without deleting + +```bash +$ docker-compose down +$ docker-compuse up --force-recreate +``` + +***If you choose this option, make sure you see the container restvv being re-created and all Python packages being +reinstalled in the printed logs, otherwise the container may not actually be rebuilt and the contained modules may not + update*** \ No newline at end of file From 39e508c695e21fc59f0bebb9f150a5e1e9f91651 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 31 Oct 2019 16:05:50 +0000 Subject: [PATCH 015/218] Update DOCKER.md --- docs/DOCKER.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 7880562..f664f38 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -56,7 +56,7 @@ ctrl + c You can then launch the docker containers and run them using ```bash -$ docker-compose up --no-recreate +$ docker-compose up ``` Note: We do not recommend running this in the background as you need to see the logs and therefore when the databases @@ -111,8 +111,7 @@ If you are only running rest_variantValidator in docker, we recommend deleting a ```bash # Delete all containers -$ docker system prune -a -$ docker volume rm $(docker volume ls -qf dangling=true) +$ docker system prune -a --volumes ``` ***Once you have deleted the containers, got to Install and Build*** @@ -126,4 +125,4 @@ $ docker-compuse up --force-recreate ***If you choose this option, make sure you see the container restvv being re-created and all Python packages being reinstalled in the printed logs, otherwise the container may not actually be rebuilt and the contained modules may not - update*** \ No newline at end of file + update*** From 2a4601cf95e44e152ddad85d74916ca62deb5a15 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Fri, 1 Nov 2019 08:48:54 +0000 Subject: [PATCH 016/218] Tweaked install and change default port to 8080 to avoid Jupyter notebook conflicts --- Dockerfile | 2 +- configuration/docker.ini | 4 ++-- docker-compose.yml | 1 + docs/DOCKER.md | 41 ++++++++++++++++++++++++---------------- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9750c89..4aba9aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,4 +24,4 @@ COPY configuration/docker.ini /root/.variantvalidator # Start the application with gunicorn # CMD gunicorn --preload -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ -CMD gunicorn --preload -b 0.0.0.0:8000 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ \ No newline at end of file +CMD gunicorn --preload -b 0.0.0.0:8080 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ \ No newline at end of file diff --git a/configuration/docker.ini b/configuration/docker.ini index e11ca5f..1dd9b1e 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -22,8 +22,8 @@ console = DEBUG file = ERROR [Entrez] -email = -api_key = +email = admin@variantvalidator.org +api_key = 5becaa525b83ff4366802022464b5d0f3908 # diff --git a/docker-compose.yml b/docker-compose.yml index c43a8fc..5a16a3a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -33,6 +33,7 @@ services: ports: - "5000:5000" - "8000:8000" + - "8080:8080" volumes: seqdata: \ No newline at end of file diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 7880562..6c13c73 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -37,16 +37,15 @@ compiling SeqRepo* ```bash # Pull images -docker-compose pull +$ docker-compose pull # Build -docker-compose build --no-cache +$ docker-compose build --no-cache -# Build and load SeqRepo -$ docker-compose run seqrepo - -# Load UTA -$ docker-compose run uta +# Build and load restvv and databases +# This step can take >>1hour and is complete when you see the message +# - "rest_variantvalidator_seqrepo_1 exited with code 0" +$ docker-compose up # Shutdown ctrl + c @@ -56,7 +55,7 @@ ctrl + c You can then launch the docker containers and run them using ```bash -$ docker-compose up --no-recreate +$ docker-compose up ``` Note: We do not recommend running this in the background as you need to see the logs and therefore when the databases @@ -64,7 +63,7 @@ are ready to be used. ## Access rest_variantValidator In a web browser navigate to -[http://0.0.0.0:8000/webservices/variantvalidator.html](http://0.0.0.0:8000/webservices/variantvalidator.html) +[http://0.0.0.0:8080/webservices/variantvalidator.html](http://0.0.0.0:8080/webservices/variantvalidator.html) ## Stop the app `ctrl+c` @@ -72,7 +71,7 @@ In a web browser navigate to ***To re-launch the app, go to Launch*** ## Remove the containers -***Note: This step removes the container database. Effectively an uninstall*** +***Note: This step removes the container database. Effectively an uninstall. See Updating rest_variantValidator*** ```bash $ docker-compose down ``` @@ -85,6 +84,19 @@ You can go into the container via bash to use $ docker-compose run restvv bash ``` +and you can start the REST services manually, bound to one of 1 ports +```bash +# port 8080 (default) +$ gunicorn --preload -b 0.0.0.0:8080 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ + +# port 8000 +$ gunicorn --preload -b 0.0.0.0:8000 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ + +# port 5000 +$ gunicorn --preload -b 0.0.0.0:5000 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ +``` + + Note, that each time one of these commands is run a new container is created. For more information on how to use docker-compose see their [documentation](https://docs.docker.com/compose/). @@ -92,10 +104,6 @@ It is possible to access both the UTA and Validator databases outside of docker default PostgreSQL and MySQL ports (5432 and 3306 respectively). In the current set-up it is not possible to access the seqrepo database outside of docker. -Finally, it should be noted that the current UTA docker container is not up-to-date and only contains the -2017-10-26 release. Therefore use caution when interpreting these results, and be advised the - VariantValidator tests will fail. - ## Accessing VariantValidator and reconfiguring this container The container hosts a full install of VariantValidator. @@ -111,8 +119,8 @@ If you are only running rest_variantValidator in docker, we recommend deleting a ```bash # Delete all containers -$ docker system prune -a -$ docker volume rm $(docker volume ls -qf dangling=true) +$ docker-compose down +$ docker system prune -a --volumes ``` ***Once you have deleted the containers, got to Install and Build*** @@ -120,6 +128,7 @@ $ docker volume rm $(docker volume ls -qf dangling=true) Alternatively, you may wish to try and force the containers to re-build without deleting ```bash +# Force re-build $ docker-compose down $ docker-compuse up --force-recreate ``` From 727cbca3f67f1d7086f9ed46751d98f140e85015 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Fri, 1 Nov 2019 09:14:34 +0000 Subject: [PATCH 017/218] Tweaked install and change default port back to 8000 and update docs --- Dockerfile | 4 ++-- docs/DOCKER.md | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4aba9aa..a0c44ff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,5 +23,5 @@ RUN pip install -e . COPY configuration/docker.ini /root/.variantvalidator # Start the application with gunicorn -# CMD gunicorn --preload -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ -CMD gunicorn --preload -b 0.0.0.0:8080 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ \ No newline at end of file +# CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ +CMD gunicorn -b 0.0.0.0:8000 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 6c13c73..59cecda 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -63,7 +63,7 @@ are ready to be used. ## Access rest_variantValidator In a web browser navigate to -[http://0.0.0.0:8080/webservices/variantvalidator.html](http://0.0.0.0:8080/webservices/variantvalidator.html) +[http://0.0.0.0:8000/webservices/variantvalidator.html](http://0.0.0.0:8000/webservices/variantvalidator.html) ## Stop the app `ctrl+c` @@ -86,14 +86,15 @@ $ docker-compose run restvv bash and you can start the REST services manually, bound to one of 1 ports ```bash -# port 8080 (default) -$ gunicorn --preload -b 0.0.0.0:8080 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ +# port 8000 (default) +$ gunicorn -b 0.0.0.0:8000 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ # port 8000 -$ gunicorn --preload -b 0.0.0.0:8000 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ +$ gunicorn -b 0.0.0.0:8080 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ + # port 5000 -$ gunicorn --preload -b 0.0.0.0:5000 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ +$ gunicorn -b 0.0.0.0:5000 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ ``` From a8374365fd91ed2f8449f64c17d8eacd1ce2b8a0 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 7 Nov 2019 09:41:19 +0000 Subject: [PATCH 018/218] Update docker.ini --- configuration/docker.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configuration/docker.ini b/configuration/docker.ini index 1dd9b1e..fed62ea 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -22,8 +22,8 @@ console = DEBUG file = ERROR [Entrez] -email = admin@variantvalidator.org -api_key = 5becaa525b83ff4366802022464b5d0f3908 +email = +api_key = # @@ -41,4 +41,4 @@ api_key = 5becaa525b83ff4366802022464b5d0f3908 # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -# \ No newline at end of file +# From 9e3b109fb83fd5d67a7ea05fffe17d1736669d2e Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Fri, 15 Nov 2019 10:09:08 +0000 Subject: [PATCH 019/218] start building version 2 --- .gitignore | 19 ++ Dockerfile | 2 +- README.md | 5 +- REQUIREMENTS.txt | 11 +- docs/DOCKER.md | 4 +- rest_api/app.py | 155 ++++++++++ rest_api/endpoints/__init__.py | 39 +++ .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 788 bytes .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 618 bytes .../__pycache__/exceptions.cpython-36.pyc | Bin 0 -> 352 bytes .../representations.cpython-36.pyc | Bin 0 -> 627 bytes .../__pycache__/request_parser.cpython-36.pyc | Bin 0 -> 404 bytes .../variantformatter_endpoints.cpython-36.pyc | Bin 0 -> 2298 bytes .../variantvalidator_endpoints.cpython-36.pyc | Bin 0 -> 3316 bytes rest_api/endpoints/exceptions.py | 19 ++ rest_api/endpoints/hello.py | 68 +++++ rest_api/endpoints/name.py | 70 +++++ rest_api/endpoints/representations.py | 43 +++ rest_api/endpoints/request_parser.py | 28 ++ .../endpoints/variantformatter_endpoints.py | 92 ++++++ .../endpoints/variantvalidator_endpoints.py | 140 ++++++++++ .../utils}/__init__.py | 0 rest_variantValidator/app.py | 264 ------------------ setup.py | 14 +- 24 files changed, 692 insertions(+), 281 deletions(-) create mode 100644 .gitignore create mode 100644 rest_api/app.py create mode 100644 rest_api/endpoints/__init__.py create mode 100644 rest_api/endpoints/__pycache__/__init__.cpython-36.pyc create mode 100644 rest_api/endpoints/__pycache__/__init__.cpython-37.pyc create mode 100644 rest_api/endpoints/__pycache__/exceptions.cpython-36.pyc create mode 100644 rest_api/endpoints/__pycache__/representations.cpython-36.pyc create mode 100644 rest_api/endpoints/__pycache__/request_parser.cpython-36.pyc create mode 100644 rest_api/endpoints/__pycache__/variantformatter_endpoints.cpython-36.pyc create mode 100644 rest_api/endpoints/__pycache__/variantvalidator_endpoints.cpython-36.pyc create mode 100644 rest_api/endpoints/exceptions.py create mode 100644 rest_api/endpoints/hello.py create mode 100644 rest_api/endpoints/name.py create mode 100644 rest_api/endpoints/representations.py create mode 100644 rest_api/endpoints/request_parser.py create mode 100644 rest_api/endpoints/variantformatter_endpoints.py create mode 100644 rest_api/endpoints/variantvalidator_endpoints.py rename {rest_variantValidator => rest_api/utils}/__init__.py (100%) delete mode 100644 rest_variantValidator/app.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e1400c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +#Git ignorance file for variant validator +#Testing script +VariantValidator/vvTest.py + +# docker config +configuration/docker.ini + +# Log +rest_api.log + +#pycs +rest_variantValidator/**/*.pyc +build +.idea +dist +rest_VariantValidator.egg-info +.DS_Store + + diff --git a/Dockerfile b/Dockerfile index a0c44ff..ffcb99b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,4 +24,4 @@ COPY configuration/docker.ini /root/.variantvalidator # Start the application with gunicorn # CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ -CMD gunicorn -b 0.0.0.0:8000 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ +CMD gunicorn -b 0.0.0.0:8080 app --threads=5 --worker-class=gthread --chdir ./rest_api/ diff --git a/README.md b/README.md index 63bf987..f5e8e19 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Optional software: For installation instructions please see [INSTALLATION.md](./docs/INSTALLATION.md) -# Operation and configuration +## Operation and configuration Please see [MANUAL.md](./docs/MANUAL.md) @@ -59,6 +59,9 @@ Please see [DOCKER.md](https://github.com/openvar/rest_variantValidator/blob/mas Please see [LICENSE.txt](LICENSE.txt) +## Terms and conditions of use +[Terms and conditions can be found here](https://github.com/openvar/variantValidator/blob/master/README.md) + ## Cite us Hum Mutat. 2017 Oct 1. doi: 10.1002/humu.23348 diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index 5be1521..a3b38eb 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -1,12 +1,8 @@ git+https://github.com/openvar/vv_hgvs.git@master#egg=vvhgvs git+https://github.com/openvar/variantValidator@master#egg=VariantValidator git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter -git+https://github.com/openvar/vv-flask-restful-swagger@master#egg=vv_flask_restful_swagger flask -flask-log -flask-mail -flask-cors -flask-restful +flask-restplus gunicorn biocommons.seqrepo>=0.5.1 httplib2>=0.9.0 @@ -15,6 +11,5 @@ pyliftover>=0.3 biotools>=0.3.0 mysql-connector-python requests -pytest>=3.6 -pytest-cov -codecov +dicttoxml + diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 59cecda..87787be 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -84,7 +84,7 @@ You can go into the container via bash to use $ docker-compose run restvv bash ``` -and you can start the REST services manually, bound to one of 1 ports +and you can start the REST services manually, bound to one of the following ports ```bash # port 8000 (default) $ gunicorn -b 0.0.0.0:8000 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ @@ -131,7 +131,7 @@ Alternatively, you may wish to try and force the containers to re-build without ```bash # Force re-build $ docker-compose down -$ docker-compuse up --force-recreate +$ docker-compose up --force-recreate ``` ***If you choose this option, make sure you see the container restvv being re-created and all Python packages being diff --git a/rest_api/app.py b/rest_api/app.py new file mode 100644 index 0000000..71e6f2d --- /dev/null +++ b/rest_api/app.py @@ -0,0 +1,155 @@ +""" +Simple rest interface for VariantValidator built using Flask Flask-RESTPlus and Swagger UI +""" + +# Import modules +from flask import Flask, request +from endpoints import api, representations, exceptions, request_parser +import logging +from logging import handlers +import time + + +""" +Logging +""" +logger = logging.getLogger('rest_api') +# We are setting 2 types of logging. To screen at the level DEBUG +logger.setLevel(logging.INFO) + +# We will also log to a file +# Log with a rotating file-handler. This sets the maximum size of the log to 0.5Mb and allows two additional logs +# The logs are then deleted and replaced in rotation +logHandler = handlers.RotatingFileHandler('rest_api.log', maxBytes=500000, backupCount=2) +# We want to minimise the amount of information we log to capturing bugs +logHandler.setLevel(logging.ERROR) +logger.addHandler(logHandler) + + +""" +Create a parser object locally +""" +parser = request_parser.parser + +# Define the application as a Flask app with the name defined by __name__ (i.e. the name of the current module) +# Most tutorials define application as "app", but I have had issues with this when it comes to deployment, +# so application is recommended +application = Flask(__name__) + +api.init_app(application) + +# By default, show all endpoints (collapsed) +application.config.SWAGGER_UI_DOC_EXPANSION = 'list' + + +""" +Representations + - Adds a response-type into the "Response content type" drop-down menu displayed in Swagger + - When selected, the APP will return the correct response-header and content type + - The default for flask-RESTPlus is application/json + +Note + - The decorators are assigned to the functions +""" +# Add additional representations using the @api.representation decorator +# Requires the module make_response from flask and dict-to-xml + + +@api.representation('application/xml') +def application_xml(data, code, headers): + resp = representations.xml(data, code, headers) + return resp + + +@api.representation('application/json') +def application_json(data, code, headers): + resp = representations.application_json(data, code, headers) + return resp + + +""" +Error handlers + - exceptions has now been imported from utils! +""" + + +# Simple function that creates an error message that we will log +def log_exception(exception_type): + # We want to know the arguments passed and the path so we can replicate the error + params = dict(request.args) + params['path'] = request.path + # Create the message and log + message = '%s occurred at %s with params=%s' % (exception_type, time.ctime(), params) + logger.exception(message, exc_info=True) + + +@application.errorhandler(exceptions.RemoteConnectionError) +def remote_connection_error_handler(e): + # Add the Exception to the log ensuring that exc_info is True so that a traceback is also logged + log_exception('RemoteConnectionError') + + # Collect Arguments + args = parser.parse_args() + if args['content-type'] != 'application/xml': + return application_json({'message': str(e)}, + 504, + None) + else: + return application_xml({'message': str(e)}, + 504, + None) + + +@application.errorhandler(404) +def not_found_error_handler(): + # Collect Arguments + args = parser.parse_args() + if args['content-type'] != 'application/xml': + return application_json({'message': 'Requested Endpoint not found'}, + 404, + None) + else: + return application_xml({'message': 'Requested Endpoint not found'}, + 404, + None) + + +@application.errorhandler(500) +def default_error_handler(): + # Add the Exception to the log ensuring that exc_info is True so that a traceback is also logged + log_exception('RemoteConnectionError') + + # Collect Arguments + args = parser.parse_args() + if args['content-type'] != 'application/xml': + return application_json({'message': 'unhandled error: contact https://variantvalidator.org/contact_admin/'}, + 500, + None) + else: + return application_xml({'message': 'unhandled error: contact https://variantvalidator.org/contact_admin/'}, + 500, + None) + + +# Allows app to be run in debug mode +if __name__ == '__main__': + application.debug = True # Enable debugging mode + application.run(host="127.0.0.1", port=5000) # Specify a host and port fot the app + + +# +# Copyright (C) 2019 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/rest_api/endpoints/__init__.py b/rest_api/endpoints/__init__.py new file mode 100644 index 0000000..45dca14 --- /dev/null +++ b/rest_api/endpoints/__init__.py @@ -0,0 +1,39 @@ +from flask_restplus import Api + +# from .hello import api as ns_hello +# from .name import api as ns_name +from .variantvalidator_endpoints import api as ns_vv +from .variantformatter_endpoints import api as ns_vf + +# Define the API as api +api = Api(version="1.0.1", + title="rest_variantValidator", + description="### REST API for [VariantValidator](https://github.com/openvar/variantValidator) and" + " [VariantFormatter](https://github.com/openvar/variantFormatter)" + "   \n" + "- [Source code](https://github.com/openvar/rest_variantValidator)\n" + "- [Terms of use and about VariantValidator](https://github.com/openvar/variantValidator/blob" + "/master/README.md)") + +# api.add_namespace(ns_hello) +# api.add_namespace(ns_name) +api.add_namespace(ns_vv) +api.add_namespace(ns_vf) + + +# +# Copyright (C) 2019 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/rest_api/endpoints/__pycache__/__init__.cpython-36.pyc b/rest_api/endpoints/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0ce64f1b347a609205d89b355eb7b627d73d4aca GIT binary patch literal 788 zcmbtSO^ee&7|t|Fo2~8MyzFV9Am(7k#giY1g{`2Vl`}IpYy)w{qU}x(D4`Un%549 zM?mFqkOJV59`UOl_tRj@L&y8Tqkse?Bz-dY?595UR|6V+a{4vvcZ@^}FW$RyeqIb( zf2!CO*mA7c$M7(Dm>i~eH8nPCux41;Q(Q5EtaYjY6{Jib+xx~lvqJ-4^?U^~#}_mVOyh>}cLqk~Qm3X?U> zPUkkZneZRoMpC9@Oh_g$r$*s|e(`kV-ml^39KACx98#J>!zp6@A|QKC|y&6pX;g4?qt@dqeOGw5|Qj literal 0 HcmV?d00001 diff --git a/rest_api/endpoints/__pycache__/__init__.cpython-37.pyc b/rest_api/endpoints/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6c0e9e7fe6623f081db4bc94788f19cc88ff5fc2 GIT binary patch literal 618 zcma)4%}N6?5Kfw1TYu`!i>HNxxCa{rFa97ZD0q=#)x(N}bd%cXZW5AoLEphQ@SsN@ z!`IlWC-Dsg!K^<;M8ts%lgT&VWb$RZ*_;60Uhf}HZ)yNMR+}#ieX!}5UG|Ov1~ZVu z1VX@S3}v+(B>@B+vVetbjMcAEf;hSjd2m-@8eI^NKlw@!!xC+2@dQ6(s>KyrYA+@; zhFeDpY6O+;m}Y`er_9xJtJR7QcDjerR%b8jD-)d@|G=Ei51i9>72`AE2E$%ERXJ9g zOHYP>62=jgY{apvOirES=06-GaJ=3#8*{Q}_1bs85S?_@(4;&{72|(0{yJ+Mqq1Ib zV}+7s=!DC7Imx(9jnK~L_T#Z+x}Q;dL8^M44XyDqfn@e`$l~kO2$xJNA)Pfr1qh`j zMUgZqV?@%NTTN4b4@|=!zPpzWKC-?u*5~~d+__8#)Z`tb&UxxA{`IyWSuY8(f)yJ9&rceNDuu*>lQbxuG literal 0 HcmV?d00001 diff --git a/rest_api/endpoints/__pycache__/exceptions.cpython-36.pyc b/rest_api/endpoints/__pycache__/exceptions.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e01ef99de20ff098e3feb790c94e6856c258cb7 GIT binary patch literal 352 zcmYjMO-sZu5S_F|>*9h3^*{8oJ$MpC7G&|ZEGv2_NC?e{S=yvdy5gSo&-g<;`44jS zPlzX{tsgt^9xpR5nU|CCctmgSx5qKS2W(Zsddl#JOqZZRix&pMb6f}=T|pL?JoziZ zYRd2jObG0OBGRIW2X8o9V(nVIaMohk8fRxjj?5?Dn`cQ#rL?L>DZ@z0+Ua%G>3u05 z)~foMB&E~%3bex;diq-C*B;5|&FbznUu?>IMfHN*iZ}S2(6{nYkx{n2QI*lEbu@HU zV{){*amKO{pGs`HNPg06sQX9;6ew0e$;ONk<^?bLm?IV{ zdWEr2J++4aNcArviNEm%B&->g@?KAz_SRHhV_;q7SqSE-EHXhkxFR&dXJ~#GU4R;1 z*b)M(p=K|9DHN;4oGX3|wLp4t{=6xzE%Ho;+*B#DJrB=J6?7H$uNGD(!V#k*1xZct(`%1;shv-c(u+klldimQX0Ef~Q%nq#N9l4| zg&SGq$Yk6qDs7(XN?DUvZKB0o?4<3@AJ|1Bu)!^?;HeC-m<{lA1H}IiVFyoW2p#dP zD}Jjhe(p{6M?G(aw+fTU;j@BUBS(^5pBy!^a_fkiF5!d-w=wGJ4y=0JKy;q;n%=aK j{(eQ@PMAbEI_*%l)?$nLkR1L z2Qc6vkN98{@c2EVgbyD`mi*!j$*4~ZY&A0$x^bbT1L-c@PD7VYr_%=zP`K2UkQ%UB zNek&(nT9$1?9Q3iqAHlHl+0_ZWOsJ#yfz{WeY9WZqY{Mn!=hAG0roD7{pngT`@F)n zPK(x>0BQW>9=0ty^F5F8anEJ^m~p;hX45uE=R@nv2Qeo&(97Qw0~a&v!-1 zOfxsCMs%C+16D80R7|=B6BTDpnS;x8m4oD3RkCmFqgRK_ncn&2x>39pa6>W5j!?#f JgvK<$`wtgxeg^;m literal 0 HcmV?d00001 diff --git a/rest_api/endpoints/__pycache__/variantformatter_endpoints.cpython-36.pyc b/rest_api/endpoints/__pycache__/variantformatter_endpoints.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dde217b2fc8c90348aae1772f89acaac615301dd GIT binary patch literal 2298 zcmZuyOK%%D5Z<>|YuQnrXbQAtix!E5SdTcV93wE|3bk7xQ4QNQPyxkay`=1w_Z5-~ zVnIF>3ffctLk~T)Kcql`{sUfn%3tWI!`;;bjaT4s_zgKToEZ%_R#zSR=ifi_>x%M^ zvh+E?KZC)31A-N-hDxON)ky1Ws)9AFhk9i6jf^*dH~S{=3N}NlZ)J0-ZxLf?_idtL z3zx9{R_m8ZdFbHsRE2p3J9uTPWn2ZX7Tg-XR&Z5(z2I)(n^SF1!ME`3HwwN@w5dK- z7r%Z5n}Gtjbv3G-=?N)_X`%~!dG z`Tpip#%U0rZk>`iiHLhL3PQX+M~m(5l9GjxQ!mbSyHSEkxR5Xs5}&(^4QwIv&xn7X z#NoxZxOs(o?<)vMyDqrzEdb_ceZBeR(A_dgZ_Fi;e^dh)l*RO z07ava2Wd!is62wMnnoxl14$8f(znC2=F36DY#pp!tJ&6Ly z9DcB*>Er_OGlJ3}CFtJd9`et?gb(SRyK+FJbrNH+8+osTXcVD

_++4WxwT+8KjP z*F|*^{5(73GlRE zbl)FQ3I!TophAsIthn)h{>Q(7#e>|0Jeelo=Lv*$p`Xos>LoGB(s>Al8In2tBFp`g zjmD!)It%g@@<~tbZ9nX6wmOd*?R?p4!O;y-S+4YP4NhMXl)Z!Q*+!?hX|~uD<$2|f zf4chA5gn0vAiwK9{~^n3W)CX4NEKT8FF^st7{v)+3PT2^6iP9GY%Kn~^T6+VAtSzQ zDYMT2<{1q384#lMRjl+iq7MyXVijv|p($eHjWSiC0m_DCiQC6{A2{~H05YD?Zs;*~ z1pUH(l|#h0K$-k(7?SV8V7~*I=vY11SQTdNT%)(Ja;Z;@4Er2riQNaxn3!0HeVZ^F zfIY?Lr3t(R*mIamfbC=Z(&Wle9cmM8qE0~1#GIO!76reWY`eYZJeN&N0FxcB(lGWq1s78qj) zh@w?hLtPqLMYVNXuV`z)t?A>tSC&}=bo6Q^VY_Y&52EV|$CW*56iUA0x-UmwSV(km zPFUo13YVZCfJyqPB=WvX1j*$Zkee`=2E_g-Th~^_+5k%X+?DrF!x5uWN_8MYp{C?z zCh|Pq`+o)-S0^@i71a1PgQ4BO{V5dG1Xjz)OmZ?xAvrDJ6ukD7ztB@>NlKz0O%6Gg#*TMqW_M`L0{Ho=yOPR6kWkeFEi%! zES7q6`W)twf-)%kM$lEP_U6&tn1}y4q@ww;5cP6sA?fAO;#g>NsDQ4#=Fk-^jKwh@ z|LF_S?5oM_CA1uQuApn+d2QmU=OeG{iPsIZ8hPDBx4`Sx)N2vRd)(UXx3D{IO$g^Y zW*<}Ev~ZZ+#nc-R3%})=jSuq#zZ_s17`{m;CZJg)*e94`H!uU+b7?0m?3=_k-C*Bz zY-9!=8Qwjw(H=e9(Y|t#@7ZoZVU>A=sYPs`EiwF|d>pTEJXxXqfCj{NyUi|ky*@S$ z2DXE?r`p8bNG&omQtV(WFalz_aR|B{UcIOs{ng|~1 zwx%(=s={CoQgZM_+j(kK;M=N|HY4lPffLyNrS?(eTivKPD)r4$H6BJ&YvIx*yD@); z^M^Q3+WUKxa78T%*l3h$O{;XTvDMyw9_0t7R+LM%UaD4_-O^@bqkgY;zt-A*G;wHy zLw&PSDOJJVxL<3`CV#!#HvU5!RA-S?$mAKt1LPI=TaRaHRqMrjmFiZbQrRfBij#oV zdUK*}wwe=dd$)B|YZNb^5*wT6XR^3YP7a8TW-+ZJl5%6))YwH8wF`z%<5ky^bBOKT z(j>Rp30-2D%nfsC91g#LJRVsV_5+NXvz($p^(0OlH5nXrt6S4`7QgJQChOxk1uvZp`aSL|Y|XInS<^V^aDYSd~o(ge|rz`!dj2zXgqc z4I53Af%X0%L7RBJi$HRi@`;Kizm80CFp8NI>$Z5Ek zR=?^yYuQjbhP{vluEb5Q;kiXAQcy~C~YvlHvcB>iW^>p@+iWhj1u zk#Kfe@?x#&+hshdauz%7N!Ov<%3{8>yN&EyBzI0Wmq*4%OLUxXb9;?zA+f)96Y6z;iT@r0S}+^Ce@A z+8Pi7(9r0>_dNn+I_pE#qPB|<_wdUPB~1I=9wvl|FkrD=vybs)%9RcbBCfy#d@2M& zPXum=BN6ey4FMSNc_8)V5o2e}jo89i^B62j$|5A(kUQ^V1Ia_wi)_e{MbPU7cA)92 zh_Ceu*dqLstE{&KVof5;tp5=c>}JU|CT4dZT|2|WH_#}H$_eTj{9Srv3I51U@F%yJ zxXr{JChjuvZk%c0vI8x~nL^ljSWa+u)IFvf1Rr96goURlW9l>Sd{{WI8vY7sI)wGDN?6-!N`pKZ#t`;0&jXknjTw_#KTih!cBK>Fs9d}XH19Bt)oI!q zO(IgwBv`XzfIBDJz@378{R7dw^yJJ*{XemsW%tFd`@IYYlSd>2ruvpgS`yCGoXZ7i3lR(h|K!zc_QpB4(Fy%Mnvj(`;0)L zOuRvWLm6H*vme#qh#*mn6m=sKGyaCz2XX1&X5pfr4+TEQ=aej;l@$S=6JAx+!m^yd Mk>!^~bp@pSFJGcgZvX%Q literal 0 HcmV?d00001 diff --git a/rest_api/endpoints/exceptions.py b/rest_api/endpoints/exceptions.py new file mode 100644 index 0000000..5cdb740 --- /dev/null +++ b/rest_api/endpoints/exceptions.py @@ -0,0 +1,19 @@ +class RemoteConnectionError(Exception): + code = 504 + +# +# Copyright (C) 2019 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/rest_api/endpoints/hello.py b/rest_api/endpoints/hello.py new file mode 100644 index 0000000..db10604 --- /dev/null +++ b/rest_api/endpoints/hello.py @@ -0,0 +1,68 @@ +from flask_restplus import Namespace, Resource +from . import request_parser +from . import representations + + +""" +Create a parser object locally +""" +parser = request_parser.parser + + +""" +The assignment of api changes +""" + +api = Namespace('hello', description='Simple API that returns a greeting') + +""" +We also need to re-assign the route ans other decorated functions to api +""" + + +@api.route("/") +class HelloClass(Resource): + + # Add documentation about the parser + @api.expect(parser, validate=True) + def get(self): + + # Collect Arguments + args = parser.parse_args() + + # Overrides the default response route so that the standard HTML URL can return any specified format + if args['content-type'] == 'application/json': + # example: http://127.0.0.1:5000/name/name/bob?content-type=application/json + return representations.application_json({ + "greeting": "Hello World" + }, + 200, None) + # example: http://127.0.0.1:5000/name/name/bob?content-type=application/xml + elif args['content-type'] == 'application/xml': + return representations.xml({ + "greeting": "Hello World" + }, + 200, None) + else: + # Return the api default output + return { + "greeting": "Hello World" + } + + +# +# Copyright (C) 2019 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/rest_api/endpoints/name.py b/rest_api/endpoints/name.py new file mode 100644 index 0000000..e4d4de5 --- /dev/null +++ b/rest_api/endpoints/name.py @@ -0,0 +1,70 @@ +from flask_restplus import Namespace, Resource +from . import request_parser +from . import representations + + +""" +Create a parser object locally +""" +parser = request_parser.parser + + +""" +The assignment of api changes +""" + +api = Namespace('name', description='Return a name provided by the user') + + +""" +We also need to re-assign the route ans other decorated functions to api +""" + + +@api.route("/") +@api.param("name", "Enter name") +class NameClass(Resource): + + # Add documentation about the parser + @api.expect(parser, validate=True) + def get(self, name): + + # Collect Arguments + args = parser.parse_args() + + # Overrides the default response route so that the standard HTML URL can return any specified format + if args['content-type'] == 'application/json': + # example: http://127.0.0.1:5000/name/name/bob?content-type=application/json + return representations.application_json({ + "My name is": name + }, + 200, None) + # example: http://127.0.0.1:5000/name/name/bob?content-type=application/xml + elif args['content-type'] == 'application/xml': + return representations.xml({ + "My name is": name + }, + 200, None) + else: + # Return the api default output + return { + "My name is": name + } + + +# +# Copyright (C) 2019 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/rest_api/endpoints/representations.py b/rest_api/endpoints/representations.py new file mode 100644 index 0000000..aa64202 --- /dev/null +++ b/rest_api/endpoints/representations.py @@ -0,0 +1,43 @@ +# Import modules +from flask import make_response +from dicttoxml import dicttoxml + +""" +Representations + - Adds a response-type into the "Response content type" drop-down menu displayed in Swagger + - When selected, the APP will return the correct response-header and content type + - The default for flask-RESTPlus is application/json + +Note + - These will only be used by namespaces so are undecorated. Decorated versions will appear in app.py +""" + + +def xml(data, code, headers): + data = dicttoxml(data) + resp = make_response(data, code) + resp.headers['Content-Type'] = 'application/xml' + return resp + + +def application_json(data, code, headers): + resp = make_response(data, code) + resp.headers['Content-Type'] = 'application/json' + return resp + +# +# Copyright (C) 2019 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/rest_api/endpoints/request_parser.py b/rest_api/endpoints/request_parser.py new file mode 100644 index 0000000..00f784c --- /dev/null +++ b/rest_api/endpoints/request_parser.py @@ -0,0 +1,28 @@ +from flask_restplus import reqparse + + +# Create a RequestParser object to identify specific content-type requests in HTTP URLs +# The request-parser allows us to specify arguments passed via a URL, in this case, ....?content-type=application/json +parser = reqparse.RequestParser() +parser.add_argument('content-type', + type=str, + help='***Select the response format***', + choices=['application/json', 'application/xml']) + + +# +# Copyright (C) 2019 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/rest_api/endpoints/variantformatter_endpoints.py b/rest_api/endpoints/variantformatter_endpoints.py new file mode 100644 index 0000000..fa5387d --- /dev/null +++ b/rest_api/endpoints/variantformatter_endpoints.py @@ -0,0 +1,92 @@ +# Import modules +from flask_restplus import Namespace, Resource +from . import request_parser +from . import representations +# import requests +# from requests.exceptions import ConnectionError +# from . import exceptions + +# Import variantFormatter +import VariantFormatter +import VariantFormatter.simpleVariantFormatter + +""" +Create a parser object locally +""" +parser = request_parser.parser + +api = Namespace('VariantFormatter', description='Variantformatter API Endpoints') +@api.route("/variantformatter////" + "/") +@api.param("variant_description", "***Genomic HGVS***\n" + "> - NC_000017.10:g.48275363C>A\n" + "\n***Pseudo-VCF***\n" + "> - 17-50198002-C-A\n" + "> - 17:50198002:C:A\n" + "\n> *Notes*\n" + "> - *pVCF, multiple comma separated ALTs are supported*\n " + "> - *Multiple variants can be submitted, separated by the pipe '|' character*\n" + "> - *Recommended maximum is 10 variants per submission*") +@api.param("transcript_model", "***Accepted:***\n" + "> - refseq (return data for RefSeq transcript models)\n" + "> - all (currently refseq only") +@api.param("select_transcripts", "***Return all possible transcripts***\n" + "> None\n" + "\n***Single***\n" + "> NM_000093.4\n" + "\n***Multiple***\n" + "> NM_000093.4|NM_001278074.1|NM_000093.3") +@api.param("genome_build", "***Accepted:***\n" + "> - GRCh37\n" + "> - GRCh38") +@api.param("checkonly", "***Accepted:***\n" + "> - True (return ONLY the genomic variant descriptions and not transcript and protein" + " descriptions)\n" + "> - False") +class VariantValidatorClass(Resource): + # Add documentation about the parser + @api.expect(parser, validate=True) + def get(self, genome_build, variant_description, transcript_model, select_transcripts, checkonly): + if transcript_model == 'None' or transcript_model == 'none': + transcript_model = None + if select_transcripts == 'None' or select_transcripts == 'none': + select_transcripts = None + if checkonly == 'False' or checkonly == 'false': + checkonly = False + if checkonly == 'True' or checkonly == 'true': + checkonly = True + + content = VariantFormatter.simpleVariantFormatter.format(variant_description, genome_build, transcript_model, + select_transcripts, checkonly) + + # Collect Arguments + args = parser.parse_args() + + # Overrides the default response route so that the standard HTML URL can return any specified format + if args['content-type'] == 'application/json': + # example: http://127.0.0.1:5000.....bob?content-type=application/json + return representations.application_json(content, 200, None) + # example: http://127.0.0.1:5000.....?content-type=application/xml + elif args['content-type'] == 'application/xml': + return representations.xml(content, 200, None) + else: + # Return the api default output + return content + + +# +# Copyright (C) 2019 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/rest_api/endpoints/variantvalidator_endpoints.py b/rest_api/endpoints/variantvalidator_endpoints.py new file mode 100644 index 0000000..b9cf092 --- /dev/null +++ b/rest_api/endpoints/variantvalidator_endpoints.py @@ -0,0 +1,140 @@ +# Import modules +from flask_restplus import Namespace, Resource +from . import request_parser +from . import representations +# import requests +# from requests.exceptions import ConnectionError +# from . import exceptions + +# Import VariantValidator code +import VariantValidator +vval = VariantValidator.Validator() + +""" +Create a parser object locally +""" +parser = request_parser.parser + +api = Namespace('VariantValidator', description='VariantValidator API Endpoints') + + +@api.route("/variantvalidator///") +@api.param("select_transcripts", "***Return all possible transcripts***\n" + "> all\n" + "\n***Single***\n" + "> NM_000093.4\n" + "\n***Multiple***\n" + "> NM_000093.4|NM_001278074.1|NM_000093.3") +@api.param("variant_description", "***HGVS***\n" + "> - NM_000088.3:c.589G>T\n" + "> - NC_000017.10:g.48275363C>A\n" + "> - NG_007400.1:g.8638G>T\n" + "> - LRG_1:g.8638G>T\n" + "> - LRG_1t1:c.589G>T\n" + "\n***Pseudo-VCF***\n" + "> - 17-50198002-C-A\n" + "> - 17:50198002:C:A\n" + "> - GRCh38-17-50198002-C-A\n" + "> - GRCh38:17:50198002:C:A\n" + "\n***Hybrid***\n" + "> - chr17:50198002C>A\n " + "> - chr17:50198002C>A(GRCh38)\n" + "> - chr17:g.50198002C>A\n" + "> - chr17:g.50198002C>A(GRCh38)") +@api.param("genome_build", "***Accepted:***\n" + "> - GRCh37\n" + "> - GRCh38\n" + "> - hg19\n" + "> - hg38") +class VariantValidatorClass(Resource): + # Add documentation about the parser + @api.expect(parser, validate=True) + def get(self, genome_build, variant_description, select_transcripts): + + # Validate using the VariantValidator Python Library + validate = vval.validate(variant_description, genome_build, select_transcripts) + content = validate.format_as_dict(with_meta=True) + + # Collect Arguments + args = parser.parse_args() + + # Overrides the default response route so that the standard HTML URL can return any specified format + if args['content-type'] == 'application/json': + # example: http://127.0.0.1:5000.....bob?content-type=application/json + return representations.application_json(content, 200, None) + # example: http://127.0.0.1:5000.....?content-type=application/xml + elif args['content-type'] == 'application/xml': + return representations.xml(content, 200, None) + else: + # Return the api default output + return content + + +@api.route("/tools/gene2transcripts/") +@api.param("gene_query", "***HGNC gene symbol or transcript ID***\n" + "\nCurrent supported transcript IDs" + "\n- RefSeq") +class Gene2transcriptsClass(Resource): + # Add documentation about the parser + @api.expect(parser, validate=True) + def get(self, gene_query): + content = vval.gene2transcripts(gene_query) + + # Collect Arguments + args = parser.parse_args() + + # Overrides the default response route so that the standard HTML URL can return any specified format + if args['content-type'] == 'application/json': + # example: http://127.0.0.1:5000.....bob?content-type=application/json + return representations.application_json(content, 200, None) + # example: http://127.0.0.1:5000.....?content-type=application/xml + elif args['content-type'] == 'application/xml': + return representations.xml(content, 200, None) + else: + # Return the api default output + return content + + +@api.route("/tools/hgvs2reference/") +@api.param("hgvs_description", "***hgvs_description***\n" + "\nSequence variation description in the HGVS format\n" + "\n *Intronic descriptions in the context of transcript reference sequences are currentl" + "y " + "unsupported*") +class Hgvs2referenceClass(Resource): + # Add documentation about the parser + @api.expect(parser, validate=True) + def get(self, hgvs_description): + content = vval.hgvs2ref(hgvs_description) + + # Collect Arguments + args = parser.parse_args() + + # Overrides the default response route so that the standard HTML URL can return any specified format + if args['content-type'] == 'application/json': + # example: http://127.0.0.1:5000.....bob?content-type=application/json + return representations.application_json(content, 200, None) + # example: http://127.0.0.1:5000.....?content-type=application/xml + elif args['content-type'] == 'application/xml': + return representations.xml(content, 200, None) + else: + # Return the api default output + return content + + +# +# Copyright (C) 2019 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/rest_variantValidator/__init__.py b/rest_api/utils/__init__.py similarity index 100% rename from rest_variantValidator/__init__.py rename to rest_api/utils/__init__.py diff --git a/rest_variantValidator/app.py b/rest_variantValidator/app.py deleted file mode 100644 index 5f636ef..0000000 --- a/rest_variantValidator/app.py +++ /dev/null @@ -1,264 +0,0 @@ -# This application uses the flask restful API framework -import os - -# IMPORT FLASK MODULES -from flask import Flask ,request, redirect -from flask_restful import Resource, Api -from vv_flask_restful_swagger import swagger -from flask_log import Logging - -# Import variantFormatter -import VariantFormatter -import VariantFormatter.simpleVariantFormatter - -# Import variant validator code -import VariantValidator -vval = VariantValidator.Validator() - - - -# Extract API related metadata -config_dict = vval.my_config() -api_version = config_dict['variantvalidator_version'] -vf_api_version = VariantFormatter.__version__ - -# CREATE APP -application = Flask(__name__) - -# configure -application.config.from_object(__name__) - -# Wrap the Api with swagger.docs. -BaseURL = os.environ.get('SERVER_NAME') -if BaseURL is not None: - api = swagger.docs(Api(application), apiVersion=str(api_version), - basePath=BaseURL, - resourcePath='/', - produces=["application/json"], - api_spec_url='/webservices/variantvalidator', - description='VariantValidator web API' - ) -else: - api = swagger.docs(Api(application), apiVersion=str(api_version), - resourcePath='/', - produces=["application/json"], - api_spec_url='/webservices/variantvalidator', - description='VariantValidator web API' - ) - -# Create Logging instance -flask_log = Logging(application) - - -# Resources -############ -""" -Essentially web pages that display json data -""" - -""" -Home -""" - -class home(Resource): - def get(self): - root_url = str(request.url_root) - full_url = root_url + 'webservices/variantvalidator.html' - return redirect(full_url) - - -""" -Simple interface for VariantValidator -""" -class variantValidator(Resource): - @swagger.operation( - notes='Submit a sequence variation to VariantValidator', - nickname='VariantValidator', - parameters=[ - { - "name": "genome_build", - "description": "Possible values: GRCh37, GRCh38, hg19, hg38", - "required": True, - "allowMultiple": False, - "dataType": 'string', - "paramType": "path" - }, - { - "name": "variant_description", - "description": "Supported variant types: HGVS e.g. NM_000088.3:c.589G>T, NC_000017.10:g.48275363C>A, NG_007400.1:g.8638G>T, LRG_1:g.8638G>T, LRG_1t1:c.589G>T; pseudo-VCF e.g. 17-50198002-C-A, 17:50198002:C:A, GRCh3817-50198002-C-A, GRCh38:17:50198002:C:A; hybrid e.g. chr17:50198002C>A, chr17:50198002C>A(GRCh38), chr17:g.50198002C>A, chr17:g.50198002C>A(GRCh38)", - "required": True, - "allowMultiple": False, - "dataType": 'string', - "paramType": "path" - }, - { - "name": "select_transcripts", - "description": "Possible values: all = return data for all relevant transcripts; single transcript id e.g. NM_000093.4; multiple transcript ids e.g. NM_000093.4|NM_001278074.1|NM_000093.3", - "required": True, - "allowMultiple": False, - "dataType": 'string', - "paramType": "path" - } - ]) - - def get(self, genome_build, variant_description, select_transcripts): - try: - validate = vval.validate(variant_description, genome_build, select_transcripts) - validation = validate.format_as_dict(with_meta=True) - except Exception as e: - error = {'flag' : ' Major error', - 'validation_error': 'A major validation error has occurred. Admin have been made aware of the issue'} - return error, 200, {'Access-Control-Allow-Origin': '*'} - else: - return validation, 200, {'Access-Control-Allow-Origin': '*'} - - - -""" -Return the transcripts for a gene -""" -class gene2transcripts(Resource): - @swagger.operation( - notes='Get a list of available transcripts for a gene by providing a valid HGNC gene symbol or transcript ID', - nickname='get genes2transcripts', - parameters=[ - { - "name": "gene_symbol", - "description": "HGNC gene symbol or transcript ID (Current supported transcript types: RefSeq)", - "required": True, - "allowMultiple": False, - "dataType": 'string', - "paramType": "path" - } - ]) - def get(self, gene_symbol): - g2t = vval.gene2transcripts(gene_symbol) - return g2t, 200, {'Access-Control-Allow-Origin': '*'} - - -""" -Simple function that returns the reference bases for a hgvs description -""" -class hgvs2reference(Resource): - @swagger.operation( - notes='Get the reference bases for a HGVS variant description', - nickname='get reference bases', - parameters=[ - { - "name": "hgvs_description", - "description": "Sequence variation description in the HGVS format. Intronic descriptions in the context of transcript reference sequences are currently unsupported", - "required": True, - "allowMultiple": False, - "dataType": 'string', - "paramType": "path" - } - ]) - def get(self, hgvs_description): - h2r = vval.hgvs2ref(hgvs_description) - # return jsonify(h2r) - return h2r, 200, {'Access-Control-Allow-Origin': '*'} - -""" -Simple interface for VariantFormatter -""" -class variantFormatter(Resource): - @swagger.operation( - notes='Submit a genomic sequence variant description to VariantFormatter', - nickname='VariantValidator', - parameters=[ - { - "name": "genome_build", - "description": "Possible values: GRCh37 or GRCh38", - "required": True, - "allowMultiple": False, - "dataType": 'string', - "paramType": "path" - }, - { - "name": "variant_description", - "description": "Supported variant types: genomic HGVS e.g. NC_000017.10:g.48275363C>A, pseudo-VCF e.g. 17-50198002-C-A, 17:50198002:C:A (Note, for pVCF, multiple comma separated ALTs are supported). Multiple variants can be submitted, separated by the pipe '|' character. Recommended maximum is 10 variants per submission", - "required": True, - "allowMultiple": False, - "dataType": 'string', - "paramType": "path" - }, - { - "name": "transcript_model", - "description": "Possible values: all = return data for all relevant transcripts; refseq = return data for RefSeq transcript models; refseq = return data for Ensembl transcript models:", - "required": True, - "allowMultiple": False, - "dataType": 'string', - "paramType": "path" - }, - { - "name": "select_transcripts", - "description": "Possible values: None = return data for all relevant transcripts; single transcript id e.g. NM_000093.4; multiple transcript ids e.g. NM_000093.4|NM_001278074.1|NM_000093.3", - "required": True, - "allowMultiple": False, - "dataType": 'string', - "paramType": "path" - }, - { - "name": "checkOnly", - "description": "Possible values: True or False. True will return ONLY the genomic variant descriptions and will not provide transcript and protein level descriptions", - "required": True, - "allowMultiple": False, - "dataType": 'string', - "paramType": "path" - } - ]) - def get(self, variant_description, genome_build, transcript_model=None, select_transcripts=None, checkOnly=False): - if transcript_model == 'None' or transcript_model == 'none': - transcript_model = None - if select_transcripts == 'None' or select_transcripts == 'none': - select_transcripts = None - if checkOnly == 'False' or checkOnly== 'false': - checkOnly = False - if checkOnly == 'True' or checkOnly== 'true': - checkOnly = True - v_form = VariantFormatter.simpleVariantFormatter.format(variant_description, genome_build, transcript_model, select_transcripts, checkOnly) - return v_form, 200, {'Access-Control-Allow-Origin': '*'} - - - -# ADD API resources to API handler - -# VariantValidator -api.add_resource(home, '/') -api.add_resource(variantValidator, '/variantvalidator///') -api.add_resource(gene2transcripts, '/tools/gene2transcripts/') -api.add_resource(hgvs2reference, '/tools/hgvs2reference/') - -# VariantFormatter -api.add_resource(variantFormatter, '/variantformatter/////') - - -if __name__ == '__main__': - from configparser import ConfigParser - from VariantValidator.settings import CONFIG_DIR - config = ConfigParser() - config.read(CONFIG_DIR) - if config["logging"]["log"] == "True": - application.debug = True - application.config['PROPAGATE_EXCEPTIONS'] = True - else: - application.debug = False - application.config['PROPAGATE_EXCEPTIONS'] = False - application.run(host="0.0.0.0", port=5000) - -# -# Copyright (C) 2019 VariantValidator Contributors -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program 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 Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# diff --git a/setup.py b/setup.py index 139a535..09cb1d1 100644 --- a/setup.py +++ b/setup.py @@ -43,15 +43,19 @@ # List run-time dependencies here. These will be installed by pip when the project is installed. install_requires=[ "flask", - "flask-log", - "flask-mail", - "flask-cors", - "flask-restful", + "flask-restplus", "gunicorn", + "httplib2>=0.9.0", + "configparser>=3.5.0", + "pyliftover>=0.3", + "biotools>=0.3.0", + "mysql-connector-python", + "requests", + " dicttoxml", "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@master#egg=vvhgvs", "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@master#egg=VariantFormatter", - "vv_flask_restful_swagger @ git+https://github.com/openvar/vv-flask-restful-swagger.git@master#egg=vv_flask_restful_swagger", + "biocommons.seqrepo>=0.5.1", ] ) From 8a448d207602485747eee836d9e28d46471c823b Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Fri, 15 Nov 2019 10:12:05 +0000 Subject: [PATCH 020/218] start building version 2a --- .../__pycache__/__init__.cpython-36.pyc | Bin 788 -> 0 bytes .../__pycache__/__init__.cpython-37.pyc | Bin 618 -> 0 bytes .../__pycache__/exceptions.cpython-36.pyc | Bin 352 -> 0 bytes .../__pycache__/representations.cpython-36.pyc | Bin 627 -> 0 bytes .../__pycache__/request_parser.cpython-36.pyc | Bin 404 -> 0 bytes .../variantformatter_endpoints.cpython-36.pyc | Bin 2298 -> 0 bytes .../variantvalidator_endpoints.cpython-36.pyc | Bin 3316 -> 0 bytes 7 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 rest_api/endpoints/__pycache__/__init__.cpython-36.pyc delete mode 100644 rest_api/endpoints/__pycache__/__init__.cpython-37.pyc delete mode 100644 rest_api/endpoints/__pycache__/exceptions.cpython-36.pyc delete mode 100644 rest_api/endpoints/__pycache__/representations.cpython-36.pyc delete mode 100644 rest_api/endpoints/__pycache__/request_parser.cpython-36.pyc delete mode 100644 rest_api/endpoints/__pycache__/variantformatter_endpoints.cpython-36.pyc delete mode 100644 rest_api/endpoints/__pycache__/variantvalidator_endpoints.cpython-36.pyc diff --git a/rest_api/endpoints/__pycache__/__init__.cpython-36.pyc b/rest_api/endpoints/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 0ce64f1b347a609205d89b355eb7b627d73d4aca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 788 zcmbtSO^ee&7|t|Fo2~8MyzFV9Am(7k#giY1g{`2Vl`}IpYy)w{qU}x(D4`Un%549 zM?mFqkOJV59`UOl_tRj@L&y8Tqkse?Bz-dY?595UR|6V+a{4vvcZ@^}FW$RyeqIb( zf2!CO*mA7c$M7(Dm>i~eH8nPCux41;Q(Q5EtaYjY6{Jib+xx~lvqJ-4^?U^~#}_mVOyh>}cLqk~Qm3X?U> zPUkkZneZRoMpC9@Oh_g$r$*s|e(`kV-ml^39KACx98#J>!zp6@A|QKC|y&6pX;g4?qt@dqeOGw5|Qj diff --git a/rest_api/endpoints/__pycache__/__init__.cpython-37.pyc b/rest_api/endpoints/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index 6c0e9e7fe6623f081db4bc94788f19cc88ff5fc2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 618 zcma)4%}N6?5Kfw1TYu`!i>HNxxCa{rFa97ZD0q=#)x(N}bd%cXZW5AoLEphQ@SsN@ z!`IlWC-Dsg!K^<;M8ts%lgT&VWb$RZ*_;60Uhf}HZ)yNMR+}#ieX!}5UG|Ov1~ZVu z1VX@S3}v+(B>@B+vVetbjMcAEf;hSjd2m-@8eI^NKlw@!!xC+2@dQ6(s>KyrYA+@; zhFeDpY6O+;m}Y`er_9xJtJR7QcDjerR%b8jD-)d@|G=Ei51i9>72`AE2E$%ERXJ9g zOHYP>62=jgY{apvOirES=06-GaJ=3#8*{Q}_1bs85S?_@(4;&{72|(0{yJ+Mqq1Ib zV}+7s=!DC7Imx(9jnK~L_T#Z+x}Q;dL8^M44XyDqfn@e`$l~kO2$xJNA)Pfr1qh`j zMUgZqV?@%NTTN4b4@|=!zPpzWKC-?u*5~~d+__8#)Z`tb&UxxA{`IyWSuY8(f)yJ9&rceNDuu*>lQbxuG diff --git a/rest_api/endpoints/__pycache__/exceptions.cpython-36.pyc b/rest_api/endpoints/__pycache__/exceptions.cpython-36.pyc deleted file mode 100644 index 5e01ef99de20ff098e3feb790c94e6856c258cb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 352 zcmYjMO-sZu5S_F|>*9h3^*{8oJ$MpC7G&|ZEGv2_NC?e{S=yvdy5gSo&-g<;`44jS zPlzX{tsgt^9xpR5nU|CCctmgSx5qKS2W(Zsddl#JOqZZRix&pMb6f}=T|pL?JoziZ zYRd2jObG0OBGRIW2X8o9V(nVIaMohk8fRxjj?5?Dn`cQ#rL?L>DZ@z0+Ua%G>3u05 z)~foMB&E~%3bex;diq-C*B;5|&FbznUu?>IMfHN*iZ}S2(6{nYkx{n2QI*lEbu@HU zV{){*amKO{pGs`HNPg06sQX9;6ew0e$;ONk<^?bLm?IV{ zdWEr2J++4aNcArviNEm%B&->g@?KAz_SRHhV_;q7SqSE-EHXhkxFR&dXJ~#GU4R;1 z*b)M(p=K|9DHN;4oGX3|wLp4t{=6xzE%Ho;+*B#DJrB=J6?7H$uNGD(!V#k*1xZct(`%1;shv-c(u+klldimQX0Ef~Q%nq#N9l4| zg&SGq$Yk6qDs7(XN?DUvZKB0o?4<3@AJ|1Bu)!^?;HeC-m<{lA1H}IiVFyoW2p#dP zD}Jjhe(p{6M?G(aw+fTU;j@BUBS(^5pBy!^a_fkiF5!d-w=wGJ4y=0JKy;q;n%=aK j{(eQ@PMAbEI_*%l)?$nLkR1L z2Qc6vkN98{@c2EVgbyD`mi*!j$*4~ZY&A0$x^bbT1L-c@PD7VYr_%=zP`K2UkQ%UB zNek&(nT9$1?9Q3iqAHlHl+0_ZWOsJ#yfz{WeY9WZqY{Mn!=hAG0roD7{pngT`@F)n zPK(x>0BQW>9=0ty^F5F8anEJ^m~p;hX45uE=R@nv2Qeo&(97Qw0~a&v!-1 zOfxsCMs%C+16D80R7|=B6BTDpnS;x8m4oD3RkCmFqgRK_ncn&2x>39pa6>W5j!?#f JgvK<$`wtgxeg^;m diff --git a/rest_api/endpoints/__pycache__/variantformatter_endpoints.cpython-36.pyc b/rest_api/endpoints/__pycache__/variantformatter_endpoints.cpython-36.pyc deleted file mode 100644 index dde217b2fc8c90348aae1772f89acaac615301dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2298 zcmZuyOK%%D5Z<>|YuQnrXbQAtix!E5SdTcV93wE|3bk7xQ4QNQPyxkay`=1w_Z5-~ zVnIF>3ffctLk~T)Kcql`{sUfn%3tWI!`;;bjaT4s_zgKToEZ%_R#zSR=ifi_>x%M^ zvh+E?KZC)31A-N-hDxON)ky1Ws)9AFhk9i6jf^*dH~S{=3N}NlZ)J0-ZxLf?_idtL z3zx9{R_m8ZdFbHsRE2p3J9uTPWn2ZX7Tg-XR&Z5(z2I)(n^SF1!ME`3HwwN@w5dK- z7r%Z5n}Gtjbv3G-=?N)_X`%~!dG z`Tpip#%U0rZk>`iiHLhL3PQX+M~m(5l9GjxQ!mbSyHSEkxR5Xs5}&(^4QwIv&xn7X z#NoxZxOs(o?<)vMyDqrzEdb_ceZBeR(A_dgZ_Fi;e^dh)l*RO z07ava2Wd!is62wMnnoxl14$8f(znC2=F36DY#pp!tJ&6Ly z9DcB*>Er_OGlJ3}CFtJd9`et?gb(SRyK+FJbrNH+8+osTXcVD

_++4WxwT+8KjP z*F|*^{5(73GlRE zbl)FQ3I!TophAsIthn)h{>Q(7#e>|0Jeelo=Lv*$p`Xos>LoGB(s>Al8In2tBFp`g zjmD!)It%g@@<~tbZ9nX6wmOd*?R?p4!O;y-S+4YP4NhMXl)Z!Q*+!?hX|~uD<$2|f zf4chA5gn0vAiwK9{~^n3W)CX4NEKT8FF^st7{v)+3PT2^6iP9GY%Kn~^T6+VAtSzQ zDYMT2<{1q384#lMRjl+iq7MyXVijv|p($eHjWSiC0m_DCiQC6{A2{~H05YD?Zs;*~ z1pUH(l|#h0K$-k(7?SV8V7~*I=vY11SQTdNT%)(Ja;Z;@4Er2riQNaxn3!0HeVZ^F zfIY?Lr3t(R*mIamfbC=Z(&Wle9cmM8qE0~1#GIO!76reWY`eYZJeN&N0FxcB(lGWq1s78qj) zh@w?hLtPqLMYVNXuV`z)t?A>tSC&}=bo6Q^VY_Y&52EV|$CW*56iUA0x-UmwSV(km zPFUo13YVZCfJyqPB=WvX1j*$Zkee`=2E_g-Th~^_+5k%X+?DrF!x5uWN_8MYp{C?z zCh|Pq`+o)-S0^@i71a1PgQ4BO{V5dG1Xjz)OmZ?xAvrDJ6ukD7ztB@>NlKz0O%6Gg#*TMqW_M`L0{Ho=yOPR6kWkeFEi%! zES7q6`W)twf-)%kM$lEP_U6&tn1}y4q@ww;5cP6sA?fAO;#g>NsDQ4#=Fk-^jKwh@ z|LF_S?5oM_CA1uQuApn+d2QmU=OeG{iPsIZ8hPDBx4`Sx)N2vRd)(UXx3D{IO$g^Y zW*<}Ev~ZZ+#nc-R3%})=jSuq#zZ_s17`{m;CZJg)*e94`H!uU+b7?0m?3=_k-C*Bz zY-9!=8Qwjw(H=e9(Y|t#@7ZoZVU>A=sYPs`EiwF|d>pTEJXxXqfCj{NyUi|ky*@S$ z2DXE?r`p8bNG&omQtV(WFalz_aR|B{UcIOs{ng|~1 zwx%(=s={CoQgZM_+j(kK;M=N|HY4lPffLyNrS?(eTivKPD)r4$H6BJ&YvIx*yD@); z^M^Q3+WUKxa78T%*l3h$O{;XTvDMyw9_0t7R+LM%UaD4_-O^@bqkgY;zt-A*G;wHy zLw&PSDOJJVxL<3`CV#!#HvU5!RA-S?$mAKt1LPI=TaRaHRqMrjmFiZbQrRfBij#oV zdUK*}wwe=dd$)B|YZNb^5*wT6XR^3YP7a8TW-+ZJl5%6))YwH8wF`z%<5ky^bBOKT z(j>Rp30-2D%nfsC91g#LJRVsV_5+NXvz($p^(0OlH5nXrt6S4`7QgJQChOxk1uvZp`aSL|Y|XInS<^V^aDYSd~o(ge|rz`!dj2zXgqc z4I53Af%X0%L7RBJi$HRi@`;Kizm80CFp8NI>$Z5Ek zR=?^yYuQjbhP{vluEb5Q;kiXAQcy~C~YvlHvcB>iW^>p@+iWhj1u zk#Kfe@?x#&+hshdauz%7N!Ov<%3{8>yN&EyBzI0Wmq*4%OLUxXb9;?zA+f)96Y6z;iT@r0S}+^Ce@A z+8Pi7(9r0>_dNn+I_pE#qPB|<_wdUPB~1I=9wvl|FkrD=vybs)%9RcbBCfy#d@2M& zPXum=BN6ey4FMSNc_8)V5o2e}jo89i^B62j$|5A(kUQ^V1Ia_wi)_e{MbPU7cA)92 zh_Ceu*dqLstE{&KVof5;tp5=c>}JU|CT4dZT|2|WH_#}H$_eTj{9Srv3I51U@F%yJ zxXr{JChjuvZk%c0vI8x~nL^ljSWa+u)IFvf1Rr96goURlW9l>Sd{{WI8vY7sI)wGDN?6-!N`pKZ#t`;0&jXknjTw_#KTih!cBK>Fs9d}XH19Bt)oI!q zO(IgwBv`XzfIBDJz@378{R7dw^yJJ*{XemsW%tFd`@IYYlSd>2ruvpgS`yCGoXZ7i3lR(h|K!zc_QpB4(Fy%Mnvj(`;0)L zOuRvWLm6H*vme#qh#*mn6m=sKGyaCz2XX1&X5pfr4+TEQ=aej;l@$S=6JAx+!m^yd Mk>!^~bp@pSFJGcgZvX%Q From 33c3cd2ea778340277567156d520d9d74e84430c Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Fri, 15 Nov 2019 13:49:20 +0000 Subject: [PATCH 021/218] Add incrementing version information --- Dockerfile | 4 ++-- configuration/docker.ini | 2 +- rest_VariantValidator/__init__.py | 13 +++++++++++++ {rest_api => rest_VariantValidator}/app.py | 4 ++-- .../endpoints/__init__.py | 4 ++-- .../endpoints/exceptions.py | 0 .../endpoints/hello.py | 0 .../endpoints/name.py | 0 .../endpoints/representations.py | 0 .../endpoints/request_parser.py | 0 .../endpoints/variantformatter_endpoints.py | 0 .../endpoints/variantvalidator_endpoints.py | 0 .../utils/__init__.py | 0 setup.py | 9 ++++++++- 14 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 rest_VariantValidator/__init__.py rename {rest_api => rest_VariantValidator}/app.py (97%) rename {rest_api => rest_VariantValidator}/endpoints/__init__.py (95%) rename {rest_api => rest_VariantValidator}/endpoints/exceptions.py (100%) rename {rest_api => rest_VariantValidator}/endpoints/hello.py (100%) rename {rest_api => rest_VariantValidator}/endpoints/name.py (100%) rename {rest_api => rest_VariantValidator}/endpoints/representations.py (100%) rename {rest_api => rest_VariantValidator}/endpoints/request_parser.py (100%) rename {rest_api => rest_VariantValidator}/endpoints/variantformatter_endpoints.py (100%) rename {rest_api => rest_VariantValidator}/endpoints/variantvalidator_endpoints.py (100%) rename {rest_api => rest_VariantValidator}/utils/__init__.py (100%) diff --git a/Dockerfile b/Dockerfile index ffcb99b..000d0b3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,5 +23,5 @@ RUN pip install -e . COPY configuration/docker.ini /root/.variantvalidator # Start the application with gunicorn -# CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ -CMD gunicorn -b 0.0.0.0:8080 app --threads=5 --worker-class=gthread --chdir ./rest_api/ +# CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_VariantValidator/ +CMD gunicorn -b 0.0.0.0:8080 app --threads=5 --worker-class=gthread --chdir ./rest_VariantValidator/ diff --git a/configuration/docker.ini b/configuration/docker.ini index 1dd9b1e..6cc67c6 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -23,7 +23,7 @@ file = ERROR [Entrez] email = admin@variantvalidator.org -api_key = 5becaa525b83ff4366802022464b5d0f3908 +api_key = a991783f8c1e9e1958d1e6bd90b6f4046f08 # diff --git a/rest_VariantValidator/__init__.py b/rest_VariantValidator/__init__.py new file mode 100644 index 0000000..b79e898 --- /dev/null +++ b/rest_VariantValidator/__init__.py @@ -0,0 +1,13 @@ +import pkg_resources +import re +import warnings + +# Pull in use_scm_version=True enabled version number +_is_released_version = False +try: + __version__ = pkg_resources.get_distribution("rest_VariantValidator").version + if re.match(r"^\d+\.\d+\.\d+$", __version__) is not None: + _is_released_version = True +except pkg_resources.DistributionNotFound as e: + warnings.warn("can't get __version__ because %s package isn't installed" % __package__, Warning) + __version__ = None diff --git a/rest_api/app.py b/rest_VariantValidator/app.py similarity index 97% rename from rest_api/app.py rename to rest_VariantValidator/app.py index 71e6f2d..0349168 100644 --- a/rest_api/app.py +++ b/rest_VariantValidator/app.py @@ -13,14 +13,14 @@ """ Logging """ -logger = logging.getLogger('rest_api') +logger = logging.getLogger('rest_VariantValidator') # We are setting 2 types of logging. To screen at the level DEBUG logger.setLevel(logging.INFO) # We will also log to a file # Log with a rotating file-handler. This sets the maximum size of the log to 0.5Mb and allows two additional logs # The logs are then deleted and replaced in rotation -logHandler = handlers.RotatingFileHandler('rest_api.log', maxBytes=500000, backupCount=2) +logHandler = handlers.RotatingFileHandler('rest_VariantValidator.log', maxBytes=500000, backupCount=2) # We want to minimise the amount of information we log to capturing bugs logHandler.setLevel(logging.ERROR) logger.addHandler(logHandler) diff --git a/rest_api/endpoints/__init__.py b/rest_VariantValidator/endpoints/__init__.py similarity index 95% rename from rest_api/endpoints/__init__.py rename to rest_VariantValidator/endpoints/__init__.py index 45dca14..35a6d8b 100644 --- a/rest_api/endpoints/__init__.py +++ b/rest_VariantValidator/endpoints/__init__.py @@ -1,12 +1,12 @@ +import rest_VariantValidator from flask_restplus import Api - # from .hello import api as ns_hello # from .name import api as ns_name from .variantvalidator_endpoints import api as ns_vv from .variantformatter_endpoints import api as ns_vf # Define the API as api -api = Api(version="1.0.1", +api = Api(version=rest_VariantValidator.__version__, title="rest_variantValidator", description="### REST API for [VariantValidator](https://github.com/openvar/variantValidator) and" " [VariantFormatter](https://github.com/openvar/variantFormatter)" diff --git a/rest_api/endpoints/exceptions.py b/rest_VariantValidator/endpoints/exceptions.py similarity index 100% rename from rest_api/endpoints/exceptions.py rename to rest_VariantValidator/endpoints/exceptions.py diff --git a/rest_api/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py similarity index 100% rename from rest_api/endpoints/hello.py rename to rest_VariantValidator/endpoints/hello.py diff --git a/rest_api/endpoints/name.py b/rest_VariantValidator/endpoints/name.py similarity index 100% rename from rest_api/endpoints/name.py rename to rest_VariantValidator/endpoints/name.py diff --git a/rest_api/endpoints/representations.py b/rest_VariantValidator/endpoints/representations.py similarity index 100% rename from rest_api/endpoints/representations.py rename to rest_VariantValidator/endpoints/representations.py diff --git a/rest_api/endpoints/request_parser.py b/rest_VariantValidator/endpoints/request_parser.py similarity index 100% rename from rest_api/endpoints/request_parser.py rename to rest_VariantValidator/endpoints/request_parser.py diff --git a/rest_api/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py similarity index 100% rename from rest_api/endpoints/variantformatter_endpoints.py rename to rest_VariantValidator/endpoints/variantformatter_endpoints.py diff --git a/rest_api/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py similarity index 100% rename from rest_api/endpoints/variantvalidator_endpoints.py rename to rest_VariantValidator/endpoints/variantvalidator_endpoints.py diff --git a/rest_api/utils/__init__.py b/rest_VariantValidator/utils/__init__.py similarity index 100% rename from rest_api/utils/__init__.py rename to rest_VariantValidator/utils/__init__.py diff --git a/setup.py b/setup.py index 09cb1d1..0deacad 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,11 @@ setup( name='rest_VariantValidator', - version=open('VERSION.txt').read(), +# version=open('VERSION.txt').read(), + use_scm_version=True, + zip_safe=True, + author="VariantValidator Contributors", + author_email = 'admin@variantvalidator.org', description='Rest API for VariantValidator', long_description=open('README.md').read(), url='https://github.com/openvar/variantFormatter', @@ -56,6 +60,9 @@ "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@master#egg=VariantFormatter", "biocommons.seqrepo>=0.5.1", + ], + setup_requires=[ + "setuptools_scm", ] ) From d40bf800b5c90a7b5025e9518e90bdf5d827fa2d Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Mon, 18 Nov 2019 11:51:00 +0000 Subject: [PATCH 022/218] Update setup to provide auto incrementing version numbering --- VERSION.txt | 1 - rest_VariantValidator/endpoints/__init__.py | 30 ++++++++++++++++----- setup.py | 1 - 3 files changed, 23 insertions(+), 9 deletions(-) delete mode 100644 VERSION.txt diff --git a/VERSION.txt b/VERSION.txt deleted file mode 100644 index 6a2b0ac..0000000 --- a/VERSION.txt +++ /dev/null @@ -1 +0,0 @@ -v1.0.1 \ No newline at end of file diff --git a/rest_VariantValidator/endpoints/__init__.py b/rest_VariantValidator/endpoints/__init__.py index 35a6d8b..f67336a 100644 --- a/rest_VariantValidator/endpoints/__init__.py +++ b/rest_VariantValidator/endpoints/__init__.py @@ -1,19 +1,35 @@ import rest_VariantValidator +import VariantValidator +import VariantFormatter from flask_restplus import Api # from .hello import api as ns_hello # from .name import api as ns_name from .variantvalidator_endpoints import api as ns_vv from .variantformatter_endpoints import api as ns_vf +# Obtain VariantValidator related metadata +vval = VariantValidator.Validator() +config_dict = vval.my_config() + + # Define the API as api api = Api(version=rest_VariantValidator.__version__, - title="rest_variantValidator", - description="### REST API for [VariantValidator](https://github.com/openvar/variantValidator) and" - " [VariantFormatter](https://github.com/openvar/variantFormatter)" - "   \n" - "- [Source code](https://github.com/openvar/rest_variantValidator)\n" - "- [Terms of use and about VariantValidator](https://github.com/openvar/variantValidator/blob" - "/master/README.md)") + title="rest_VariantValidator", + description="## By continuing to use this service you agree to our terms and conditions of Use\n" + "- [Terms and Conditions](https://github.com/openvar/variantValidator/blob" + "/master/README.md)\n\n" + "## Powered by\n" + "- [VariantValidator](https://github.com/openvar/rest_variantValidator) version " + + VariantValidator.__version__ + "\n" + "- [VariantFormatter](https://github.com/openvar/variantFormatter) version " + + VariantFormatter.__version__ + "\n" + " - [vv_hgvs](https://github.com/openvar/vv_hgvs) version " + + config_dict['variantvalidator_hgvs_version'] + "\n" + " - [UTA](https://github.com/biocommons/uta) release " + + config_dict['uta_schema'] + "\n" + " - [SeqRepo](https://github.com/biocommons/biocommons.seqrepo) release " + + config_dict['seqrepo_db'].split('/')[-1] + ) # api.add_namespace(ns_hello) # api.add_namespace(ns_name) diff --git a/setup.py b/setup.py index 0deacad..44aac20 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,6 @@ setup( name='rest_VariantValidator', -# version=open('VERSION.txt').read(), use_scm_version=True, zip_safe=True, author="VariantValidator Contributors", From def44e4f4e1cd88703eece4b89f5ebfd52e727a8 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 19 Nov 2019 12:34:19 +0000 Subject: [PATCH 023/218] Updated to use stable releases of VV vvhgvs and VF --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 139a535..3f8615a 100644 --- a/setup.py +++ b/setup.py @@ -48,9 +48,9 @@ "flask-cors", "flask-restful", "gunicorn", - "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@master#egg=vvhgvs", - "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", - "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@master#egg=VariantFormatter", + "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs", + "VariantValidator @ git+https://github.com/openvar/variantValidator.git@v1.0.1#egg=VariantValidator", + "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@v1.0.1#egg=VariantFormatter", "vv_flask_restful_swagger @ git+https://github.com/openvar/vv-flask-restful-swagger.git@master#egg=vv_flask_restful_swagger", ] ) From 40b826c84ebf1e2a8113f49ea26fda36e36583e2 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 19 Nov 2019 14:37:29 +0000 Subject: [PATCH 024/218] Modifications for v2 stable tag --- Dockerfile | 2 +- rest_VariantValidator/__init__.py | 17 +++++++++++++++++ rest_VariantValidator/app.py | 2 ++ .../endpoints/variantvalidator_endpoints.py | 4 +++- setup.py | 8 ++++---- 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 000d0b3..65a36b7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,4 +24,4 @@ COPY configuration/docker.ini /root/.variantvalidator # Start the application with gunicorn # CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_VariantValidator/ -CMD gunicorn -b 0.0.0.0:8080 app --threads=5 --worker-class=gthread --chdir ./rest_VariantValidator/ +CMD gunicorn -b 0.0.0.0:8000 app --threads=5 --worker-class=gthread --chdir ./rest_VariantValidator/ diff --git a/rest_VariantValidator/__init__.py b/rest_VariantValidator/__init__.py index b79e898..6f5a9f8 100644 --- a/rest_VariantValidator/__init__.py +++ b/rest_VariantValidator/__init__.py @@ -11,3 +11,20 @@ except pkg_resources.DistributionNotFound as e: warnings.warn("can't get __version__ because %s package isn't installed" % __package__, Warning) __version__ = None + +# +# Copyright (C) 2019 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 0349168..057c3a3 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -58,12 +58,14 @@ @api.representation('application/xml') def application_xml(data, code, headers): resp = representations.xml(data, code, headers) + resp.headers['Content-Type'] = 'application/xml' return resp @api.representation('application/json') def application_json(data, code, headers): resp = representations.application_json(data, code, headers) + resp.headers['Content-Type'] = 'application/json' return resp diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index b9cf092..92820a8 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -39,8 +39,10 @@ "\n***Hybrid***\n" "> - chr17:50198002C>A\n " "> - chr17:50198002C>A(GRCh38)\n" + "> - chr17(GRCh38):50198002C>A\n" "> - chr17:g.50198002C>A\n" - "> - chr17:g.50198002C>A(GRCh38)") + "> - chr17:g.50198002C>A(GRCh38)\n" + "> - chr17(GRCh38):g.50198002C>A") @api.param("genome_build", "***Accepted:***\n" "> - GRCh37\n" "> - GRCh38\n" diff --git a/setup.py b/setup.py index 44aac20..e45b53a 100644 --- a/setup.py +++ b/setup.py @@ -54,10 +54,10 @@ "biotools>=0.3.0", "mysql-connector-python", "requests", - " dicttoxml", - "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@master#egg=vvhgvs", - "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", - "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@master#egg=VariantFormatter", + "dicttoxml", + "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs", + "VariantValidator @ git+https://github.com/openvar/variantValidator.git@v1.0.1#egg=VariantValidator", + "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@v1.0.1#egg=VariantFormatter", "biocommons.seqrepo>=0.5.1", ], setup_requires=[ From 2d1e6bdb46bf0043d1366076797b0a760f6a53e9 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 19 Nov 2019 14:58:31 +0000 Subject: [PATCH 025/218] Update docker.ini --- configuration/docker.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configuration/docker.ini b/configuration/docker.ini index 6cc67c6..c49617d 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -22,8 +22,8 @@ console = DEBUG file = ERROR [Entrez] -email = admin@variantvalidator.org -api_key = a991783f8c1e9e1958d1e6bd90b6f4046f08 +email = 'YOUR_EMAIL_ADDRESS' +api_key = 'YOUR_API_KEY' # @@ -41,4 +41,4 @@ api_key = a991783f8c1e9e1958d1e6bd90b6f4046f08 # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -# \ No newline at end of file +# From 925094df61ed6d023e9faa5c31802482cad74b51 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 19 Nov 2019 15:59:10 +0000 Subject: [PATCH 026/218] nothing much --- configuration/docker.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configuration/docker.ini b/configuration/docker.ini index fed62ea..c49617d 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -22,8 +22,8 @@ console = DEBUG file = ERROR [Entrez] -email = -api_key = +email = 'YOUR_EMAIL_ADDRESS' +api_key = 'YOUR_API_KEY' # From cd60d1c54e171f9ab40dab1a26e9f5c03fa54ad1 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 21 Nov 2019 14:05:39 +0000 Subject: [PATCH 027/218] Set VV to use masxter --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3f8615a..c30626c 100644 --- a/setup.py +++ b/setup.py @@ -49,7 +49,7 @@ "flask-restful", "gunicorn", "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs", - "VariantValidator @ git+https://github.com/openvar/variantValidator.git@v1.0.1#egg=VariantValidator", + "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@v1.0.1#egg=VariantFormatter", "vv_flask_restful_swagger @ git+https://github.com/openvar/vv-flask-restful-swagger.git@master#egg=vv_flask_restful_swagger", ] From 81ed27e9172fb247c10a2f1e105f91e2ebb03051 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Mon, 2 Dec 2019 09:38:25 +0000 Subject: [PATCH 028/218] Update to latest tag --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c30626c..7940159 100644 --- a/setup.py +++ b/setup.py @@ -49,7 +49,7 @@ "flask-restful", "gunicorn", "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs", - "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", + "VariantValidator @ git+https://github.com/openvar/variantValidator.git@v1.0.2#egg=VariantValidator", "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@v1.0.1#egg=VariantFormatter", "vv_flask_restful_swagger @ git+https://github.com/openvar/vv-flask-restful-swagger.git@master#egg=vv_flask_restful_swagger", ] From a8fdb57cb211745bf89c4705fc30b07d714102b7 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Mon, 2 Dec 2019 09:41:32 +0000 Subject: [PATCH 029/218] update tag to latest release of VV = v1.0.2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e45b53a..e5aa4cd 100644 --- a/setup.py +++ b/setup.py @@ -56,7 +56,7 @@ "requests", "dicttoxml", "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs", - "VariantValidator @ git+https://github.com/openvar/variantValidator.git@v1.0.1#egg=VariantValidator", + "VariantValidator @ git+https://github.com/openvar/variantValidator.git@v1.0.2#egg=VariantValidator", "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@v1.0.1#egg=VariantFormatter", "biocommons.seqrepo>=0.5.1", ], From d930f699595666b58c9ed59c7969c5839c9d47a0 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 3 Dec 2019 17:29:37 +0000 Subject: [PATCH 030/218] revert to master due to known issue with 1.0.2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7940159..c30626c 100644 --- a/setup.py +++ b/setup.py @@ -49,7 +49,7 @@ "flask-restful", "gunicorn", "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs", - "VariantValidator @ git+https://github.com/openvar/variantValidator.git@v1.0.2#egg=VariantValidator", + "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@v1.0.1#egg=VariantFormatter", "vv_flask_restful_swagger @ git+https://github.com/openvar/vv-flask-restful-swagger.git@master#egg=vv_flask_restful_swagger", ] From 52c58aa94b667ae6f9719f76bcb3873b306af0ce Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 3 Dec 2019 17:30:09 +0000 Subject: [PATCH 031/218] revert to master due to known issue with 1.0.2 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e5aa4cd..e383154 100644 --- a/setup.py +++ b/setup.py @@ -56,7 +56,7 @@ "requests", "dicttoxml", "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs", - "VariantValidator @ git+https://github.com/openvar/variantValidator.git@v1.0.2#egg=VariantValidator", + "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@v1.0.1#egg=VariantFormatter", "biocommons.seqrepo>=0.5.1", ], From a4a4385cc0c89c15513013751e9002f46de81173 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 4 Dec 2019 12:25:51 +0000 Subject: [PATCH 032/218] Tweak setup and add locust based testing --- REQUIREMENTS.txt | 2 + locust/__init__.py | 0 locust/__pycache__/locust.cpython-36.pyc | Bin 0 -> 1585 bytes locust/__pycache__/test_set.cpython-36.pyc | Bin 0 -> 7684 bytes locust/locust.py | 52 ++++ locust/test_set.py | 343 +++++++++++++++++++++ rest_VariantValidator/app.py | 5 +- setup.py | 1 + wsgi.py | 4 +- 9 files changed, 404 insertions(+), 3 deletions(-) create mode 100644 locust/__init__.py create mode 100644 locust/__pycache__/locust.cpython-36.pyc create mode 100644 locust/__pycache__/test_set.cpython-36.pyc create mode 100644 locust/locust.py create mode 100644 locust/test_set.py diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index a3b38eb..ec879a9 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -12,4 +12,6 @@ biotools>=0.3.0 mysql-connector-python requests dicttoxml +locustio + diff --git a/locust/__init__.py b/locust/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/locust/__pycache__/locust.cpython-36.pyc b/locust/__pycache__/locust.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..863824207610e86dcbfd38ea50c47bf12c670c07 GIT binary patch literal 1585 zcma)6UymC#5V!YVHn}@mXsIgT!S{e5Rj&)}1ES*3!BJ5JicYXaVT^ z(9I7ZIN?;1nx?e&Qm^(?zYfxX;=CurH2jfpp9e1p4}`b$(+I{Pk6;`L3gZso&2JTsj+S6I5*FZglz*0O4}IYskzXXwf} zC#NeXrx#AdI;LT>(vDfqblz~)$T>4Yioy!c)_d4Us~QYsY3zpkyPNJ$L?iB6aA68v zPAvN$-f;^v^LnV{r3fy>Kv{909gl?(EZ4#v!Oj$`m9cCr06$1{Y9L9glr*>5aB5)@ zE!~l4rIc(4dpQ@}c|7-kvH}oy4T;2w1L%fM3zNvOVuQmQ4N${jCp28*tCn=GX%Gd z3R%LY);|%p@3#RkF-o@_F?Ht3(w}PiC(%2Q@ynkRtR|C8s?nFp;k+2C{?({j}FH7zF6$$QvL_9p^hPm zbbIY%1#3{j<f2fN+ccN!l}?s%Rb-jQmrY+maSg@m44j5( zXyoEu=#G@Q8_=tfn=4@MqG2)bLpL9Qc=aj?KMh34DZEP_ygxpaaL{f&5kpg2f$3`` z@*Dwy#PxOf>-SOIK!I_gM&TL38r4W~+nwd5&1_kVx2Ad%Z1$iVJXTBts&S?tx6!KD lG~PFjxvmb5J{?;wm@|Hx-!}egh2uAvufyRcjjvq&=pV4+qagqQ literal 0 HcmV?d00001 diff --git a/locust/__pycache__/test_set.cpython-36.pyc b/locust/__pycache__/test_set.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3c6321cb5f620d97a396184d20bb5808fbf3e875 GIT binary patch literal 7684 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 fT", + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NM_000088.3:c.589GG>CT", + "NM_000094.3:c.6751-2_6751-3del", + "COL5A1:c.5071A>T", + "NG_007400.1:c.5071A>T", + "chr16:15832508_15832509delinsAC", + "chr16:15832508_15832509delinsAC", + "chr16:15832508_15832509delinsAC", + "chr16:15832508_15832509delinsAC", + "NM_000088.3:c.589-1GG>G", + "NM_000088.3:c.642+1GT>G", + "NM_000088.3:c.589-2AG>G", + "NC_000017.10:g.48279242G>T", + "NM_000500.7:c.-107-19C>T", + "NM_000518.4:c.-130C>T", + "NR_138595.1:n.-810C>T", + "NR_138595.1:n.1-810C>T", + "NC_000017.10:g.48261457_48261463TTATGTT=", + "NC_000017.10:g.48275363C>A", + "NM_000088.3:c.589-1G>T", + "NM_000088.3:c.591_593inv", + "11-5248232-T-A", + "NG_007400.1(NM_000088.3):c.589-1G>T", + "1:150550916G>A", + "1:150550916G>A", + "1:150550916G>A", + "1-150550916-G-A", + "1-150550916-G-A", + "1-150550916-G-A", + "NG_008123.1(LEPRE1_v003):c.2055+18G>A", + "NG_008123.1:c.2055+18G>A", + "NG_008123.1(NM_022356.3):c.2055+18G>A", + "NM_021983.4:c.490G>C", + "NM_032470.3:c.4del", + "NM_080680.2:c.1872+5G>A", + "NM_001194958.2:c.20C>A", + "NM_000022.2:c.534A>G", + "HSCHR6_MHC_SSTO_CTG1-3852542-C-G", + "NM_000368.4:c.363+1dupG", + "NM_000368.4:c.363dupG", + "NM_000089.3:c.1033_1035delGTT", + "NM_000089.3:c.1035_1035+2delTGT", + "NM_000088.3:c.2023_2028delGCAAGA", + "NM_000089.3:c.938-1delG", + "NM_000088.3:c.589G=", + "NM_000088.3:c.642A=", + "NM_000088.3:c.642+1GG>G", + "NM_000088.3:c.589-2GG>G", + "NM_000088.3:c.589-6_589-5insTTTT", + "NM_000088.3:c.642+3_642+4insAAAA", + "NM_000088.3:c.589-4_589-3insTT", + "NM_000088.3:c.589-8del", + "NM_000527.4:c.-187_-185delCTC", + "NM_206933.2:c.6317C>G", + "NC_000013.10:g.32929387T>C", + "NM_015102.3:c.2818-2T>A", + "19-41123094-G-GG", + "19-41123094-G-GG", + "19-41123094-G-GG", + "15-72105928-AC-A", + "15-72105928-AC-A", + "15-72105928-AC-A", + "15-72105928-AC-A", + "12-122064773-CCCGCCA-C", + "12-122064774-CCGCCA-CCGCCA", + "12-122064773-CCCGCCACCGCCACCGC-CCCGCCACCGCCGCCGTC", + "NC_000012.11:g.122064777C>A", + "NC_000012.11:g.122064776delG", + "NC_000012.11:g.122064776dupG", + "NC_000012.11:g.122064776_122064777insTTT", + "NC_000012.11:g.122064772_122064775del", + "NC_000012.11:g.122064772_122064775dup", + "NC_000012.11:g.122064773_122064774insTTTT", + "NC_000012.11:g.122064772_122064777del", + "NC_000012.11:g.122064772_122064777dup", + "NC_000012.11:g.122064779_122064782dup", + "NC_000012.11:g.122064772_122064782del", + "NC_000002.11:g.95847041_95847043GCG=", + "NC_000002.11:g.95847041_95847043GCG=", + "NC_000002.11:g.95847041_95847043GCG=", + "NC_000002.11:g.95847041_95847043GCG=", + "NC_000002.11:g.95847041_95847043GCG=", + "NC_000002.11:g.95847041_95847043GCG=", + "NC_000002.11:g.95847041_95847043GCG=", + "NC_000017.10:g.5286863_5286889AGTGTTTGGAATTTTCTGTTCATATAG=", + "NC_000017.10:g.5286863_5286889AGTGTTTGGAATTTTCTGTTCATATAG=", + "NC_000017.10:g.5286863_5286889AGTGTTTGGAATTTTCTGTTCATATAG=", + "NC_000017.10:g.5286863_5286889AGTGTTTGGAATTTTCTGTTCATATAG=", + "NC_000017.10:g.5286863_5286889AGTGTTTGGAATTTTCTGTTCATATAG=", + "NC_000003.11:g.14561629_14561630GC=", + "NC_000003.11:g.14561629_14561630GC=", + "NC_000003.11:g.14561629_14561630insG", + "NC_000003.11:g.14561629_14561630insG", + "NC_000004.11:g.140811111_140811122del", + "NC_000004.11:g.140811111_140811122CTGCTGCTGCTG=", + "NC_000004.11:g.140811117_140811122del", + "NC_000004.11:g.140811111_140811117del", + "NC_000004.11:g.140811117C>A", + "NC_000002.11:g.73675227_73675228insCTC", + "9-136132908-T-TC", + "9-136132908-TAC-TCA", + "9-136132908-TA-TA", + "NM_020469.2:c.258delG", + "NM_020469.2:c.260_262TGA=", + "NM_020469.2:c.261delG", + "NM_020469.2:c.261dupG", + "NM_020469.2:c.261_262insTT", + "NC_000019.10:g.50378563_50378564insTAC", + "NC_000019.10:g.50378563_50378564insC", + "NC_000019.10:g.50378564_50378565insTACA", + "NC_000019.10:g.50378565_50378567dup", + "NC_000019.10:g.50378563_50378564=", + "NC_000019.10:g.50378563_50378564insTCGG", + "NC_000019.10:g.50378563_50378564insC", + "NC_000019.10:g.50378563delinsTTAC", + "NC_000019.10:g.50378563_50378564insTAAC", + "NC_000019.10:g.50378562_50378565del", + "NC_000019.10:g.50378562_50378565delinsTC", + "NC_000007.14:g.149779575_149779577delinsT", + "NC_000007.14:g.149779575_149779577=", + "NC_000007.14:g.149779576_149779578del", + "NC_000007.14:g.149779577del", + "NC_000007.14:g.149779573_149779579del", + "NC_000007.14:g.149779573_149779579delinsCA", + "NC_000004.12:g.139889957_139889968del", + "NM_015120.4:c.35T>C", + "NM_015120.4:c.39G>C", + "NM_015120.4:c.34C>T", + "NC_000002.11:g.73613030C>T", + "NM_000088.3:c.590_591inv", + "NM_024989.3:c.1778_1779inv", + "NM_032815.3:c.555_556inv", + "NM_001034853.1:c.2847_2848delAGinsCT", + "NM_000038.5:c.3927_3928delAAinsTT", + "NM_198180.2:c.408_410delGTG", + "NM_080877.2:c.1733_1735delinsTTT", + "NM_080877.2:c.1735_1737delinsTGA", + "NM_080877.2:c.1735_1737delinsTAATTGTTC", + "NM_080877.2:c.1737delinsATTGTTC", + "NM_006138.4:c.3_4inv", + "NM_000088.3:c.4392_*2inv", + "NM_000088.3:c.4392_*5inv", + "NM_000088.3:c.4390_*7inv", + "NM_000088.3:c.4392_*2delinsAGAG", + "NM_000088.3:c.589_591delinsAGAAGC", + "NM_000885.5:c.*2536delinsAGAAAAATCA", + "NM_002693.2:c.-186_-185delinsCC", + "NG_009616.1:g.29052_29053insCTACATAG", + "NM_000061.2:c.588_588+1insCTACATAG", + "NM_000061.2:c.588_589insCTACATAG", + "NM_005732.3:c.2923-5insT", + "NM_198283.1(EYS):c.*743120C>T", + "NM_133379.4(TTN):c.*265+26591C>T", + "NM_000088.3:c.589-2_589-1AG>G", + "NM_000088.3:c.642+1_642+2delGTinsG", + "NM_001260.1:c.967C>T", + "NM_004415.3:c.1-1insA", + "NM_000273.2:c.1-5028_253del", + "NM_002929.2:c.1006C>T", + "NR_125367.1:n.167+18165G>A", + "NM_006005.3:c.3071_3073delinsTTA", + "NM_000089.3:n.1504_1506del", + "NC_012920.1:m.1011C>T", + "NC_000006.11:g.90403795G=", + "NC_000006.11:g.90403795G=", + "1-169519049-T-.", + "1-169519049-T-.", + "NC_000005.9:g.35058667_35058668AG=", + "NC_000005.9:g.35058667_35058668AG=", + "NC_000005.9:g.35058667_35058668AG=", + "NC_000005.9:g.35058667_35058668AG=", + "NC_000005.9:g.35058667_35058668AG=", + "NC_000005.9:g.35058667_35058668AG=", + "NC_000005.9:g.35058667_35058668AG=", + "NC_000005.9:g.35058667_35058668AG=", + "NM_000251.1:c.1296_1348del", + "NM_000088.3:c.2023_2028del", + "NM_000088.3:c.2024_2028+1del", + "ENST00000450616.1:n.31+1G>C", + "ENST00000491747:c.5071A>T", + "NM_000088.3:c.589G>T", + "NG_007400.1:g.8638G>T", + "LRG_1:g.8638G>T", + "LRG_1t1:c.589G>T", + "chr16:15832508_15832509delinsAC", + "chr16:15832508_15832509delinsAC", + "chr16:15832508_15832509delinsAC", + "chr16:g.15832508_15832509delinsAC", + "NG_012386.1:g.24048dupG", + "NG_012386.1:g.24048dupG", + "NG_012386.1:g.24048dupG", + "NM_033517.1:c.1307_1309delCGA", + "HG1311_PATCH-33720-CCGA-C", + "2-73675227-TCTC-TCTCCTC", + "2-73675227-TC-TC", + "3-14561627-AG-AGG", + "3-14561627-AG-AGG", + "3-14561630-CC-CC", + "3-14561630-CC-CC", + "6-90403795-G-G", + "6-90403795-G-G", + "6-90403795-G-A", + "6-90403795-G-A", + "6-32012992-CG-C", + "6-32012992-CG-C", + "17-48275363-C-A", + "17-48275364-C-A", + "17-48275359-GGA-TCC", + "7-94039128-CTTG-C", + "9-135800972-AC-ACC", + "9-135800972-AC-ACC", + "9-135800972-AC-ACC", + "1-43212925-C-T", + "1-43212925-C-T", + "1-43212925-C-T", + "HG987_PATCH-355171-C-A", + "20-43252915-T-C", + "20-43252915-T-C", + "20-43252915-T-C", + "20-43252915-T-C", + "20-43252915-T-C", + "1-216219781-A-C", + "2-209113113-G-A,C,T", + "2-209113113-G-A,C,T", + "2-209113113-G-A,C,T", + "2-209113113-G-A,C,T", + "2-209113113-G-A,C,T", + "2-209113113-G-A,C,T", + "2-209113113-G-A,C,T", + "2-209113113-G-A,C,T", + "2-209113113-G-A,C,T", + "2-209113113-G-A,C,T", + "2-209113113-G-A,C,T", + "2-209113113-G-A,C,T", + "NC_000005.9:g.35058665_35058666CA=", + "NC_000005.9:g.35058665_35058666CA=", + "NC_000005.9:g.35058665_35058666CA=", + "NC_000005.9:g.35058665_35058666CA=", + "NC_000005.9:g.35058665_35058666CA=", + "NC_000005.9:g.35058665_35058666CA=", + "NC_000005.9:g.35058665_35058666CA=", + "NC_000005.9:g.35058665_35058666CA=", + "NC_000002.11:g.73675227_73675229delTCTinsTCTCTC", + "NC_000002.11:g.73675227_73675228insCTC", + "NC_000017.10:g.5286863_5286889AGTGTTTGGAATTTTCTGTTCATATAG=", + "NC_000017.10:g.5286863_5286889AGTGTTTGGAATTTTCTGTTCATATAG=", + "NC_000017.10:g.5286863_5286889AGTGTTTGGAATTTTCTGTTCATATAG=", + "NC_000017.10:g.5286863_5286889AGTGTTTGGAATTTTCTGTTCATATAG=", + "NC_000017.10:g.5286863_5286889AGTGTTTGGAATTTTCTGTTCATATAG=", + "NM_000828.4:c.-2dupG", + "X-122318386-A-AGG", + "X-122318386-A-AGG", + "X-122318386-A-AGG", + "NM_000828.4:c.-2G>T", + "NM_000828.4:c.-2G=", + "X-122318386-A-AT", + "X-122318386-A-AT", + "X-122318386-A-AT", + "NM_000828.4:c.-2_-1insT", + "NM_000828.4:c.-3_-2insT", + "NM_000828.4:c.-2delGinsTT", + "NM_000828.4:c.-2_-1delGCinsTT", + "NM_000828.4:c.-3_-2delAGinsTT", + "15-72105929-C-C", + "15-72105929-C-C", + "15-72105929-C-C", + "15-72105929-C-C", + "15-72105928-AC-ATT", + "15-72105928-AC-ATT", + "15-72105928-AC-ATT", + "15-72105928-AC-ATT", + "15-72105928-ACC-ATT", + "15-72105928-ACC-ATT", + "15-72105928-ACC-ATT", + "15-72105928-ACC-ATT", + "15-72105927-GACC-GTT", + "15-72105927-GACC-GTT", + "15-72105927-GACC-GTT", + "15-72105927-GACC-GTT", + "19-41123093-A-AG", + "19-41123093-A-AG", + "19-41123093-A-AG", + "19-41123093-A-AT", + "19-41123093-A-AT", + "19-41123093-A-AT", + "19-41123093-AG-A", + "19-41123093-AG-A", + "19-41123093-AG-A", + "19-41123093-AG-AG", + "19-41123093-AG-AG", + "19-41123093-AG-AG", + "NC_000003.11:g.14561629_14561630insG", + "NC_000003.11:g.14561629_14561630insG", + "NM_012309.4:c.913-5058G>A", + "LRG_199t1:c.2376[G>C];[G>C]", + "LRG_199t1:c.2376[G>C];[G>C]", + "LRG_199t1:c.[2376G>C];[3103del]", + "LRG_199t1:c.[2376G>C];[3103del]", + "LRG_199t1:c.[4358_4359del;4361_4372del]", + "LRG_199t1:c.2376G>C(;)3103del", + "LRG_199t1:c.2376G>C(;)3103del", + "LRG_199t1:c.2376[G>C];[(G>C)]", + "LRG_199t1:c.[2376G>C];[?]", + "LRG_199t1:c.[296T>G;476T=];[476T=](;)1083A>C", + "LRG_199t1:c.[296T>G;476T=];[476T=](;)1083A>C", + "LRG_199t1:c.[296T>G;476T=];[476T=](;)1083A>C", + "LRG_199t1:c.[296T>G];[476T>C](;)1083A>C(;)1406del", + "LRG_199t1:c.[296T>G];[476T>C](;)1083A>C(;)1406del", + "LRG_199t1:c.[296T>G];[476T>C](;)1083A>C(;)1406del", + "LRG_199t1:c.[296T>G];[476T>C](;)1083A>C(;)1406del", + "LRG_199t1:c.[976-20T>A;976-17_976-1dup]" + ] + random_number = random.randint(0, 319) + odd_job = tests[random_number] + return odd_job + +def gene_list(): + tests = [ + "BRCA1", + "COL1A1", + "TP53", + "COL5A1", + "BRCA2", + "HBB", + "DMD" + ] + + random_number = random.randint(0, 6) + random_task = tests[random_number] + return random_task \ No newline at end of file diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 057c3a3..22dbb0d 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -4,7 +4,10 @@ # Import modules from flask import Flask, request -from endpoints import api, representations, exceptions, request_parser +try: + from .endpoints import api, representations, exceptions, request_parser +except ModuleNotFoundError: + from endpoints import api, representations, exceptions, request_parser import logging from logging import handlers import time diff --git a/setup.py b/setup.py index e383154..90f7440 100644 --- a/setup.py +++ b/setup.py @@ -55,6 +55,7 @@ "mysql-connector-python", "requests", "dicttoxml", + "locustio", "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs", "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@v1.0.1#egg=VariantFormatter", diff --git a/wsgi.py b/wsgi.py index fdecaad..dac45b1 100644 --- a/wsgi.py +++ b/wsgi.py @@ -1,7 +1,7 @@ """mod_wsgi gateway wsgi file """ -from rest_variantValidator.app import application as application +from rest_VariantValidator.app import application as application if __name__ == '__main__': from configparser import ConfigParser from VariantValidator.settings import CONFIG_DIR @@ -13,4 +13,4 @@ else: application.debug = False application.config['PROPAGATE_EXCEPTIONS'] = False - application.run(host="0.0.0.0", port=5000) + application.run(host="127.0.0.1", port=5000) From 50aa25502e933e84edcadc985f9cd558bc1a5909 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 4 Dec 2019 13:31:30 +0000 Subject: [PATCH 033/218] remove locustio from setup as it can cause problems --- REQUIREMENTS.txt | 1 - setup.py | 1 - 2 files changed, 2 deletions(-) diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index ec879a9..2b24da0 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -12,6 +12,5 @@ biotools>=0.3.0 mysql-connector-python requests dicttoxml -locustio diff --git a/setup.py b/setup.py index 90f7440..e383154 100644 --- a/setup.py +++ b/setup.py @@ -55,7 +55,6 @@ "mysql-connector-python", "requests", "dicttoxml", - "locustio", "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs", "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@v1.0.1#egg=VariantFormatter", From 7d95675b6f68a23e4ac7159741dee016145f70ab Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 4 Dec 2019 15:57:52 +0000 Subject: [PATCH 034/218] Switch logging on and off dependant on VV config mode --- rest_VariantValidator/app.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 22dbb0d..1d20f41 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -8,25 +8,34 @@ from .endpoints import api, representations, exceptions, request_parser except ModuleNotFoundError: from endpoints import api, representations, exceptions, request_parser -import logging from logging import handlers import time +import logging.config +from configparser import ConfigParser +from VariantValidator import settings as vv_settings +import warnings +# Change settings based on config +config = ConfigParser() +config.read(vv_settings.CONFIG_DIR) """ Logging """ -logger = logging.getLogger('rest_VariantValidator') -# We are setting 2 types of logging. To screen at the level DEBUG -logger.setLevel(logging.INFO) - -# We will also log to a file -# Log with a rotating file-handler. This sets the maximum size of the log to 0.5Mb and allows two additional logs -# The logs are then deleted and replaced in rotation -logHandler = handlers.RotatingFileHandler('rest_VariantValidator.log', maxBytes=500000, backupCount=2) -# We want to minimise the amount of information we log to capturing bugs -logHandler.setLevel(logging.ERROR) -logger.addHandler(logHandler) +if config['logging'].getboolean('log') is True: + logger = logging.getLogger('rest_VariantValidator') + # We are setting 2 types of logging. To screen at the level DEBUG + logger.setLevel(logging.INFO) + + # We will also log to a file + # Log with a rotating file-handler. This sets the maximum size of the log to 0.5Mb and allows two additional logs + # The logs are then deleted and replaced in rotation + logHandler = handlers.RotatingFileHandler('rest_VariantValidator.log', maxBytes=500000, backupCount=2) + # We want to minimise the amount of information we log to capturing bugs + logHandler.setLevel(logging.ERROR) + logger.addHandler(logHandler) +else: + pass """ From 0722961ce18b2e6c6ce2535c3537839214ab3099 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 4 Dec 2019 15:58:16 +0000 Subject: [PATCH 035/218] Switch logging on and off dependant on VV config mode b --- rest_VariantValidator/app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 1d20f41..7aaadf7 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -13,7 +13,6 @@ import logging.config from configparser import ConfigParser from VariantValidator import settings as vv_settings -import warnings # Change settings based on config config = ConfigParser() From d9bc455d99bcfc8cabb3ab720c62993c0b90a5dc Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 4 Dec 2019 16:40:42 +0000 Subject: [PATCH 036/218] Set log levels via VV config --- rest_VariantValidator/app.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 7aaadf7..45297e1 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -24,18 +24,19 @@ if config['logging'].getboolean('log') is True: logger = logging.getLogger('rest_VariantValidator') # We are setting 2 types of logging. To screen at the level DEBUG - logger.setLevel(logging.INFO) + console_level = config['logging']['console'].upper() + log_console_level = logging.getLevelName(console_level) + logger.setLevel(log_console_level) # We will also log to a file # Log with a rotating file-handler. This sets the maximum size of the log to 0.5Mb and allows two additional logs # The logs are then deleted and replaced in rotation logHandler = handlers.RotatingFileHandler('rest_VariantValidator.log', maxBytes=500000, backupCount=2) # We want to minimise the amount of information we log to capturing bugs - logHandler.setLevel(logging.ERROR) + file_level = config['logging']['file'].upper() + log_file_level = logging.getLevelName(file_level) + logHandler.setLevel(log_file_level) logger.addHandler(logHandler) -else: - pass - """ Create a parser object locally From cc8cb54a2f9c8bbe8f7143387f0970649017b363 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Fri, 6 Dec 2019 10:58:59 +0000 Subject: [PATCH 037/218] fix internal server and not found errors and add Reverse proxy access code for swagger UI. Needs testing --- rest_VariantValidator/app.py | 9 ++- rest_VariantValidator/endpoints/__init__.py | 24 +++++-- rest_VariantValidator/endpoints/hello.py | 68 -------------------- rest_VariantValidator/endpoints/name.py | 70 --------------------- 4 files changed, 24 insertions(+), 147 deletions(-) delete mode 100644 rest_VariantValidator/endpoints/hello.py delete mode 100644 rest_VariantValidator/endpoints/name.py diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 45297e1..3ad928b 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -115,7 +115,10 @@ def remote_connection_error_handler(e): @application.errorhandler(404) -def not_found_error_handler(): +def not_found_error_handler(e): + # Add the Exception to the log ensuring that exc_info is True so that a traceback is also logged + log_exception('NotFoundError') + # Collect Arguments args = parser.parse_args() if args['content-type'] != 'application/xml': @@ -129,9 +132,9 @@ def not_found_error_handler(): @application.errorhandler(500) -def default_error_handler(): +def default_error_handler(e): # Add the Exception to the log ensuring that exc_info is True so that a traceback is also logged - log_exception('RemoteConnectionError') + log_exception('InternalServerError') # Collect Arguments args = parser.parse_args() diff --git a/rest_VariantValidator/endpoints/__init__.py b/rest_VariantValidator/endpoints/__init__.py index f67336a..44eab4b 100644 --- a/rest_VariantValidator/endpoints/__init__.py +++ b/rest_VariantValidator/endpoints/__init__.py @@ -2,18 +2,31 @@ import VariantValidator import VariantFormatter from flask_restplus import Api -# from .hello import api as ns_hello -# from .name import api as ns_name +from flask import url_for from .variantvalidator_endpoints import api as ns_vv from .variantformatter_endpoints import api as ns_vf # Obtain VariantValidator related metadata vval = VariantValidator.Validator() -config_dict = vval.my_config() +config_dict = vval.my_config() + + +# Override standard specs_url to allow reverse-proxy access through mod_wsgi +class CustomAPI(Api): + @property + def specs_url(self): + """ + The Swagger specifications absolute url (ie. `swagger.json`) + + This method returns the path relative to the APP required for reverse proxy access + + :rtype: str + """ + return url_for(self.endpoint('specs'), _external=False) # Define the API as api -api = Api(version=rest_VariantValidator.__version__, +api = CustomAPI(version=rest_VariantValidator.__version__, title="rest_VariantValidator", description="## By continuing to use this service you agree to our terms and conditions of Use\n" "- [Terms and Conditions](https://github.com/openvar/variantValidator/blob" @@ -31,8 +44,7 @@ + config_dict['seqrepo_db'].split('/')[-1] ) -# api.add_namespace(ns_hello) -# api.add_namespace(ns_name) +# Add the namespaces to the API api.add_namespace(ns_vv) api.add_namespace(ns_vf) diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py deleted file mode 100644 index db10604..0000000 --- a/rest_VariantValidator/endpoints/hello.py +++ /dev/null @@ -1,68 +0,0 @@ -from flask_restplus import Namespace, Resource -from . import request_parser -from . import representations - - -""" -Create a parser object locally -""" -parser = request_parser.parser - - -""" -The assignment of api changes -""" - -api = Namespace('hello', description='Simple API that returns a greeting') - -""" -We also need to re-assign the route ans other decorated functions to api -""" - - -@api.route("/") -class HelloClass(Resource): - - # Add documentation about the parser - @api.expect(parser, validate=True) - def get(self): - - # Collect Arguments - args = parser.parse_args() - - # Overrides the default response route so that the standard HTML URL can return any specified format - if args['content-type'] == 'application/json': - # example: http://127.0.0.1:5000/name/name/bob?content-type=application/json - return representations.application_json({ - "greeting": "Hello World" - }, - 200, None) - # example: http://127.0.0.1:5000/name/name/bob?content-type=application/xml - elif args['content-type'] == 'application/xml': - return representations.xml({ - "greeting": "Hello World" - }, - 200, None) - else: - # Return the api default output - return { - "greeting": "Hello World" - } - - -# -# Copyright (C) 2019 VariantValidator Contributors -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program 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 Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# diff --git a/rest_VariantValidator/endpoints/name.py b/rest_VariantValidator/endpoints/name.py deleted file mode 100644 index e4d4de5..0000000 --- a/rest_VariantValidator/endpoints/name.py +++ /dev/null @@ -1,70 +0,0 @@ -from flask_restplus import Namespace, Resource -from . import request_parser -from . import representations - - -""" -Create a parser object locally -""" -parser = request_parser.parser - - -""" -The assignment of api changes -""" - -api = Namespace('name', description='Return a name provided by the user') - - -""" -We also need to re-assign the route ans other decorated functions to api -""" - - -@api.route("/") -@api.param("name", "Enter name") -class NameClass(Resource): - - # Add documentation about the parser - @api.expect(parser, validate=True) - def get(self, name): - - # Collect Arguments - args = parser.parse_args() - - # Overrides the default response route so that the standard HTML URL can return any specified format - if args['content-type'] == 'application/json': - # example: http://127.0.0.1:5000/name/name/bob?content-type=application/json - return representations.application_json({ - "My name is": name - }, - 200, None) - # example: http://127.0.0.1:5000/name/name/bob?content-type=application/xml - elif args['content-type'] == 'application/xml': - return representations.xml({ - "My name is": name - }, - 200, None) - else: - # Return the api default output - return { - "My name is": name - } - - -# -# Copyright (C) 2019 VariantValidator Contributors -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program 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 Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# From d8424995a5407223a3607f3bec5792cacab9a5ae Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 11 Dec 2019 16:21:35 +0000 Subject: [PATCH 038/218] Ready for tag --- REQUIREMENTS.txt | 2 +- locust/__pycache__/locust.cpython-36.pyc | Bin 1585 -> 0 bytes locust/locust.py | 6 +++++- setup.py | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) delete mode 100644 locust/__pycache__/locust.cpython-36.pyc diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index 2b24da0..3e2e44e 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -1,5 +1,5 @@ git+https://github.com/openvar/vv_hgvs.git@master#egg=vvhgvs -git+https://github.com/openvar/variantValidator@master#egg=VariantValidator +git+https://github.com/openvar/variantValidator@v1.0.3#egg=VariantValidator git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter flask flask-restplus diff --git a/locust/__pycache__/locust.cpython-36.pyc b/locust/__pycache__/locust.cpython-36.pyc deleted file mode 100644 index 863824207610e86dcbfd38ea50c47bf12c670c07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1585 zcma)6UymC#5V!YVHn}@mXsIgT!S{e5Rj&)}1ES*3!BJ5JicYXaVT^ z(9I7ZIN?;1nx?e&Qm^(?zYfxX;=CurH2jfpp9e1p4}`b$(+I{Pk6;`L3gZso&2JTsj+S6I5*FZglz*0O4}IYskzXXwf} zC#NeXrx#AdI;LT>(vDfqblz~)$T>4Yioy!c)_d4Us~QYsY3zpkyPNJ$L?iB6aA68v zPAvN$-f;^v^LnV{r3fy>Kv{909gl?(EZ4#v!Oj$`m9cCr06$1{Y9L9glr*>5aB5)@ zE!~l4rIc(4dpQ@}c|7-kvH}oy4T;2w1L%fM3zNvOVuQmQ4N${jCp28*tCn=GX%Gd z3R%LY);|%p@3#RkF-o@_F?Ht3(w}PiC(%2Q@ynkRtR|C8s?nFp;k+2C{?({j}FH7zF6$$QvL_9p^hPm zbbIY%1#3{j<f2fN+ccN!l}?s%Rb-jQmrY+maSg@m44j5( zXyoEu=#G@Q8_=tfn=4@MqG2)bLpL9Qc=aj?KMh34DZEP_ygxpaaL{f&5kpg2f$3`` z@*Dwy#PxOf>-SOIK!I_gM&TL38r4W~+nwd5&1_kVx2Ad%Z1$iVJXTBts&S?tx6!KD lG~PFjxvmb5J{?;wm@|Hx-!}egh2uAvufyRcjjvq&=pV4+qagqQ diff --git a/locust/locust.py b/locust/locust.py index 4e5553d..bfa2f38 100644 --- a/locust/locust.py +++ b/locust/locust.py @@ -17,6 +17,10 @@ # Run locust against app on a server +$ locust -f locust/locust.py + +Perform test at http://127.0.0.1:8089 (docs at https://docs.locust.io/en/latest/quickstart.html) + """ class UserBehavior(TaskSet): @@ -49,4 +53,4 @@ def profile(self): class WebsiteUser(HttpLocust): task_set = UserBehavior - wait_time = between(0, 10) # seconds + wait_time = between(1, 10) # seconds diff --git a/setup.py b/setup.py index e383154..d28985e 100644 --- a/setup.py +++ b/setup.py @@ -56,7 +56,7 @@ "requests", "dicttoxml", "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs", - "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", + "VariantValidator @ git+https://github.com/openvar/variantValidator.git@v1.0.3#egg=VariantValidator", "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@v1.0.1#egg=VariantFormatter", "biocommons.seqrepo>=0.5.1", ], From b3a02f40faaf7f80af0abd5185901213b9f7dd84 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 4 Feb 2020 11:01:48 +0000 Subject: [PATCH 039/218] add hello function --- REQUIREMENTS.txt | 2 - rest_VariantValidator/endpoints/__init__.py | 2 + rest_VariantValidator/endpoints/hello.py | 68 +++++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 rest_VariantValidator/endpoints/hello.py diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index 3e2e44e..083547e 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -12,5 +12,3 @@ biotools>=0.3.0 mysql-connector-python requests dicttoxml - - diff --git a/rest_VariantValidator/endpoints/__init__.py b/rest_VariantValidator/endpoints/__init__.py index 44eab4b..d3b4cb0 100644 --- a/rest_VariantValidator/endpoints/__init__.py +++ b/rest_VariantValidator/endpoints/__init__.py @@ -5,6 +5,7 @@ from flask import url_for from .variantvalidator_endpoints import api as ns_vv from .variantformatter_endpoints import api as ns_vf +from .hello import api as ns_hello # Obtain VariantValidator related metadata vval = VariantValidator.Validator() @@ -47,6 +48,7 @@ def specs_url(self): # Add the namespaces to the API api.add_namespace(ns_vv) api.add_namespace(ns_vf) +api.add_namespace(ns_hello) # diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py new file mode 100644 index 0000000..0473048 --- /dev/null +++ b/rest_VariantValidator/endpoints/hello.py @@ -0,0 +1,68 @@ +from flask_restplus import Namespace, Resource +from . import request_parser +from . import representations + + +""" +Create a parser object locally +""" +parser = request_parser.parser + + +""" +The assignment of api changes +""" + +api = Namespace('hello', description='Endpoint to check services are "alive"') + +""" +We also need to re-assign the route ans other decorated functions to api +""" + + +@api.route("/") +class HelloClass(Resource): + + # Add documentation about the parser + @api.expect(parser, validate=True) + def get(self): + + # Collect Arguments + args = parser.parse_args() + + # Overrides the default response route so that the standard HTML URL can return any specified format + if args['content-type'] == 'application/json': + # example: http://127.0.0.1:5000/name/name/bob?content-type=application/json + return representations.application_json({ + "status": "hello_world" + }, + 200, None) + # example: http://127.0.0.1:5000/name/name/bob?content-type=application/xml + elif args['content-type'] == 'application/xml': + return representations.xml({ + "status": "hello_world" + }, + 200, None) + else: + # Return the api default output + return { + "status": "hello_world" + } + + +# +# Copyright (C) 2019 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# From 40e1d961ffa063ddd8da9829d0f16e3fa5887f5b Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 13 Feb 2020 08:45:18 +0000 Subject: [PATCH 040/218] add LOVD endpoint ready for testing --- rest_VariantValidator/endpoints/__init__.py | 2 + .../endpoints/lovd_endpoints.py | 101 ++++++++++++++++++ .../endpoints/variantformatter_endpoints.py | 7 +- .../endpoints/variantvalidator_endpoints.py | 3 - setup.py | 4 +- 5 files changed, 107 insertions(+), 10 deletions(-) create mode 100644 rest_VariantValidator/endpoints/lovd_endpoints.py diff --git a/rest_VariantValidator/endpoints/__init__.py b/rest_VariantValidator/endpoints/__init__.py index d3b4cb0..e05f42a 100644 --- a/rest_VariantValidator/endpoints/__init__.py +++ b/rest_VariantValidator/endpoints/__init__.py @@ -5,6 +5,7 @@ from flask import url_for from .variantvalidator_endpoints import api as ns_vv from .variantformatter_endpoints import api as ns_vf +from .lovd_endpoints import api as ns_lovd from .hello import api as ns_hello # Obtain VariantValidator related metadata @@ -48,6 +49,7 @@ def specs_url(self): # Add the namespaces to the API api.add_namespace(ns_vv) api.add_namespace(ns_vf) +api.add_namespace(ns_lovd) api.add_namespace(ns_hello) diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py new file mode 100644 index 0000000..b4c844a --- /dev/null +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -0,0 +1,101 @@ +# Import modules +from flask_restplus import Namespace, Resource +from . import request_parser +from . import representations + +# Import variantFormatter +import VariantFormatter +import VariantFormatter.simpleVariantFormatter +import VariantValidator +vval = VariantValidator.Validator() + +""" +Create a parser object locally +""" +parser = request_parser.parser + +api = Namespace('LOVD', description='LOVD API Endpoints') +@api.route("/lovd////" + "//") +@api.param("variant_description", "***Genomic HGVS***\n" + "> - NC_000017.10:g.48275363C>A\n" + "\n***Pseudo-VCF***\n" + "> - 17-50198002-C-A\n" + "> - 17:50198002:C:A\n" + "\n> *Notes*\n" + "> - *pVCF, multiple comma separated ALTs are supported*\n " + "> - *Multiple variants can be submitted, separated by the pipe '|' character*\n" + "> - *Recommended maximum is 10 variants per submission*") +@api.param("transcript_model", "***Accepted:***\n" + "> - refseq (return data for RefSeq transcript models)\n" + "> - all (currently refseq only)") +@api.param("select_transcripts", "***Return all possible transcripts***\n" + "> None or all\n" + "\n***Single***\n" + "> NM_000093.4\n" + "\n***Multiple***\n" + "> NM_000093.4|NM_001278074.1|NM_000093.3") +@api.param("genome_build", "***Accepted:***\n" + "> - GRCh37\n" + "> - GRCh38\n" + "> - hg19\n" + "> - hg38\n") +@api.param("checkonly", "***Accepted:***\n" + "> - True (return ONLY the genomic variant descriptions and not transcript and protein" + " descriptions)\n" + "> - False") +@api.param("liftover", "***Accepted***\n" + "> - True - (liftover to all genomic loci)\n" + "> - primary - (lift to primary assembly only)\n" + "> - False") +class VariantValidatorClass(Resource): + # Add documentation about the parser + @api.expect(parser, validate=True) + def get(self, genome_build, variant_description, transcript_model, select_transcripts, checkonly, liftover): + if transcript_model == 'None' or transcript_model == 'none': + transcript_model = None + if select_transcripts == 'None' or select_transcripts == 'none': + select_transcripts = None + if checkonly == 'False' or checkonly == 'false': + checkonly = False + if checkonly == 'True' or checkonly == 'true': + checkonly = True + if liftover == 'True' or liftover == 'true': + liftover = True + if liftover == 'False' or liftover == 'false': + liftover = False + + content = VariantFormatter.simpleVariantFormatter.format(variant_description, genome_build, transcript_model, + select_transcripts, checkonly, liftover) + + # Collect Arguments + args = parser.parse_args() + + # Overrides the default response route so that the standard HTML URL can return any specified format + if args['content-type'] == 'application/json': + # example: http://127.0.0.1:5000.....bob?content-type=application/json + return representations.application_json(content, 200, None) + # example: http://127.0.0.1:5000.....?content-type=application/xml + elif args['content-type'] == 'application/xml': + return representations.xml(content, 200, None) + else: + # Return the api default output + return content + + +# +# Copyright (C) 2019 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index fa5387d..6e03b33 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -2,9 +2,6 @@ from flask_restplus import Namespace, Resource from . import request_parser from . import representations -# import requests -# from requests.exceptions import ConnectionError -# from . import exceptions # Import variantFormatter import VariantFormatter @@ -29,9 +26,9 @@ "> - *Recommended maximum is 10 variants per submission*") @api.param("transcript_model", "***Accepted:***\n" "> - refseq (return data for RefSeq transcript models)\n" - "> - all (currently refseq only") + "> - all (currently refseq only)") @api.param("select_transcripts", "***Return all possible transcripts***\n" - "> None\n" + "> None or all\n" "\n***Single***\n" "> NM_000093.4\n" "\n***Multiple***\n" diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 92820a8..d19e594 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -2,9 +2,6 @@ from flask_restplus import Namespace, Resource from . import request_parser from . import representations -# import requests -# from requests.exceptions import ConnectionError -# from . import exceptions # Import VariantValidator code import VariantValidator diff --git a/setup.py b/setup.py index d28985e..1485d92 100644 --- a/setup.py +++ b/setup.py @@ -56,8 +56,8 @@ "requests", "dicttoxml", "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs", - "VariantValidator @ git+https://github.com/openvar/variantValidator.git@v1.0.3#egg=VariantValidator", - "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@v1.0.1#egg=VariantFormatter", + "VariantValidator @ git+https://github.com/openvar/variantValidator.git@features#egg=VariantValidator", + "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@requests#egg=VariantFormatter", "biocommons.seqrepo>=0.5.1", ], setup_requires=[ From f418c5f8fff6c32e956c84f932e1ccd2d70d1ca7 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 13 Feb 2020 16:31:17 +0000 Subject: [PATCH 041/218] update Checkonly for lovd --- rest_VariantValidator/endpoints/lovd_endpoints.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index b4c844a..e1f36b4 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -43,7 +43,8 @@ @api.param("checkonly", "***Accepted:***\n" "> - True (return ONLY the genomic variant descriptions and not transcript and protein" " descriptions)\n" - "> - False") + "> - False\n" + "> - tx (Stop at transcript level, exclude protein") @api.param("liftover", "***Accepted***\n" "> - True - (liftover to all genomic loci)\n" "> - primary - (lift to primary assembly only)\n" From cd3a96ab510f954fedc0d35488fb944a92ec5837 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 16 Apr 2020 16:26:42 +0100 Subject: [PATCH 042/218] Tweaks that dont affect processing bit close vv issue 149 --- REQUIREMENTS.txt | 4 ++-- configuration/docker.ini | 2 +- docs/DOCKER.md | 22 +++++++++++++++++++ .../endpoints/lovd_endpoints.py | 2 +- setup.py | 4 ++-- 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index 083547e..e7eec69 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -1,5 +1,5 @@ -git+https://github.com/openvar/vv_hgvs.git@master#egg=vvhgvs -git+https://github.com/openvar/variantValidator@v1.0.3#egg=VariantValidator +git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs +git+https://github.com/openvar/variantValidator@master#egg=VariantValidator git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter flask flask-restplus diff --git a/configuration/docker.ini b/configuration/docker.ini index c49617d..3462c5c 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -22,7 +22,7 @@ console = DEBUG file = ERROR [Entrez] -email = 'YOUR_EMAIL_ADDRESS' +email = admin@variantvalidator.org api_key = 'YOUR_API_KEY' diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 87787be..aaa23f7 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -28,6 +28,28 @@ Edit the file configuration/docker.ini You will need to provide an email address and an [Entrez API key](https://ncbiinsights.ncbi.nlm.nih.gov/2017/11/02/new-api-keys-for-the-e-utilities/) +*Note: If you have MySQl and or Postgres databases already running, you will need to alter the ports used in the +docker-comose.yml file. The relevant section is shown here* +```yml +services: + vdb: + build: + context: . + dockerfile: vdb_docker.df + ports: + - "3306:3306" + expose: + - "3306" + uta: + build: + context: . + dockerfile: uta_docker.df + ports: + - "5432:5432" + expose: + - "5432" +``` + *Note: configuration can be updated (see below for details)* ## Install and build diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index e1f36b4..7eb3db3 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -44,7 +44,7 @@ "> - True (return ONLY the genomic variant descriptions and not transcript and protein" " descriptions)\n" "> - False\n" - "> - tx (Stop at transcript level, exclude protein") + "> - tx (Stop at transcript level, exclude protein)") @api.param("liftover", "***Accepted***\n" "> - True - (liftover to all genomic loci)\n" "> - primary - (lift to primary assembly only)\n" diff --git a/setup.py b/setup.py index 1485d92..2151dda 100644 --- a/setup.py +++ b/setup.py @@ -56,8 +56,8 @@ "requests", "dicttoxml", "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs", - "VariantValidator @ git+https://github.com/openvar/variantValidator.git@features#egg=VariantValidator", - "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@requests#egg=VariantFormatter", + "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", + "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@master#egg=VariantFormatter", "biocommons.seqrepo>=0.5.1", ], setup_requires=[ From 129ee7c8ced7b863df76b9370d1f66cd48d0390a Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 14 May 2020 10:56:07 +0100 Subject: [PATCH 043/218] Change installation workflow, fix Werkzeug version issue and add bin from VariantValidator to provide VV functionality. See docker docs --- Dockerfile | 2 +- REQUIREMENTS.txt | 4 + bin/update_vdb.py | 33 + bin/variant_validator.py | 71 +++ bin/vv_configure.py | 78 +++ ...variantValidator_tutorial-checkpoint.ipynb | 586 ++++++++++++++++++ docs/DOCKER.md | 66 +- docs/images/curl.png | Bin 0 -> 223122 bytes docs/images/ns_and_ep.png | Bin 0 -> 337392 bytes docs/images/query_builder.png | Bin 0 -> 162305 bytes docs/images/response.png | Bin 0 -> 249575 bytes docs/images/urls.png | Bin 0 -> 73697 bytes docs/images/web.png | Bin 0 -> 193994 bytes docs/rest_variantValidator_tutorial.ipynb | 586 ++++++++++++++++++ rest_VariantValidator/app.py | 6 +- setup.py | 4 + vdb_docker.df | 2 +- 17 files changed, 1395 insertions(+), 43 deletions(-) create mode 100644 bin/update_vdb.py create mode 100644 bin/variant_validator.py create mode 100644 bin/vv_configure.py create mode 100644 docs/.ipynb_checkpoints/rest_variantValidator_tutorial-checkpoint.ipynb create mode 100644 docs/images/curl.png create mode 100644 docs/images/ns_and_ep.png create mode 100644 docs/images/query_builder.png create mode 100644 docs/images/response.png create mode 100644 docs/images/urls.png create mode 100644 docs/images/web.png create mode 100644 docs/rest_variantValidator_tutorial.ipynb diff --git a/Dockerfile b/Dockerfile index 65a36b7..74ced38 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,4 +24,4 @@ COPY configuration/docker.ini /root/.variantvalidator # Start the application with gunicorn # CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_VariantValidator/ -CMD gunicorn -b 0.0.0.0:8000 app --threads=5 --worker-class=gthread --chdir ./rest_VariantValidator/ +# CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --chdir ./rest_VariantValidator/ \ No newline at end of file diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index e7eec69..cfa4bae 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -12,3 +12,7 @@ biotools>=0.3.0 mysql-connector-python requests dicttoxml +notebook +jupyter_contrib_nbextensions +jupyter_nbextensions_configurator +Werkzeug==0.16.1 \ No newline at end of file diff --git a/bin/update_vdb.py b/bin/update_vdb.py new file mode 100644 index 0000000..6453bc8 --- /dev/null +++ b/bin/update_vdb.py @@ -0,0 +1,33 @@ +#! /usr/bin/env python + +from VariantValidator import update_vv_db +import argparse + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--delete', '-d', action='store_true', help='Delete the contents of the current database ' + 'before updating') + + args = parser.parse_args() + if args.delete: + print("Deleting current database contents") + update_vv_db.delete() + + update_vv_db.update() + +# +# Copyright (C) 2019 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/bin/variant_validator.py b/bin/variant_validator.py new file mode 100644 index 0000000..88d3531 --- /dev/null +++ b/bin/variant_validator.py @@ -0,0 +1,71 @@ +#! /usr/bin/env python + +import argparse +import sys +from VariantValidator import Validator + + +def output_results(valoutput, outformat, with_meta): + if outformat == 'dict': + return str(valoutput.format_as_dict(with_meta=with_meta)) + elif outformat == 'json': + return str(valoutput.format_as_json(with_meta=with_meta)) + else: + # table format + table = valoutput.format_as_table(with_meta=with_meta) + newtable = [] + for row in table: + if isinstance(row, list): + newrow = '\t'.join(row) + else: + newrow = str(row) + newtable.append(newrow) + return '\n'.join(newtable) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('-v', '--variant', required=True, nargs='+', help="Variant(s) to validate") + parser.add_argument('-g', '--genome', nargs='?', default='GRCh37', choices=['GRCh37', 'GRCh38', 'hg19', 'hg38'], + help="Genome assembly (default: %(default)s)") + parser.add_argument('-t', '--transcripts', nargs='?', default='all', + help='Transcripts to output results for (default: %(default)s)') + parser.add_argument('-s', '--submission', choices=['individual', 'batch'], default='individual', + help='Submit variants individually or as a single batch validation (default: %(default)s)') + parser.add_argument('-f', '--output_format', choices=['dict', 'table', 'json'], default='dict', + help='Output validations as a list or as a dictionary (default: %(default)s)') + parser.add_argument('-o', '--output', type=argparse.FileType('w'), default='-', + help='Specifies the output file (default: stdout)') + parser.add_argument('-m', '--meta', action='store_true', default=False, + help='Also output metadata (default: %(default)s)') + + args = parser.parse_args() + + validator = Validator() + + if args.submission == 'individual': + for variant in args.variant: + output = validator.validate(variant, args.genome, args.transcripts) + args.output.write(output_results(output, args.output_format, args.meta) + '\n') + else: + batch = '|'.join(args.variant) + sys.stderr.write("Submitting batch query: %s\n" % batch) + output = validator.validate(batch, args.genome, args.transcripts) + args.output.write(output_results(output, args.output_format, args.meta) + '\n') + +# +# Copyright (C) 2019 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/bin/vv_configure.py b/bin/vv_configure.py new file mode 100644 index 0000000..f2834a4 --- /dev/null +++ b/bin/vv_configure.py @@ -0,0 +1,78 @@ +#! /usr/bin/env python +from __future__ import print_function +import argparse +import os +import configparser +import pkgutil + + +def find_root(): + package = pkgutil.get_loader('VariantValidator') + path = os.path.dirname(os.path.dirname(package.get_filename())) + return path + + +def read_settings(): + root = find_root() + settings_file = os.path.join(root, 'VariantValidator', 'settings.py') + with open(settings_file) as f: + values = {} + exec(f.read(), {}, values) + return values + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('-s', '--section', choices=['mysql', 'seqrepo', 'postgres', 'logging', 'EntrezID', 'liftover'], + nargs='?', help='Optional choice of section to configure') + + args = parser.parse_args() + + settings = read_settings() + newfile = False + + if os.path.exists(settings['CONFIG_DIR']): + readfile = settings['CONFIG_DIR'] + else: + root = find_root() + readfile = os.path.join(root, 'configuration', 'default.ini') + newfile = True + + config = configparser.ConfigParser() + config.read(readfile) + + values_changed = False + + for section in config.sections(): + if not newfile and args.section and args.section != section: + continue + print('Section:', section) + for name, value in config.items(section): + print("{} [{}]: ".format(name, value), end="") + newval = input() + if newval != '': + config.set(section, name, newval.strip()) + values_changed = True + + print() + + if newfile or values_changed: + with open(settings['CONFIG_DIR'], 'w') as fh: + config.write(fh) + +# +# Copyright (C) 2019 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/docs/.ipynb_checkpoints/rest_variantValidator_tutorial-checkpoint.ipynb b/docs/.ipynb_checkpoints/rest_variantValidator_tutorial-checkpoint.ipynb new file mode 100644 index 0000000..f3972ee --- /dev/null +++ b/docs/.ipynb_checkpoints/rest_variantValidator_tutorial-checkpoint.ipynb @@ -0,0 +1,586 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + "
Lecturer - Healthcare Sciences
\n", + "
(Clinical Bioinformatics)
\n", + "
The University of Manchester
\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Introduction to SPRINT 1 notebook B\n", + "****\n", + "\n", + "## Overview\n", + "This introductory notebook, and subsequent SPRINT_introduction notebooks will continue to introduce the concept of retrieving data from Application Programming Interfaces [API](https://en.wikipedia.org/wiki/Application_programming_interface) which are web-hosted ([web API](https://en.wikipedia.org/wiki/Web_API))\n", + "\n", + "Many bioinformatics tools and data repositories can be accessed using web APIs including NCBI and Ensembl. \n", + "\n", + "Although we cannot hope to demonstrate how each an every useful bioinformatics web API works during this 10 week course, we will give you a broad overview of the tools we use to request data from these resources and the tools we use to make-sense of the data that are returned " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Table of Contents

\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Notebook B, section 1: [Introduction to JSON](#json)\n", + "- [What is JSON](#watsit)\n", + "- [The JSON format](#jform)\n", + "- [Reading and writing JSON using Python](#jpy)\n", + "- [Section 1 Summary](#s1s)\n", + "\n", + "#### Notebook B, section 2: [Introduction to REST API](#restapi)\n", + "- [The REST framework](#rest)\n", + "- [Building a simple API: Part A - Build a simple REST API](#builder_a)\n", + "- [Building a simple API: Part B - Request data using Python](#builder_b)\n", + "- [Building a simple API: Part C - Create a new VariantValidator API](#builder_c)\n", + "- [Section 2 Summary and Assignment](#s2s)\n", + "- [Marked assessment](#practical)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
Learning Objective: Create functioning, standards compliant and well documented Python code
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "

Introduction to the VariantValidator REST API

\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "Image by Peter Causey-Freeman" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "

The REST framework

\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## [An Introduction to APIs](https://restful.io/an-introduction-to-api-s-cee90581ca1b) \n", + "- [Gonzalo Vázquez](https://restful.io/@gonzalovazquez)\n", + "- [Restful Web](https://restful.io/) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "

Using the VariantValidator REST API

\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### API structure\n", + "\n", + "#### Framework\n", + "The VariantValidator REST API is built on the [Flask](https://en.wikipedia.org/wiki/Flask_\\(web_framework) web framework. \n", + "\n", + "The REST components are built using [Flask-RESTPlus](https://flask-restplus.readthedocs.io/en/stable/)\n", + "\n", + "> Flask-RESTPlus is an extension for Flask that adds support for quickly building REST APIs. Flask-RESTPlus encourages best practices with minimal setup. \n", + "\n", + ">It provides a coherent collection of tools to describe your API and expose its documentation properly (using Swagger).\n", + "\n", + "#### Namespaces and Endpoints\n", + "\n", + "The VariantValidator REST API has several tool-sets. Each set is divided into separate namespaces. \n", + "\n", + "For example, the namespace \"hello\" is used to test whether our services are up-and running. The namespaces and endpoints are most easily demonstrated by looking at the Swagger documented API on [https://rest.variantvalidator.org/](https://rest.variantvalidator.org/).\n", + "\n", + "The namespaces are\n", + "- VariantValidator; Core [VariantValidator](https://github.com/openvar/variantValidator) Python library\n", + "- VariantFormatter; [VariantFormatter](https://github.com/openvar/variantFormatter/tree/develop) extension library\n", + "- LOVD; Adapted endpoint for LOVD specific access to our resourced\n", + "- hello; Simple handshake allowing external users to test whether services are alive before submission\n", + "\n", + "Swagger documentation displays the namespaces as follows" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![title](images/ns_and_ep.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each namespace contains endpoints which access specific functions of the VariantValidator libraries. For example the VariantValidator namespace has 3 endpoints\n", + "- gene2transcripts\n", + "- hgvs2reference\n", + "- variantvalidator\n", + "\n", + "### Building Queries\n", + "\n", + "In this interactive mode, the endpoint can be clicked allowing us to access a human-friendly query builder\n", + "\n", + "![title](images/query_builder.png)\n", + "\n", + "Currently the data can be returned in 2 different formats, JSON and XML. These are selected using the `Select the response format` drop-down menu. \n", + "\n", + "For this example I have selected the simple `gene2transcripts` endpoint which searches for all transcripts associated with a particular gene. The documentation tells us that me must input either a HGNC compliant gene symbol or a RefSeq transcript ID. However, this documentation will be improved because the tool also accepts RefSeq transcript IDs without version numbers, LRG IDs (*e.g.* LRG_1) and LRG transcript IDs (*e.g.* LRG_1t1). \n", + "\n", + "Once all the required fields are populated we can execute the query" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The API response\n", + "\n", + "Let's take a look at the response which Swagger has parsed into a user-friendly web page.\n", + "\n", + "![title](images/response.png)\n", + "\n", + "#### Server responses\n", + "1. [Response code](https://developer.amazon.com/docs/amazon-drive/ad-restful-api-response-codes.html) 200\n", + "2. The Response headers provide additional response metadata, *e.g.* the content-type and the time of the response\n", + "3. Response body, *i.e.* the JSON or XML the endpoint returns\n", + "\n", + "### The API query URLS\n", + "\n", + "Swagger also displays queries that can be used to trigger the response in a standard format, *i.e.* a non-interactive mode.\n", + "\n", + "![title](images/urls.png)\n", + "\n", + "#### curl\n", + "\n", + "curl is generally used in terminals and programming\n", + "\n", + "In this screen shot I have used a terminal to request data directly from the VariantValidator API using the provided curl. I have piped this into `python -m json.tool` to provide a pretty JSON display.\n", + "\n", + "The full request is `curl -X GET \"https://rest.variantvalidator.org/VariantValidator/tools/gene2transcripts/COL1A1?content-type=application%2Fjson\" -H \"accept: application/json\" | python -m json.tool`\n", + "\n", + "![title](images/curl.png)\n", + "\n", + "#### web URL\n", + "The web URL can simply be pasted into a browser and in the next section we will use the web URL to recover data from the VariantValidator API using Python\n", + "\n", + "![title](images/web.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "

Request data using Python

\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The Requests module\n", + "[Requests](https://2.python-requests.org/en/master/)\n", + "> Requests: HTTP for Humans™\n", + "\n", + "> Requests is the only HTTP library for Python safe for human consumption\n", + "\n", + "***\n", + "Courtesy of the \"requests\" © 2019 Kenneth Reitz [Apache 2 License](https://www.apache.org/licenses/LICENSE-2.0)
\n", + "\n", + "OK, we have to take their word for it, but we are going to use requests because is's simple, easy to understand and is well maintained\n", + "\n", + "#### Method\n", + "\n", + "1. Install requests into your environment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "! pip install requests" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Import modules we will use" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import requests\n", + "import json" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Create a simple function that calls the API using responses\n", + "\n", + " - *Note: This function is in a format that can be expanded*" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "base_url = 'http://127.0.0.1:5000/'\n", + "def make_request(base_url, api_function):\n", + " # Tell the User the full URL of their call to the rest API\n", + " url = '%s%s' % (base_url, api_function)\n", + " print(\"Querying rest API with URL: \" + url)\n", + " \n", + " # Make the request and pass to a response object that the function returns\n", + " response = requests.get(url)\n", + " return response" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4. Make a request to our API using the function. We need to specify the base_url and the api_function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response = make_request(base_url, 'hello')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "5. Look at the response content\n", + " - response status code" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response.status_code" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " - response headers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response.headers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "5. Finally, extract the body which the requests.json() method formats into Python dictionary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "body = response.json()\n", + "body" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "

Building a simple API: Part C - Create a new VariantValidator API

\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## A bit basic isn't it?\n", + "\n", + "The simple `hello` API is a bit basic, but it does show you how an API works and we have also made requests to our API using Python.\n", + "\n", + "So what if we want to pass some data to the API?\n", + "\n", + "To `application/app_v2` I have added an additional **namespace** called name_space.\n", + "\n", + "I have also added a new API to our REST interface called name\n", + "\n", + "```python\n", + "name_space = application.namespace('name', description='Return a name provided by the user')\n", + "@name_space.route(\"\")\n", + "class NameClass(Resource):\n", + " def get(self, name):\n", + " return {\n", + " \"My name is\" : name\n", + " }\n", + "```\n", + "\n", + "To capture data submitted to the API, we tell the name_space.route to expect a **string** object\n", + "```python\n", + "@name_space.route(\"/\")\n", + "```\n", + "\n", + "and the NameClass Resource to expect the string object\n", + "```python\n", + "def get(self, name):\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "

Have a go

\n", + "\n", + "Activate `app_v2`\n", + "\n", + "```bash\n", + "$ python SPRINT/application/app_v2.py\n", + "```\n", + "\n", + "\n", + "### Exercise\n", + "\n", + "In a browser navigate to [http://127.0.0.1:5000/](http://127.0.0.1:5000/) and see whether you can figure out how to return your name using the API\n", + "\n", + "*Swagger is your friend here. It makes it very simple for a lay user to use an API*\n", + "\n", + "
\n", + "\n", + "### Exercise 2\n", + "\n", + "Now write a script that can make a call to the API and return the JSON that displays your name\n", + "\n", + "*Use the script above as a template. Remember, you may want to make a call to the hello API again, so keep the function flexible*\n", + "\n", + "Once the script is working, print out the response status, headings and JSON\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "

Section 2 Summary and Assignment

\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Summary\n", + "In section 2 of this notebook we have learned about the REST API framework. We have learned how to build a simple REST API of our own. We have briefly touched upon the concept of how Swagger documentation makes APIs accessible to mere humans\\*. We have also learned how to request and make sense of data returned by REST PAIs using the Python requests module\n", + "\n", + "\\**We will look at of Swagger in more detail in week 8 of this unit* " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "cell_style": "split" + }, + "source": [ + "\n", + "\n", + "### Over to you\n", + "\n", + "#### Aim of this exercise\n", + "The aim of this exercise is to keep you into the mindset of working together as a team. We will concentrate on aspects of working in an Agile fashion.\n", + "\n", + "#### Structure your team\n", + "Assign your team roles:\n", + "\n", + "1. **Project lead**\n", + " - Initiate the project on Git Issues (Note, there are two separate short projects here)\n", + " - Lead the group discussion in Git Issues and Slack\n", + " - Provide final feedback on the group's activities and close the issue\n", + " \n", + "\n", + "2. **Team members**\n", + " - Coders who will be responsible for writing the Python functions\n", + " - Testers who will be responsible for testing the code and providing feedback to the coders\n", + "\n", + "***We recommend ensuring that you most experienced coders work with your least experienced coders. Don't forget, this is a team assignment, if you can't figure out how to do something, ask your team on Slack!***" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "cell_style": "split" + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "cell_style": "split" + }, + "source": [ + "### Work-flow\n", + "\n", + "1. Group leader creates an issue on Git Issues\n", + "2. The coders will work together to write the module\n", + "3. The testers will review the final code and test the code. Feedback will be given to the coders within the Git issue\n", + "4. Once the coding is completed and tested, the project lead will summarise the key work-flow points and close the issue\n", + "\n", + "**Details about how the assignment will be marked can be found [here](LINK)**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "\n", + "### Team Assignment\n", + "\n", + "***Remember, you are working as a team. Make sure you assign tasks in an agile way***\n", + "\n", + "#### Coding Workflow\n", + "1. In `applications/app_v3.py` I have created a `vv_space` namespace and `VariantValidatorClass` resource (Endpoint). Your task is to replace all the sections of the module marked \\_\\_\\_\\_\\_ (5 underscores) with actual code. The namespace requires 3 variables.\n", + "\n", + "When you have finished filling in the blanks, the answers can be found in `app_v4.py`\n", + "\n", + "
\n", + "\n", + "***Refer to the existing [VariantValidator REST API](https://rest.variantvalidator.org/webservices/variantvalidator.html#!/variantvalidator/VariantValidator)***\n", + "\n", + "2. In `applications/app_v3.py` create a new namespace and Endpoint that incorporates and returns the data from the function you created in SPRINT_1_introduction_a. When you are creating the namespace route, add a field that allows the user to select whether or not the sequences your function returns are displayed.\n", + " \n", + "*Note: for non-coding transcripts some of these fields will need to return None*\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Concluding remarks\n", + "We will cover methods for reading and writing JSON data to-and-from files in week_6, but a key aspect of learning to program is learning to use the internet to find out how to do things. Google and stack overflow are your friends!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/DOCKER.md b/docs/DOCKER.md index aaa23f7..bdb9822 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -28,8 +28,10 @@ Edit the file configuration/docker.ini You will need to provide an email address and an [Entrez API key](https://ncbiinsights.ncbi.nlm.nih.gov/2017/11/02/new-api-keys-for-the-e-utilities/) -*Note: If you have MySQl and or Postgres databases already running, you will need to alter the ports used in the -docker-comose.yml file. The relevant section is shown here* +*Note: If you have MySQl and or Postgres databases already running, you may encounter a build error e.g. +"listen tcp 0.0.0.0:33060: bind: address already in use" In this case you will need to alter the ports used in the +docker-comose.yml file. +The relevant section is shown here* ```yml services: vdb: @@ -73,53 +75,37 @@ $ docker-compose up ctrl + c ``` -## Launch -You can then launch the docker containers and run them using +## Launch the rest_VariantValidator API +You run the API directly in the docker container directly via bash ```bash -$ docker-compose up +$ docker-compose run restvv bash ``` +Then start the REST services manually, bound to one of the following commands. Note, if you get an error saying +there is a conflict on for example port 8000, try starting with an alternate version of the commands provided +```bash +# port 8080 +$ gunicorn -b 0.0.0.0:8080 app --workers=3 --threads=5 --chdir ./rest_VariantValidator/ -Note: We do not recommend running this in the background as you need to see the logs and therefore when the databases -are ready to be used. +# port 5000 +$ gunicorn -b 0.0.0.0:5000 app --workers=3 --threads=5 --chdir ./rest_VariantValidator/ + +# port 8000 +$ gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --chdir ./rest_VariantValidator/ + ``` ## Access rest_variantValidator In a web browser navigate to -[http://0.0.0.0:8000/webservices/variantvalidator.html](http://0.0.0.0:8000/webservices/variantvalidator.html) +[http://0.0.0.0:8080](http://0.0.0.0:8080) +***Note: you may need to change :8080 to one of :5000 or :8000 depending on the activation command*** -## Stop the app +## Stop the app and exit the container `ctrl+c` -***To re-launch the app, go to Launch*** - -## Remove the containers -***Note: This step removes the container database. Effectively an uninstall. See Updating rest_variantValidator*** ```bash -$ docker-compose down -``` - -## Run -You can go into the container via bash to use -[VariantValidator](https://github.com/openvar/variantValidator/blob/master/docs/MANUAL.md) directly. - -```bash -$ docker-compose run restvv bash +$ exit ``` -and you can start the REST services manually, bound to one of the following ports -```bash -# port 8000 (default) -$ gunicorn -b 0.0.0.0:8000 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ - -# port 8000 -$ gunicorn -b 0.0.0.0:8080 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ - - -# port 5000 -$ gunicorn -b 0.0.0.0:5000 app --threads=5 --worker-class=gthread --chdir ./rest_variantValidator/ -``` - - Note, that each time one of these commands is run a new container is created. For more information on how to use docker-compose see their [documentation](https://docs.docker.com/compose/). @@ -134,6 +120,7 @@ The container hosts a full install of VariantValidator. VariantValidator can be run on the commandline from within the container. Instructions can be found in the VariantValidator [manual](https://github.com/openvar/variantValidator/blob/master/docs/MANUAL.md) +under sections **Database updates** and **Operation** ## Updating rest_variantValidator Update requires that the restvv container is deleted from your system. This is not achieved by removing the container @@ -158,4 +145,9 @@ $ docker-compose up --force-recreate ***If you choose this option, make sure you see the container restvv being re-created and all Python packages being reinstalled in the printed logs, otherwise the container may not actually be rebuilt and the contained modules may not - update*** \ No newline at end of file + update*** + + ## Removing the containers +```bash +$ docker-compose down +``` \ No newline at end of file diff --git a/docs/images/curl.png b/docs/images/curl.png new file mode 100644 index 0000000000000000000000000000000000000000..427ab6dbd62a3f9597dbd737357d6a779be23ef2 GIT binary patch literal 223122 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

vlAtot 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

S{QTr|3{-YAwTe>X;G4`j-y$S;S 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

U)UP3xD%*)kviEz4j4 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 z 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

PGXxHn9_m2 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


|c)ZRcKKo%e17o6MATz+YKzuc(H_ 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 L;!4yd_51$^dgS0k literal 0 HcmV?d00001 diff --git a/docs/images/response.png b/docs/images/response.png new file mode 100644 index 0000000000000000000000000000000000000000..f2f576e56f484ba441b3a1b8f549e60043d38d6c GIT binary patch literal 249575 zcmeFZXH-*b*EXssiXcVlAVpD;4$`EfC`d<;P(nv~liop8L_t7BigZPahF(JN0!r@z zLQz6*p$7aqQT&*XAZBZ|-tm`)YdL z#N=xS7YDJ6r{?qLFEvd98|oTZzp}QQwAQ_gGcfp0NAbP!*hzi{YQM_nI-(DJi;vqI z-nNjGX=;W=1#Pmhswwhb<1{rfQ8`u^-KI<|yF|kZz0ASbo_ySl_Ba#KgS_JfeHWKb zmWX z8f)mieV2Ui!?A*If_!@F3iV&>zCH>%PBl(3kaD8_$Jc8fr!GD_MpV#RT^C1WW@0k@ zf`a74o>RE=i6`8Gw^USL1l`O^4l*^r>0)z_zMoFMx#;ZKr#r`NA<9t^$Sh$XpED2d z>bV{}Mso%K_qd|w{f)FoY24OTfP7YkNlULjuoOVY%wtgMnQ4=lws zZ_EFFIQXB`C2KdghvIyEo}Qk(o`SqiE>?U3Vq#)^{MY%eU*`dP@VI(Ax|w?MIJ&a^ zc9K8NbKAnz+{Nahn~jqrEB?HuW=`&IQkO2_FZAc1-|o}G%jQ2qg|KZgoj7yTcH z{^hIR50&J@FX1ms`b}OxKLrAlCYI#;Q~1)vOP0)r;CWoIxvhR5{3gIZ9PrBz{B!NM z-(Z_S#VF7+nf}-@*<*^gZ{GJhzBJ~UsNFPn`a4SpjnFj_iY7YJL2IEqQC0CnzGw!L z53C|o_b4Q2LQ|=@o|~aV8SdDhGhb3X+jCohlcqcM6r0%zZ}bJ%NX)n)#Y0a$kCZ5K zU#ppRF{>FdQPIFD>^qzFm3}_U;3vn96PzTXV3mDz#D=Y5>Wex^4x{f8`~7*Y>&Jt5 zj8FdB$%*=3KdOT;-Sj>Ci+6Jp?sxjnEB*V1h5`Wr)cxeLz%MP6h=O8ho#|+C{Jf?t z(-|WBfpf(zzaT&S0kS4-o=3^~kHxrOX3f`D=w_Dt#RGzPCVo+N|10c2Wc;T_{C_HJ z*__jG>7DOS=nflpyq32RsFJ{H8njw~s6NjaXN;cZysp)l`=pi3p6bb()$VQaZB!8a;^VD>$>tEkEj!|=%~t|hPZ{#hL0ihEA<*V?hGdcH zu||7P>>wlr=~ykrTf4cwzs;fI(esMr=lQpwCcP3-W=ki`tt7tAbR@KdIzV2EIE;K* zu*+r+O6V$q$5~$5{GM2m6sX ztIP1IjqSJWj* zo4F}zk;|MI;3V#`K!&KF>O8ay zk-a~y6yp35ekEv+O9K^@qhl0sB|?0Cj#F^lFRa6SgC~G-8Ce=hF1^oxpsCB0;kFew zNf_cOExA2zm1kDcDNP7pcscTfs6SNJ!h|bFvSht=xzNgA%v@f@4;3DFeHzEv#jLM8 z?9?GFao;8-_Sp!LeYmf^f#i0G^Dciy(nAC8@~Jm^W5iIgup_lhckZOEP`FUrg}mwU4HiVC;yd!xrNlUoR5 zzI|V|t7?gK%G3=cGCTF;X3^TnmIc`1DpY4i3FTXv?>-C*UQz%sm*&5{KVY8j!F8uELlyQCdg3@i37-c2(1K6@>jETmWwex6 zYi;Hi`pl$z+l5cC^BA+#%SYt0_st_42(}yQ;}zBl%%exD?Kjx8}=a zkI(R6!NO-)z#{s=8ol(Hqe4N@$hoYZI*9x6Y^vuK)?b5*nEX?UIys&))#%;1>V84M z#f#=Kr_tNTB#4L#k=)vqXJ%T_T~syV%P+Oe!&!3fO&iz(UqVg{jY*>9JHRz@&U%h| zw{TYp@2~&ZEnjGs^54r8*bn{T4CFn7QzGH8_t-2@Xo`V4(5uD^aY&`BJd))h0juC5 z{ZV^}b6P4`OtwfXUyETh8@7n;82RZqSm+pOH(7KC*|mE=8?tXLowLiShIp*tFcXoD zWb;&gnd*7HqB!P6*8{PFai2xY+2%tBzr?_m-GSWol7mx=E$>5^7#6dOJOWZUp_j(} zI<}gy|{qYR*i(Um2$FbTX zFiKX9FalS`AtkvR*mmi)owAmVo=q{#(vVkFc#%)-23BWRwN>IEPh%+sk|+M~a6&t| za}jrHGn`a(g#Tb?hXs`_*x8A+(z!O4T%csKn6YSHJ{{Y}n`8)V^{e~7Gy9jeLqBhi z$gd-wV-AQL3vs!wOQ_37J5UJ%VTK_FF|!AwUX7|2Y}J8F{ykOR8-;N1PX+C%mS9D9p_CW~@K z1U2>z-E5av9b+6DAX9UQL$}n2FjK*D;QIFuPNhc|2K2v|t6B%H_+q!z_NY{a+wz++ zs{jJ%&k#knV`-6J?1DHI9C+w8&$6as>X_crQ95>^jl0OrChGdpqBxBgh4{v1(c}vm z>DT0y+AxUoB2($qmN$GORcsVoHA%!le+j;^vRmFek+gfpeoLy>SaSb+<1WszW-CKCW9zyejy!VzVyDz> z)xN>VEswqqk&Z>;KhvCl+K?S1>bEw5xn}ZOz}mA*w)F)b41M2C{`UNbPIHIoxPb?b z^a}DZwfb2`ALo}fu5JbK{xmv~^ltU3WrsBa7PIn(zV0>CX5wr8QR*!=)#_Y}S`3M0 zzX#6g*`JYMxf7apCyd4$8QrMRtSTB%RH3`z+O{m(@*#{j9&?XXHg)^t%$0n1ogbLV z$p*Ci+KrE>iv?Kb%BD_-Jf`x1}_z=vEm zbUmxx&9?@0BD-pLBm*IBF+4cwX@(;Omb!wEf!gKbsv*J>U-Ffobz_2y;K9c9P9s}nvvTnAnLe7}0jBg?^$p6Q3QV%E z`D(D-=Sug%n1D+7uw%kbhf`ow&Z;mnLhY!q1>);fjkKVgqOK*%$VgvF7JFiemo=D# z=uhjEu6jd(DLC`ix0sFbbm*(;@Zbgz^UW4{;2aL2loc#2$mM)PIJMr$n{2P8>MkQ^ zui3`1fqmDh=dYd3P1}kl?=cL0zMxRhRqDUJ(o|w1e zreVYoX+FZzy+lN&eRN%`^uj-xz@OQm?D9`rxW4)JnO~Q`XNy@Fj|j3S@L+d#ZDq={ zt+Uhyd6ne+{b%`VlAnC-iyXZ9VO~FsD$z{o_1S76EHu7LB?|VXTiZ>%!?QM4i3|yj za2q}7=lz3mY%Am|=PCGSk|)~bULvGpzHX5YB(UKdKc8_qsl9gTrHLHR(eqjdQdcTG z#5h^3iG_2jSH!)m3%iRIqlOOaaJ9nuTay#UFCAN$jXk{gNJr)}8Jpwna_HrF85lmP zi`Uqhz;<5guvamVNBCz^q%!Hy`0ymFr_77Gq~NwW95k;US?4dvrpllA-7nMWiY!ek z=~#qciB52=63e|1V4uWjBY?BEk?xzC3}}#Omp+v+OB;=POr=q2o0Z2Uz(QwP|Qh z#zZ{Tq>4h+Efbhtk)@uu8khZ;7cN|2y%km%Ft5JnpoFZXiR{gv_dH{tUVxi_Aw%uN z$OX5dw978=!AFMXTK<~+g~1aR$i@s={fP#64shiERUZD&Pf{HTf%Qj2@WBF6NDoq* zLV<;fdjpQu(G8To=mK?>6ltm248uwf=K*21qP=&SDb(uwYcKXkuO`@EuuAduFyf;z z^4^WUvG6r5Y+%XtYks|+?q@qwNlIcACo>YA?|)&U8d?w^m^_Z%^jiOxx$a|Q0`oLY zD7H%W7LoCsx@TyHk@D!mdvx7ciF}4t+dd>Nb_dRb-;)Q&$|B z+2mO1w^6CR#~4d4`wQijdk_PueR@x_>vs9t5DPOGO(G2oo0438AMmu<%ZqUF^)2hB91i(P=wp66bR&axDL`=#S&KbmP;s1 zoa2eLr^j`oS;(b!GzxsTy>Y6Dru8?Z z>zF?DM_R8Io!c7CVC1)!PUrID)!iRC3lSZ}$lD&0OWpWdXhF@6?P!7Mr9udxHtoOV z<#aa?jWR#JP+khY#u>?1vvcVgDLoBlLNsIjezXs%PDpr5a zp3@$&!ut@1dk}lrS^Mg9u9K_VMyESi?7l)KVh zMhdehc5$fr%;a22>Xr+>ZrS~g>D>L=lI5T@Zc4464k`i}Ni!N<%?BmTtcdpP4Hc_a zM>8?r&pr>S%2Yq<&VGAn45hL;r{JN*DS-#xRoc4gmiYQLNioXookh%xXdOq|W#U6i z+I&tU=-xX>o;PO9v&c6cTVmky&3YmrTGu-|I?bXjCf(2qda&Af_~ogN5l6Q8#zaHO zvUv$hE)Cw491a)MKJHd+sF_YDiH^Vg#NtoIMGdEcrt?QoiQDylRCJHEK3au-Mo-y( zMUybWBMZ0R_3>$44Bzoz+<8H%ju`5GWm)V<7x*)!6Y@D8lp}vgee6ip|LwjF@rZtO z@$Aw0F#h`8bA%`13^<)*f2NQBzTtiafciSU$-f5b%cjv%4B2iPA3yeMYweE)O%y{# zU$S2lWw?>739KiO@Yt`dRYM2gNBLjD|4nxIzk>fq75ZO||0mm({l9iKw%qx`AZ-t4 zru=s=^XC;wGV2&*^wZQ%@#br@w=>`Rn>_Kq&m`+lqdP|c-J<@400oNv7%Vhp*;VShe@cP18 z2%GDFitb-G`mI1v+1vMc8UEkm` zf!}}PlXUeL!^-~(|G#Da=O_Ow{C~6h|KDqVR!Q9;fbXdSSq?E?yD5AvJGJ3mX9IQ` z2R`6F!VrHZb^xJ>oh0Q5VYr4c^mgGM8!hO(?J)r8qr{}Xa zY}hD44UhJ$lw-%2%JDt@7=7DMLne)+#28(PK|`l8UALY)vtHEz*7_ye&ms-dKVv`U zijfkS`taWM0@@TnirA$}Pa&J$kJ%G=jGH~h%S9z_)mG29pzzCiWdJ{iG_`V-a*0Iy zt9e=6O7yxF$z81NF&P+5wYPvg+}E`)X!csHnq7yse`p%nRPgxC$P@N&BC|86WHbRWFF)0(Su@lO-{*9}<* zV$iF=w_qu;OXcd)&;k-T zS0C~LlxZ7_d>16IAshIG{Q#uvd{FL>!ncf))Nf`}jc`hxBBP@+Utx(7PESPy%bmKj zIG9tkheg-2v@pBd;}Hyqy7}VJC-eE~)?a<1+IX02%pWl(;Py4&zAgc0Sv2QA!l4b9 zRH=u_k&6qFt0f^YqdUW0fiZ@z0Ig}6`W98>(xv6oOapZ^64|w3Q4fRAVZG#87og|86jvaSdPu0Zt5bWRo#?$yubrz(YAE5^QL4agSPvWN zWH_=@{#&x3@dV%7TX0|Jz;i?_8W{tosS6g!AvqpdvBn;EMm^dbhRe<77U~PgyvDaqQ| z=dT;_Kpm|5;P-ak;w!}O0Gxq?O!%*=o(*30Iy%1RUXZ@-Sh1E-wdmkE$}r)yg`?|s zl6~p_y*?XPET|TyDvGVGQn&e0)FmC31fLMIQ-ZQb)L?S6s}L)`E&ZRgiq<O@yA-;jd@Qy)N{1wUwpQMyX#Hy52aH$GWDyl96QTUfkO2?SN~iSZko1X! z*i<&UZPun5?>}fvnZj|BKpkq_$AxCM> zr_L@J`{=*>o3!hXWb?0IfhN{sP!8x7G&Pb-U64CX9yZ^XIZLUCJjdKK{oER09D@Lg z95dhFS<~|#N|#HM3nBBKXuV}~gNqQ}D+ovWV5^W%B42j2MF*eBQ+b|i7||L}2(%2OyEK6D7$5icU0sFZofxXTiJ=YXh?3d<*{SSr8 zkVC*eF`KB~Zfjj0DG7t+(!Yx)|Gg8Wx7*p{dAscj)DjpKK6v_finH5|yO$cGvdk+W z9{1S))wTcmpk#B1@ZfdMqi9IJ&l;L<=ET83UZD2S!Ro3tzy`-5dSwR{jFStmb*wrR zSbXm<6)y5d8y4;{=GC4HVez;PF$#RLg#t)!;ra{q&F8cSd$%ApemfQgBjh@5tH$j2^Sip3sOGQ+;Y?vl+JD`s$iXAMM-0x=V z!Bo}g)rvW2dv%RrzPt*1W{D${oU6OB^Xf_AdhiLpP;5Y;F+_T02W8fh~aiCS$j#$f$K=EAP=g%HIT)MU6LaTuFX|c4UK+&iq}|9A)0h8xY&lUIS7;KzuPP zHT3AptKJ)dna!ui=DIkPnn!l&pc$^)Kg2ir^-`=-ym{5;y_FzFcm`ra{gV%P8XqF? z_Xhj414e2UYu)qVj*-s)g@J?U{6~`}9Fw@Q_k~a7ph+^k+HDIq;dqd6W4b@@gl<#9 zPmYB?y06F<2FaP6K{|$Js3ur$(FT;pWg}*hYx!!HW2F=hkTk5nxRVMbX2}^;Dkaw zbsXDR!FS&FJ;ElMHI;ej0|QpNoOdmBre-TJbIeG`QdJ}MU`D4h4*3YWcctme=S9aK zpz!cDXfu;fL9b?zvE?!_1D;+Nmh+cVJ<$Gu6M~XH`qn+9Q&FR2WT`TOx1c52*eN)U zH(>L%0;=@!tpe}Q+zg?6Uy8fFU<9fjF|?Ir@AiWq5U4r4!Pz@%?!0D5}c&r z4I-D`VO^n@nmt?*br|H-OD3=Weajg^Rtmy0J!nHZ|3z^WUge7Tj)U4j-iaF!tZlHjvz$EUS$F-i+@`DKNV~ zh>k8-Lg%SGs%zkQ)gYV#)<@SeH|go<+jrwk8(6yN~z5RP+=)pvHoC!Qw=?yN*5=x9Hr~r zL~SN>KvrF~GpVdsX4gcm^D@Q0>?U``+y__pYBkV*H6Ykv7~X}K~Nwi-8xTwAF`H?s#vABXZG z#X(Ptl8XvLlr5<_vB}4znjjL6@5;bx#nw7@=F%by?RrZ9)9_POaW=@XQ(B--HPnLl_6L`C7s94jPwywZ>wehf&uEHenJR z2S3~ivj!n`*k8H!#Qbgj>{DB+QKl~puTU8nbyHBo-%a>qypcf*k>J@ z8t^Gi#GK3R?1E-du7E9x+Ca(8?{a#vZnY6f;_`;1c4vC@aIb{# z%J6J!=W^*`&Dx!da~}A<`?BY_zrv{d>>Qo@Kvq=_$~2@o;Pg@~LhHxl1KKYq%$sK% zV##Z{A=n?b-iAjJ2^cZ}_|@o_XL%>J=S;h@39RpZ2rcAed>muA_v(S)R$4Es_W1h` z-Ls=b<|tK>PH0NN!u*Y`w|(nA?!i}LbT2(K6z7H*l^c3gI9*KK0;~reM2QkopoN)9 z0$t%celTp6(!{}z$vuC+RboFcA7tuxYDxAi>+|~W(+??`f95TFyXT=>BzKnC=ibWo z=?Se5S}l!MNEySZh`!DnJR)ODhc&rW2c3~t0nT^cU(eTeuE^el86R?XN@8CY!rS<& zB^@ibwhQ5{O4sK>y?u)LbRbcT}iCz}s%HtUXv{~G()s@x8h-)Gi$$%dpkI;JM zfJpLalj^gzP0OtuA%>Q}I)b^)kc*d-f2pN>>b)~~5;Hyt4`K1S%**WK?6}cpjJw{s z;A#m8+9 z^|AQ6*4ohTIiBSJAgiU5IMyuB=vux@FS2v0`jJ5gII0ld13f_0|Dd;FsST+eu_!g1 zoHLJTt`VYUI&5=h$2aHNA>;EO!?kZqXmCkNyX3Z?!w2NDArsaS~5 zYY~x2ZW7SOi17hy!+HC6g{R>5t%h@q_OW3-%R{I(#{>cg8{wR_K*p5QnVKQn%g7dd zY1L@^gqMyA-@RTM5epvEG=YJB=so(g-2NeXYF@$-OZ!`%7sqzKH-wttnI^-6M;U*r zDKS8e+cCC~lMG3C=Kb-HJcMBiBkE$TApbn$Uy^gfYdc*S1) z~b@Bs9EYdjU@Z;N${NN0Xm}FjjNb{`;dK3+N^j;2PQ2Hf|65Vv{74>f7WBe`mpq!1pWLieY&8Qvg1qFcztKD9ck?K=A zxkW4D&S03NXsok8Bnt?)NJdnAj3)~Y+64>cmytP zosVx9Y!AfK1vOvM009Ddl9$*1-cA~sOlw8DcCp8}Y3%+Rz$j%-HYC!Zj#QW4y^L(g z)m4Y_r@!ieI#n|4g!(nnC z5JL309oKY*1G?*L!zQQ?qM1F%#47Bm3;rNJM%N8WW0xtD$Yi){i(LqFpO8!4APG$; zt6VCx)BDboQ?<2th;`2{>Ky6Ga7ftM-{{)Fa#wHm>iGvqZY12tQ?;3TrdNmT&`u3@ z_!xF#TPoi^kz7*fYDVC7DG#ItWYTZ=V~T6onAxnM zN2qxOR{T{ggVxaEBV22W-dTI?XS*U}_I(X8uQM$%O7AOvmPl$r*s zdjUPneMZPD2#}D|H6VFfzD*#cCp#=6%C%yzXBPWt&)+M*g#d+ec=6s~fG^q@{y}?Q z*d{v90X`<)+X@8(KDPosU(Xi?1^x!lj%Fs+NlsFF>Jw(AV?MqH0rS(l{=5LE!y~AG z4wF~A?Q@~$lk0d4gg}|=vG%D#pKWom-oa;0s2tIW(1;K}%I}<{_DO#*$vkq_%iT>G zzb<=@UL<*#9+g+Ut=}GL70nncbRR%n3_kY`s?186$J9BIH0#e2oSu`*?6XEluP40H zZfPV7$B_-?c_|*~F(`38%W|w)o1qifZ>>@%Hc0;t)@7pwXf4tpb4wv zHHMxeIYV#r24h689VNaepz~$horjnr5@PlW&r%QOZtJbM4p9fOX}uJkq3%c**V2CW9IA zNAD+dNQ&%@S56ix^M)avD%sG2>kC}w)7p^E#ZRk0PO~&i`z|MxQtO0$K5I{1y3OU0 zZNLL4$y*0lbjy6TdgK{OBOZ^uZNZ%FXb5hPH+f*ZtXr3UsNx`F(QJ1*mTysd?(t3M zLx1yiIG4PE(NM)hmqbumctf3=3aWkt2FsBz#5u|7Z8E#`bT5@R)vzgq1LCNoPPS!= zja_4Llj+(ufQz0%<;7LvxFwm9H2kG1X+Tl(g|qOLydW1mcV`IrJ6R;pdvCmkLRYb zXT#$SOzi+m*~VVbs`I5)a=_JQz~$cuRMTj}38j`~NR9r-J7MT4J)c^$^7S!1)N8-^ zZE|;WxCF3AHHMSY(9b=uRtru5&pc4l%L#2lExC4vss@xR0-8M}cR`zBC|=y%_TzWM zwY#t+e;{~)ihKtP@z2QQf+56FgLXnHpjG*7*Qg^@HjOe4`CId2EMh(e`*C@>SHzR! zp>~DpSO6By$Ry$3js-EFy8~32c6OQ|w%HAGBlpmNp7jlj+ozT3YUMlJnC2dJ1JFhc zVL~W!C;Ak;mk?nTsEQxb+LuZ>8C+Qee5eR5_1z4q9Ls1(38&Nw=hGm3 zt}%Y!;GS)O&X5w9NA=F6C~_~Sd?p@0MYT3kYZO+yKh5XdHRo%-KJ{&PgG^#Szk%kJ zL}#p3K$IcmY3EHB1csg#Jl>;!baekuixsh*a5-SbNI63km&HP!>M1e+tei z=UBM}SH9riE(&|C{p@RRtEEkF(e7INRe=NsxM3X>?}FTU2x6xPC!1fPpsk_54my%q?8k_nGL;=jqv6s^5~)s>8+Zot&Ci-yZU+Fq;3nDlxWS&>F9i^s1g{@?d=HL)>b28MwZYfq zAXVXOmga{87J~0er34MszJL?x$`+pHUA58qvCyt&TxsoD10+=W!5Dt1uFPj@ph>x~ zi)u$nfkAz~ps!P+sbDPPTl|b@{?>eEN;7`w#0Sa!HU@5!2pOsna3I{>Mmo*To<6(8 z!npt7NgPYp{?!30PT&@JzuAuztG7aSm1aRLAw*sneu&(;@g9nqyfSQ6;Rv9&h+5jm z&Uiqp3qN>5vY1ZY-|Osy?n8ZJPigJSycLfPVexyF_g34d5abmr)ol@HCf^7A@h*VM z7>{qQpvBWQIobe^IEtTcZ2)qg@J0Uwo}g1)_=>c3d98Or9$_HENn`A{)}GO&H~R(D zY#lu|Z8`9lfMxriX(1)8gtu9%pC*t2mILfj^MU zMHX0O@>Wh2mf_$Mts|UV98{i0wHx?OYNvYos|mo8Hl$f-Ri`1IkMFJ10cq}*)Zu8& z+SkU&w?;HlzDXCL_9b`p5{|aMcW4cTz`)8zU__MfX8R(JZ;n=Gc$M87hv&M=k%=$$Qzf!4;OM(U*@Wn*c}4{i$!eyT}qMo9%+oR>25>Wv#E|W zbTzD~t{a-Vf?o~YF@jyaEj^N~s+;>2XeMx2F zv(ZudL9ZyIK*_Y^Gu#15)oSb%r8CBpseV|TpY%gQV0z*(B9a73n86AmKFqFKZv~ig zSfMhop3-q=We;hGw4J?0r!Sna>C!w!z87w37&U4Y` z@{rm_yB&5m0hss!@8JRF?apc=`3F$Rfe2!eHm1aN2ecnLw+}W@V#oyT#5m9o#^ZN> zwF4NE4JVlv&$qm)7YnKCl+fF0ks0zM1Oo4PfYYV+%+*&#R0DQ)uvJNDak|;axQk;% zYxGzzrvp1i+{Tf2;K=Y02g1V%-}f1E0LHw?Us*TUAmEdhs1wxV86IIO$J@!L413~` zU%&SYm_{T%(s&~*l^)4TodcaFRC#3mBCpD(uHkFe2+u$_g!$xjV2y#u@OWnO`P$)q(O-D>UI0>L=&BUa0+`}! zSupzAB1bO$HazS1am)U+9(pC!Ga$t#14ZqPo$e<&x!PKFEB{VQ3JTVcHL<(sXY&k? z|MIPZ`uEOWY{XU7_}fL69W)1u?`~56gtn!zn?<^QFRp-Q2K0=r_PO4_K|TM%a0_Z? z2N7mxyquo$h*VVz1R_!Wywfj3EAfG6&tqv8eH5ebB z$1`w1`@QG;to`F7z<1e4b%})W!gkE~aj?b12Tz8~U^(VFC5Yq=!V))#2`t8BEaq4c zAsheFhp@>SU$h=SIp+S656|TNxuJl}q{%r{#lWr?criob=!r8FSv)~~KHDz=8ViRz zqHB)4HQ5uCLvvO#VbZghoIbrX_5&QHA?H{%pahV?&pdjVBL`bZp{cn#Bj<-~hbGvI z*t)Ns#7n6MH)PJxWT5)yucH6kTgmbc3X*%)tZ$cRxFMQ56F~qSNVUEM<~7deR}406 z)x3;3>Jv_V zbbxl=uyhd&V`bE-A?+zaot4$0P$qh$vUEQJ57>zr^U^z2&GOps6j;C?vag+{De?Bj z96b!l5LD++E@i-8L^br8(vtk5Y^n$df3{kP>Z1(dFE9y3)^U)VFvz-*9Hl+oU-7|p zm$q*63`LwTIH0`mpIx53*m{`R^4#}D#H0t?+B%8)ot z5Uzu_rW#+tBb)QW_~`|$H^86EKEY2WtmX}!JW>ukBtkE+%syE$DQjEH2~`qXVS4WC zmw_n3Ybfxu7U#2(M>*Ued3>#4ZB+8&JnQNh5K&F#dx@iTD5#kS98@+@J50&SuZ~~s zm-M3tQZ()WVGzGK1?1PR2Aq<|^^v1icr2(KIMa9u_G(ZmYT#cVCHx;b|5J#nNdVEn zbHh~_$X^Lu@?N!L)R9(Q!V2V1lI5uQNLG#-$Unyz1K*%)LYC|Yl^B;nT#AASNvpz}=vN#}y&w=PK&w(D60)cOB575yarHQ(C@lp2; ztwkKL@6$l#_f~0M9@X<9;%*1$C)q#>dVowsH8ATv#)+9*Cb}%> zt;N>v=R{a7IUivRKovVcS~QTIS$U4NJC$&OGrwdyrZ&80HVOaEly6cgJ`2O(y&Nk? zXKq9kx^#HiHJ4hpgXS=6PzAAlcsV*k*RkAULoWb7(rOl|h5DA6{0&L<50C6W1|JlB z59pw&pItLZjR?+X!RiKGyCh@satHDbgPq%QJb6>;e<5gU6ai@oFU~ujBoea5-;vbe zQrW0;T8cw_EcGwkgdzSW0-rRiKayo4?sqq_!*h@NQk)h;|pmLh5u)4uPr~~tCil?PW2A(X! zCo=4p+mgw4l%}z2On@6%z%pK!{+YfyoQ4nTS>@2V+&i=$SilZv%j}^8!ONCBvK0uP z9X%@HCkm7mJu_=daFQWf)`1X?$7V5y2iv+H!-E#dI%jJ+d21&>7m83HDIpV6fC=lz zO(Yk9E^-S&3H6Y^T@IP^m~dm8bc6Sroz~G@TN+ESyx`qsdJrMpxiu1ka)hEuMRuIeUU=wO zY`cZCw@gtASUw3MvfWhWQJ^P}BF_l7gV zNrvr@bd~>U0#Ib>{j^BzCd&t#zKr4Tc@=vVWoA1AYfZGq{YwuY7sV`FS* zD0Fr4&dVJ=R(d-@glk3xPqs<)9^75SVwu1GzjHXJ824|;8wd!+3)#Hs+EiDUW@@>B zWXt0^D*hfPAV~u;u3=~JJ1O+&0UnbTpwBboN`S4+Pz_4Hv_s-! zN2U)GAS^6O5L6@{3u*?hi5!RE-{-MLdN^G;MlARzn%(eXdLd%`~|V_@jy_PAZ?jY8#2`w_O>3)H0(&Yq+G z2vt0Y;{~9f$?tNN{fuw4bque_N0eO#nsPqyppB#l_eG0%D-3m7MpxLfJ?DPl!ae%e58p9Mp zDI`Ha%9HQjH3$9RfTR}8j#886!IL;HxVp_|O9$+SIp1wzvpd~uc&5Lg?p-D(G|6Lx z*LMN*UKwzE#&qQ2)e^A4y?Lv(Mb*+=)VYoW=LGxx4LXs{$Jvd5VABFeqM0LQwq>~s zzD(*NJ&@>ghrIhKD_cKw194j)>|z`7AjEXzI{{bAL?7D}@LEUFb@&3YR0Rtoqr+wt zcr%S+fH-0Q-gYHFFcs@|f{E!`d8=RQkW$JF&1Xq(#K%DzKRZ5)!_F&uDn?yumz4}v(jqW<~ zA!K$B(93mC$Rf7^3OAeV>H}fH>`*%IpQ&cpm%s&XcM`ORE|8*FEakkU=RdV&I+r>ZbjI0%JJMebIi}a(63*E|JYLBBLSjJ#^dB%rO zrI_3crYodfKU%57HoZC%#n^>qY_CB@^9Gq$U&RS}p7UOxQhZ8>Ubi55JDb;k&`f~# zQ>M03kGUx4qgUaTT)PA<j=(5eO$$4BCF2IY8c|b2ucb!p*!C8B zb0U|odRr|NmdgK$86O~p)U&0u5JV$EGJ;fe=*VrLr4c&(1 z*j}0MtAy25u2E*#l5;DNEx(BqFmG)Ni~XonB7SbB6Lbjfi1|ItWbP_Ju{ULEpVnJz zBEb3G$|9_X+%8b0lvfNQ@Wzp{^VdTjpUnH(xxC18JjmwJV;;E%tof6@l63Qs!HOU* zom{10rIIQq?$Ch?A|Gqta%f%Zu8bGR$|HrAlCZqnfWzPvHr!N0vSewhMGk3~h= zX~O;7UFk1I3RO&Wv!oKhi*R4;f6JSMEZ<~`dCUXhYw~s94|0A3ZA*wS2oI@`kKM$qPf%0AeS-UB^&k7Arm9=te$mQT~@ir}7iv|ERBwYb;n^I``y`>I>VnF-h{ zN#lAZjxYK{R^vWgNx2O4C!x*01E8Hmct}rfdQLH)Fix-VL5#YkdP&BVHxZmcB#Ui# zeI`ar2FFdwu3FlBo`(JPG>)1E(<$<=y5@z~HD;8OreyC_-rL*X4O^edyIM?bccEy{S(IWjhJl zS4|sEK0fF^SSVR6DZT7#@%)y?DUZb&hh5Z&FqeegV7q4V-HW?FTC@uNv5qiR*dy_q z6X>d>d;Sk`vXd>5eBfG|-WiV`HYZ&ig;aStZ{0XINb&5UYPpL=ESFATtiLb$S9GiE z3w9FqdYZCFL{7!EDX^PK9+jG_($8!-|(WF7M(DS_W;O13HD z+mi@A)w)m3STt7fxw#743HL9$^*y6rUx0mk5KBytDDgG+G?XeWK7oaQwz?G2o>F@^ zLnVQuF@*G0Z+d8(aiC$Wrp4!zH*$vx%#|TmDC*+`o_F*F)a89-pv(UIJp@wEfW!*Wu}ifxvFMKQ1{i zoxw~$A;10&7kC&Q#a{awa4x?21P--5o03#dP9%_aHu6!-bN2EP+(v9IuFw5o+tpf@ zX_S9S`VcMX{@C^DH35a~>^Hgv&A0=P`NI9xMh>xnNf@gzW&bSKW!LMb`$Qb~`D>ZU zPeQd}h^YNt7nrxV`ttm&MWj!l_eku$GhI3w6K8H#%(GH8NSrlwU{|C2)EuvXAR@1a zJUlq4PgNl@N|eiYzlqCvGCOsp#F$0G<9fII_vPde%<*j=^BqRwz=V@j5K-0m`y<#X zoG)g&cbdzmn&uvMSpxmwtyX3L&C~YnVucSWuc~U>Z+KrmexCK8euCz1K6;!pC(u=; ze4aH(<08etNhk%!TP_{_PFSR;LS%CHOJZ20i(=H}*8_HgzWb@MQcxOtDck4Y*=qY7 zN2@;Fz7at1sQ`7g?M-@{#ka}wHv)}N$UQ#_K12BD8hFUD+YJI&9<8n|TSo^!deHM& zEM}HjE5o{nlVL!F*|p4fe1NIvkD z(EOpaSE_V!OuPnQ#413Qkd6Ng{7u@14uuM38 zM1K}^kICwx4YXjmI&sa)U|Ox|}lq}+(8ZXIe2A-5Q& zg>7!w^hySwxqa7w$bdb9#b#MCgRFt@R+&J+=tC~@MdGe!eNU*9r=3xma{;RUt}1UX z7rbPWx-S!Qwd2A)awF#1EGr)Fvx~>k0_H%yFziCY-WSCNv+mugaI?)!k@8%YeLDQ{ zQyxuoS(~WMP4603IPtf{AwnqTt?ucI<`<6%o_gICR}eMYY4LFUOh$FLq*}6&xWP$i zJr3J4nr4_!cF?dU`?~m16(p|L#xAHf(0Zd> zlr*h7(g)uP$DmQ8;!`kJW)Y(;oJjM9tlh#S`VfV%$KV`wo2R7>RlpsWL2u!hbz-Kv z_O;c_kf_DvTB@6XW8w0>XhGb=iEzXI4UF%PRn~_9A?n`1(gqZZ0#stNa+udWNkwVMyp0!9ST%{r- zfNh$#z{6hy8QS?Zn_XU1DEWD^dzbIyU_R{kn!gR#4Xv)q;3GL~EW?b1oEFuir+LkOR%JU&Gi6n}$@{EzCdK>y&GvZVdf&0zvm-{ahEC#Ltr~+W?TAzw zf3kDykD?;8tI7*z?fG^2E#mi$9YDn3Pe8l9xk%21tw5A;xgZp2bnQ$|eFzR+R59;8-5TBUycK%jDi665xfFT9N7O=w*Eh>O93#5Cx#UZ5 zzfq}v;QL5XT8ue62f@)z1aV=Bg373~y1?G&MZRtcyKNscjZ#LcGjiQRcQJfgi9pva zbe#bep4kkIdL_ZpOc*`7CLb3cRhvv4Wkm1S63sTKK8rqk#j$k=LCeFU4xh9nTq(P| zK?O$;O!mK0uX78jwO{5WI7TQZKIqG=Cv?0M4d4ldm(ApKN7yQL9tWNj511#$pT1}Zc7n@>!xd&9v`0to#UAwm+WuTpo$=re{S@= znW&6S#ex1H9-6B@kR~X@NZZ?8M&d_|mYH3^Zv%r_q`39*Gzm6`+jyR{gM8o^0c%e2 zI%5C5jC);NDxZEa4O8I40OGat{-}gM0*l26`6D~?&Lu;eiCxIXNWQC|DtEM8t5(VS z-lE{fd6FLkf>aBRo=`ubU&Sqa3ZzL)*Y2EeluG249KN8`?>)a}XBj~#oOVv|CV^p+ ztY(IxJa2x37a&GD-xwe+>lrN2D5kvq%M1yYJS}~C0Iv=0Vorulkz4eS`+f`oowORv zDIM^JKiyC940M|l=@aOGudwZq-EOnLGVU5uGhk2*RIiPIK&kGB^s|> zl8HGkPqjXt-_$>fEI`~kiv`DahygeJF!XSH{dM-{OHXo}sVQ;D3M91+BSyu!+IF5K zu2^S0J?}BCXG-Z~eZt6dg1T?gUOKh!A~NJ+lu|^^!|CJ|>3qhzT9ne=(0AOIQqjw7 z?t(6VFpAO+unLk5y4mo9qi#D&fj(e>M|6wY;Xj*tNkh zTUaIP{>YRp2(=>7S0mkM6M8+fKmPbp?B^I={w;&Pqm_>*w!^OC)jysP2Ylcb z6qtpl7&YsQSf^9#1<9|o=558nlrAB^C;4c0$fN}t!n(3v0a_Lfx#K%$G`}cKHY#Kh z!01H5Nl(eML1IOsM~;7lr2gqD||q1{VTKNPh~i zC^#>K-;oA6=4l;b4`wni^5@ic-Nq$^Dg~lebc8H$j@38D5I(OJTqVcp%@nP{fuC%f z+xG6%ixrZO2?j7WnFEgf?YpKr&w6#?Y_RFZM?4-LmkNaBj4~3xcJ?^?MUMb6E71H> zbsXa^XGj8RlJRKaHdQ-Oc^l}_AH^F?QPQgQL%S)efNZw#i+@--VG3{_`0{vx2XIT39_&0eL0=rm~vEjezxuynwulkIR=#v1e$(FWI2A z_BOcI+%I3s(@|fnx3rBEd2uGy#q)crX}HCdx^7@ae?Y$c5rq1RI8z#Xa`VtrwZIMD z%hp@#wOxUzUCT~9)nncQbR(A*wWzEn(4%3o-m`rmxjG(apg^}#MV;kr`5`SBir1*! z=z8N%p8aAtXuK%;Av%03kK0|wq!yUbt&qR7R_@t>_magSsrj6wAy=oi0bfned&!qp zJhXt-v6DQ1u1D3%Wa_YVf?bX{N!VP`D4wkk49U>cc0EBZKL)q2TZ_fnLP^E}A?H-{z>70fkiktLrY`XH9`cX?yG=ES$1Lk#QXtAM99SUsy6LTc4|Y}ls96+cN^ zAWaPkm`>IpL|}j5OJ%*YF&pY_ zaX5?7-MQYTz(bZo!%+8yT}aO6Ydd&txze>#GLHg&^aJDuPV>K!oWQsKO?7ewsuO(@ zFS@*tH0f)2ZG;oG)Nre*)DPEH$W^;(2=n7dXBL#e)pCeiXDJlRl{yAW+2;sZY>zKl{OFY189|*%NhZ$R zwBWn%K5i-Rk*Y*^sIq?NxH}#DY9OZ<{wVN0)|ga;6tQtwp2Ag=zP>r}N6RtkRp(UI z32i}-o}zoATC$Gv-OR2HVvB3tpUfsJaC`VT|D&$KC$VgUR)nfutKmHSuI`VDt0q^+ z<8{&H^Qi**zE0@oRn=fzj0yjeAX8}5yE?e@$;>|vML4NAm`Pc-7&YsigCG~M@Vv28 zkH&37>hVvQ&ret7`hY3#^;CgksNfLIntM`6$RCNWMNMH7&Zr!@DrxZgy0Ule8TR#y z4No7)joeU3;E22ia+S#O1*+}vebSWZE72N+hu>MieVXMTtS}LDr<(%hzr{{_3zL>T zO@qoMCWgy@JLTOZfsV=4Qi_KdF62axHV?0Tj#_p2AfnlThCq#(+Ugz3eBLL8U;8Sy*6`0lBV{@G0V=9Ud5BJ>TX z(N1i&neM1XiIZ5HLaUokccR|*4Q=qMCG3{%#59U4nwP^yKjjeGsSmGXh+F8&^a5`` z<8l@eM&3=ABFBfPz*cgjxf1^dm9ec6qfO2PHfK^*O~U8=*Zi#ycpwVMN< zipWMtQ$Y_2SLtH$$xD=iX$UPGWkX!ceVw;1Q52lI&&{}Gn;n?~++f`|u*0WW=jF;3wL+6G`T zMlY@)AHD5W+bcZeXUbFXRaNO?55V>Rkggo3SLX9uShvYGIdLS@kvHFV+F^V>%xbdz zYWNm{T(va(2>SZ#e65Fi@Z$jHZjHGbCgfH@Rp5PmdhP9mX!Am;C019*^!E@H_!O`N zVyz3RT1DH^9UwW{Z{Hx`PDbq;|2X}*(bFbA@;zQB&N%md^morUu)a(MLRyYsqoni+ zt{-LqW`W}O&*QDhysjAv5oHr+Cw%XZedEg{%O}*hl)gSQtmJVv2a@o5tiBIFUF8nP zup$2*$!~??1f(yB53x}87{bf~eKnGMi zjch$-Zm(?umBwxMLO99}H~oX+;=L%`iXbK-C(w$sa=xZ8 ztKu-|Wp!ww*}QbbwPdS3-Ux7gJCHQ3f-us-2(6cC498DCfNrzz^wv7!ii~75*SUm! z;^9);AY@ZW-MlT3Fp}Em|A;ocrZ>rRK%RNQ z=Za4jWq_>5vAjGKpWTcsD-iZ~8vq&v7D+%hL&>NfVj#Fa8rCg214h%9y)%nZXT?;T z_Qcj`bJy?4JNZN3`G8Ro-#>R9s;#m^xz|=3#`%N-yQ_~~(!~%9UnddY9WEPEHq$gu`7zSZn~0xqs`dnkeSC)o|e&f7>H&L^dv-^Ihu z)`2RNAwKxf;cy5&sxVBh80W?4dapX%as#WeoAmA@xQbx3w+$`<9iyyMiPN;T1Wq(^w*|hvVq@U*M z6A3gCWSEBJX}OuDn7IYyDiszH&W76T6znf)A}rY1&es+4pTvi2X8nLUX=kshhV*(D zye@2_=+XGtt%lWN8KGvGcUPPGBc+b=nwQhobbFKfgA#=^Asw^arFclo-M(Jh@P4@U z&f9a+pS+(>ML}n4qfnnXobN(voo}j)-RK%Aj74}RRQ4M>>}en0Ttt4BvNz^LfVazc zKWK;=UWXnP#L??cV@RIu%!+=ABpwS8UNS-1Svgvzx;_R+-zk+Ii^qzydVWk{v-<3Q zj=6PM9|*)9q_X!Q<^{kl^Y|Qsi*XUH?O~R6x!1^Ho^sDX1f%ou`^S- zk^$)$+KprS`+|i%=WULdH^>{m?jG2*mB_j7&yn%FKN~u*w_8A*EdISWse#~aHV{w! z5~~;YT|??*wWgz+_(GGHcrzUd?f$Ht1cLif{x-;G<2Q4oeoDc7Qx+j}9``=-#tDy$ z?d`IO-&dPKIKICRS847zqpCghY}SXzvfp4@p;jLZE}a~vr6Alb`uraE5j3=Ejb+j% z2Ix#x=jYmmI_KrhFMNBlcrTLA zae|L7Bfa~B!fMa>Vq+ra=v#>}I(2%7qt}7wOPM|UHZ&Pt0U6%*6T69(uZ$1F71dn# z_xpQp58#OQv@a{-ikq%Gd;F+)TzA@O4S0of-ni-dwLDHaYBi->ImcQ)l!`ezvBnuQ zn|TeV2-hIeLcD}8=u!jI2#r1XX5h7{;Yg4Jw0&Qm=aY8ySg}Y;K#PA|BhnLH)X5_T zehnW`zB(tU?*%gCtw|g5WO$2Lp;MpN@XC7SgHEZ6@a z&ooOt)4seIU*ir_&KgkBtHR-q^7q=a&`WwI957QSYi8z9QY?-_l=CJ;xbX6fb5T-x zod9)X?rAd}aC!+_Ndbn{eco!+XMV?|b8hU7;Q zr(N1>yM-=2*%VPO?KC9@pREPTXqrN?GSQSz#^{z+hTmfsIDWeS?S|0;+b~+FKDLp8 zF(?7X;PJO-0qU=K-Q&ODK4FtblND|ss~;rZ6YkAeji-<{-Lq&7U=YOY`>NZ*@vlCuh zs!XP&KG37MR}cZ`kh&lW@Vyev5ef({o9+Kr9j%@~TLxLB#U0e4cHABtR4NvB^mxyJ zvol^^u2QHp{08f35nA(}vf*53ND2n&f!42*N;^l3iHbDo#hPLlf;!37w`b(?w(53b z@!72Fd5C;xLgp+dtCII{HaX40{p;WCDC6+C%eJaW=JJ)ZKlR-i*L~q+FA3%>emP31 z;km4tYq%O)W<0F3vtUPkAk*<#5Sa{Tj{ALW3z`nd1GV3V;%79jn;)>&pVJ;#4+_u* zr*0_}Zhu1MX-Q0G_h>56y6Y27+`_kdXn2?Oi2fsM)BUyf*2MP|F8&oyKCkrW>Si;C zPVI@g8nv-M98q@nzP_x;#xc_$xc=p_p6AT!*{(sCud7W&u#ftPy}l)C9;DBbo1r!p zljLK8FAzyZ>p{wi%jdQZN$2};=e|)uuh1$SfXudmWlhp35#a|DL?q*vG~EO4B5*&O#POJ3&+N3(zwatpp&PVAdH*fhpI<+Q1lju1M8;(#H&Wy4Em6gf0C z9aX|C(nLc~qK4m4KGolc!niA+56d>$sX#x;m@l|Kj&uTu#U7|qfB4wd={S{D}t` zC?W(juVB`;uRX~Fbgjm^%C)2KKeax<4YD54mReNiVKW?X9&W3ohU>rRuz#rnw*|M1 z>A2QK%hY%p>9jYe7fI!#8-}tn2Ez9GXTb=l7TjfkYSPVNHXL~k+90sacwq6t54XlW z*d+IalvfW?JTN3{sC}8Q)(8z4!~ACPMf`6!_juUm9)HrR2;1BxU>DzIv78D+v4|BO z^~N#Szv>*(~Rj@^z~}sXJ?af+MB#jiJMl+*YCCe@>LPjmL3*6i=kehCwMq z%v)WIdU$?~`hxNmhiz$31JLQMXKGsOTwIkZ+#t2Ud#b0;3Vir*K=OrvIK z`{L7%J9mnu@1FF-K!xQQL0+)&{H2%bdq^eJqISt<2FrRPV!_;saM@0u`!QR26ww*7 z198^pw4IB7M?veR8W$r$T!tYLzJ$Fs)KuBlY}!4tiRD zE~v&?dGES2BYeEsO9rwcCky1S1GnyKJg$2T@=(6_-Qa+%sI|TbSv|Lw%7N9NVJ5$Q z{aN3SIK>?|Ab31ViYhVdBm7HM56hG9QsT^EeWe-0NEY1g8E_5%2yfBcIVLYJ^hxYa190@F@H9vI#df`JYH zcFlkL)@=Yx3-W{w`kmH{K2n9TvWzn*|z?g7763Hp@~m+YJBZ z6aB@-|IbGj$^er=EhB;Uc{H5F(iP@MOTR)zvL}*7I^1hueO;NYiy{Z*!9maWs946D zLta&P#lMeA5p)U#S4`vEb?P~ur>W|x%|4m2>3N)6pi!$fQKUy(`mQ4q8JAJ&J7pp1 z7QNVlp`#q>!QA_`|NPO=xWH|AWY&%W`_V%p-yF&*t!mQ*a{2qZruu#UYssEyJE{6A zf$Qs8yXLq~SugGj^20eqV9aWM`jjZSB8GUT!Kl|ju3LA8828@ma{1{9o)?vz@!7?% zBcrWZ*N;iu9s^R2t{4f>V&6Lt$&Sw^XExTeP10{epv1G)MdOnf`}1*+0;Ou}J0!lH zw<0r1d|G)!?$gBQ0MxSS8VjrVMQMNpWQYWo9k+NS7!R+Q9JhsKKiXxh>zDk%954BI z+vA^~UojN6N!m(5M8SJQapFA0DjcR;F<2`2M6KBnSu&Y66Xn&6zW%|ys}PU#KDG<< zbeZ;}7`m7^o=U5(S`cEI6+g7?6{~JHk=~RB-2ioLc|8V17$*&#OFgF_Nq*y~Y~EP* zvfUb4zaQB{jM%!e+?&-&WVgu)ZWf!L)NuCs8MZ$@3Gzn<3TgGK#l+?lCyP-Dz?)KN zPoS=6axrz=HS0#g{5?8Ey@;SQt6uB%%AKjwsQYb`u5djhoGvQRMMDzG2ce~s^Q4&a zAntM0Y7IbKYm_b{sk{H_4!szWmPI3hUZhsqZ{9{0&J%r7Jhg5;0 zuz8U4ukdb2z5Q@mB&Dq7mD9q{YxniMN>=O3sgFF^iY_`J8IN^)zq);6wOCq@&t*^D zvtXrQ*ViN`n+%K%*+gb3?d%OsSag+i)w=~qP_A}yLJU6VelM%h3>@V5os3%Io$^LEi)Vo+56!!rEi9)_<ROb*Mk)22V!~mDsW~YkZ2PqfWxb9EFBN0Rq2GV8AQJn*mQp`Cl;AhO zazpfm&;8Mqr^1TjEGC=M zXqjS4WHD0;C+f1eJq|k0tErdZ$I1!DTF*DCIGN-Xsv$AAF#_I;t|oqMxYoRbrW;{A zb_LzNy}tADPPO_VqtbVG6Ug;x z?mg-bA`Wk#s}QA18{ zAo1KT#$h;_r(3)l9Cp)$OEMV}5jug_J+xaRnK8Xm;qL*M%k6lc6iuTmf9k4kQr{|6p^)0~P&~1$`8eUl+(YKZ+1d9Lpu|L}ppbMf&Elyke*J zGC)$)$K2;^=ImZ17j$Elgf}s$wq1x7EYcm_yzEY7FUMj~lJLGfind;CnzbCIO_dwE z;I%W0rq>h*2fag)MG}%#mL~xyZO3GqEZUC2q^H9#7r-7sg0EGiGrbPP36sTz$HzBB zgPjJdw+AqqnOLeD_$nE)$y$0Jul$5`%7NnUJeW|qnL#sZTGyuzWKjyhOiTh9ny5#ZZ@PN&x0`wW}ode4e^f`j4KHK{Kiobu8zH^TYoM!x$kus^6sD5<#jVEVOI+i zYVTR|@7Uex@@=$y%9A{;n{~R0u{cM;g#DF>9dJ^;!}V#g`=aniF)~Y$<+B~m-VefU z@K(0hV@KDQGgifkrt83i^1Cw*MXG%<tJiriT^l#o9y7ne~-D3Vf=G=H+7x?OExs|(8T zKFK8VJGrf{o=VXYDKX3XvTx4s4~C2)X_Zz2uISGAf=4k8aa#lwZ#_1A5uh%VGD%sY0|`~( zi$ahvsW`@vXqDEC7cB37(SPJ2{@@P)RDwSN)M0r4D>iEhADY5;x-5Cu@lBsPfN3Y^ zmMb*LBw*ZsCh??fx5xY$b~-sxFnyA#_`U8}X?LoQkdLwj9LV{`tG!XU9sYedOSHvQ zmpGafDv$*T1RP|Lm%r9LeN^WqWjq1&93M}GEa^=bn1@pB2-)c4-kK>4OKP?sb~-qJ zq0U;3RkfVA}mTV5_amMDIRALasz)rc2&`vAvmhU5rJIsj;~^*Yz`- zus!7Acy}p8w*_EQiAub)wa$=lYBNci%9Yd@h-j)U+G@ZgSzF!0EM#@t7s#9M!q95E zSg2=}N328pj2={}SSw{y>vZ*5_2);a2OG4C-YcqlW6!3w_Pe_au#C&3j&q4!1B<%-_ z`55f%MK#7sMR$l*12;3g?}B^o8?|*?Q-sG93&h|FY@ZoU?rxwo|C7vY{D|X2u8_{` znl`mR*O(uuo>Ss_LAkz13zeQH6AzOv`UA}DXqOx{X=%;WlBh5lrLFdRt&Ai7akPhe z`6XX;%dQ_`^3w7q=*0|6mwL;Utrj;%3M@oXGj*ft8D82=;=C}Y$$F4YIZW;RVBv0=P#M(r+9wVu>#(XfHi~Uc`8F@^K>e!mJr$+ ze<))$;KLiEJ#qduCi)zoYppci%iDj&u-8abxX-?5vD|D>Iz;Q4p9Zh>7cW+iyz?KX z?ps-W_IoPOn%yVG8naae!;TM@b?4ptjWdoTpD3o6aytsdykrw(iM!?kI5bUhKt^Yh z2{hQCmvF%W%p)GpU9kI4iZ-|&G+|Mz^q@?x4!>%N{n;5W{GTI3apK<6P(buj^RN6nmOX?kEr&b8fg`rvwYid^re^u~@?Yc#-)E1p}K z7rhTKD-KNQuZl#2V!q=s8Wb%t!5w6`*v_BKnQzmWbV1r_a8oq(3e}3IiA94#N3Q0h z;|gYav~l_vr&`Ze0#kmK*m1ccRfRv<$!KsNuZGz9Nx&^+ElnfpMH7>^qU$v#x%}!S zU=%4E&Gv~23h|0ID|pHiU?{2TnvZ1Va@C{CkjGPUp!w<_p17-p!}{kpkvytW$>*0x zM%^)+2|rU6$(~As1gYTo@zaoS=N-iyOR``lM4qgQ`FkgJy9OyQelw!arSVll%pj<>MgQ_EGy$u@iNtr7E3dD843q z8Uv_GoZWuD3QvYPVt*4lKO!2K6L7XYtO=@d(g*ka=*Xw|3uRMS!~ssr05q9NlcP~k zhzB`6jF3s;(90GUieYNjzkHKzfV|M3r&^r-Mi-5f0iaF2`0B-)`iwf4vL5`SA-P0- z*drvVY#cuOC|67V7k!!ZDuff1)bdvQ^nCEzzZPFIVi}V4M(1zXnlD3LfK+UCe=t`| z7Wg^(*~klE)e<9OG^F>sW4H^)Lk@pD&*u-wYu9;&Y}cX|OP>Hw*kRy;>h1Ex#m?R| zX@CqsO{5Lr(f+0I^XIpGf62fKQuuBfTtN(Afbj<&$|(&B2eO_VNkct5?jSc1Q2AA6 ze8k59J+c{})zbiYXY_kBuA`ghDImkqTxf0O*@ccq#e3nSjoyM7 zw#@R(J%Q6Ftr-&JO7d~dAdHYdvf(#k{y81Vq;#nf$Y2^{>2-?+kBo4wTcn3M7rmK4 zzFB$fxXC~X`UB^|fVpg6{)rcTG*gM(R?xmge)-L1v(p?Br3MYd9Xzj;`*rmM?zTa; z7p!VUT6AqeVU;~gaD;P9{M|1)14?Vl4{@xqLGZ&$vpAQd)lT~wd^sdj& zPG(H>&Gvib68PR8ulE<0mrN|PKaV}`$C?mW@|D8K60~J}!$Ui0-T9WLWfV9r3DP^}`a}pvnxIycZqOTJ!H>aX5q-~d z^?F@PC-}Xv8$6RE+7-NMiWiUcLhos)YC#2(@Tc%Z7k_om`k{gSmS^jaFs)@@2(}_M zZ|P8Fr2wRMcc*R4)fJ!LG~KhwI{u|A-sPprU;bhcc54UD44^d*O|#P#XW|jAW!FI2_KLDhFQNv3JPiO zZz0CV?Zawx9v1}RWa6RPljl($7yI4A->K-sc)O5gThxS>uXa}t&++wI%S>(*2ybtM zJiWdliy%|VC3PJin%Gq|je!KtLaY=d`+4>W(*?_Lr#gnR^R%xW*`W5D>()R51SP|@Yk5bL7&VwuD3ypaB-y|x_Z@{xTB<=SG zYHRv~e}%acB|mCwRax3WCLoG|NC9ZA)vAt?Fok+7Gj4a$MPP+^LZB(V?Oa^uo0CqZS|!NUeK3)($g3a~u+7HZ z9rwPx%8@ggw+TYo$~5%ni6rw-o33*XmWh8okMeWpJGxE_rUr=d+7Y-Hd<3IV2WQNzQz;W4=|-bQ9kY1#s_CC%B9&(SKik@Mpq#r~_ioGo7PyM=;U4-VoOzSLylOu@e+A zf>sg}?JfTcO-0hN1fgrC{umil?*f%(PSd;7WnBdcQHAaZ-(6NaWhJAj(_(@!Gl&E{ zWtLt(eewr375##$=*Mb!DOkd`N2%lqkpqkd4*fups@l6Gh#yOH#z2U0#mTQ>Bd@E& z<;PpYjvrvmxbe%97(Dy@9zmr)NVXcNtE( zD~ziFT#raUbs!5yR4)z~j01!!=4npR{Ee0c*L0N#pr#Ie9uzefv3?>Lb0vApq1)+P$3b~oW_iyl90 zF>@}$%j59Vy$hKN%YCwppUZ7ee%*CdtTI;QcCSYKH=1PwEHzqapC$qsEIB0%8ji2~ zjNOCOnm8S(xu1na=$xlkNY?!OeSeS3=ZFFnPAU+bsTz*$cV#`JTN_u8s-;;@jOMjt z!3Bg22Pj5yVp!YQ?)4AW#iZ3MCMv5h&#O<>=WAWS&L^!UCC?wcOieULTzM)Xoh$(RkxnQYB&gmqp}W92KHexn8Bo&vPQx;lnfKej*LowfYbF~kVX z^}Lb}P{Sp$ecJlwW1`=&r#BESMpO{f4BVPx=@aSf8eZ^ zsFwjj-+tqOu4dGP>?djx6#s0d>;yBMN5Xw!y#6KSBAHr`&nh4k^6k598!ZwPhz~{N$;0ATutS+a_ ze^?fuf^qRa#xDgv(R1951*}a1D1L0XJ!X@aL^CN|EO{f4r`k{JE(s~m%izOlsdL?- zuC!Epra=6JNg6EG)!VCZgbps&+C-^KPxK;#PzV@yChQz?tr$JM5+%RCb=GarCP7_Z z{0DT?9c{rT`<{j5NgtG#&D<1vn_U!bNpv8@g54NbX}DtVGjn6%VipnGHMur*@h zV`Kwpp4WC0U5oBN_({L&R}&yD(0W5=xs zhw*kU@9}}tVZ#Q!6-4`12?Wg0rZ5h=U3aX{oZc03F2pS4;8MMi{xVsR{W8eP% zO{4N%X@=BUmb9aW{9zHhcdH8j1b;opR%2IY`0C2;^a&4nE*ZC29H{GxYOGJ$vx18B z2jyU@{pXAah#n8Yzz}&5rV2dK^jF}ujv4;SCz*tW2pQV3`F?E#c~?ab4!>$r`5+!N zePfk0c#wWD#>H?ZezZk+6)%g(4*-(dSC>AZ5_;wjpFlofE{KIvB9A~u*NCI$$8;`LqYusQhASF)F_F3(q zI>(dxlQ9y90YcW8@f31N5@jZL3ibh$*qshjImmj<<)dQrr5!oGO?>g|Qws`UC~D z>y;J4$Z^^IA87s7U+MU7_*8cV7Xl6+EvC98pCsx6|`5$Zj?_YcXcQBTug$(u!Z~&Al z6hbc*;zFlZt_!F*w%Aa$GaF2fl1ui0z{rlj>RI&v`*#u8Ukb-C2CS{GxfpnD{(8DW z^*`Q#|NQ|3On^lMBC2sn0b`IIHp199V_bnt_iaZWCE-xez5f1fXoQ~e?RemF+O;~U)X)^CI`C<5C1zv;_(vr)TxwJz zfoRauq1XQ@>i6#C6Aod6@y{KR)QTc7fLTnc|>b{K=( znVCmyk5bK73FmsRS624B_Pw&!bw%|bXdzk?95H|u>c7GEm+{gOzh1vL(k6G;{c2j~ zNubh8RjbOJ^b-c=yWHjklvq z>-s9Ke54W_#*G)D43Aj23#@CmkAGHLT_*|t{k@9#2qUh{%eYj)=-rh2S5RX^>)${P zVkNH!&w4vbz4urltIq@tXJZR_Gh9=I4W|iaLk-}b6HbwUoM!|o;{a4JBXQK2^f$;` ziqBuX<{RrkAgR6UI+ig5Xay+8*gR0Po3CSd^Eu`u&Q_iagcRUN)Gz1cUQkdTSL^e; zso9A#fZz9G!9%@{_Thp*Xx-ByD&7kS(zd2iiLthw;$}*k$mZqu?LNQn!eG3Mk+*Tz zub~BX;1R6a<$y*u;cA7v*V3qxtuyQ?^@jm*Lm;RP&gf{`FtzXDV2!hcHBrByUj_no^R0dc=P5R$i&uwi}SFmMdoiOl`jHp z*oo8#HvMykywCG zvamya@L9nQV91dG)U|9P8cd=q-|U;*9KL%h65xI&5UI^+*M0i3eEmZ3g*!D*AtI*f zP(ZrnTn-eQr^2%%(cx_S0R4%$v3u6&dRH zQvGJ6oTYS;p5Qpq3s?24{cRciljnNAZLmJZVjw48co#CUq>Pt*mP?2JABM7I7o>@ z4YO4Tk)RWyMjB*U70rX_Z@Ys?5M#hd6%0ANU>U%J%^`qBa%v~0&pLpT1UXzvTkAjA zTyF8i+WoUt+C0$?XwwH{a@Gfu>$P$oMW_#x29m$|%(*su;5|fS1|<8{VnPh!p$}7! z{?X`+ZCQDuAiH6Ee&AI&lLKNTOqYR-BFoZoMR44^TA#7H z$2A18zOJs=HFtMo>U&^$bc7MfgToG0`3dI+&vtyYjmjMzIpvg@K`r?4KCfNxPF`~J z?<;Y+J0~sg%jM|ARa1vwLyFU-j$t%->13^svt+KaY}>wK8__GK=iD+`U~s4HpMY%k z@$O@iG$d?B;_gVw81*q7gMNH&2c74HJS)Y76B?iZXdRkZ->Y6^365d}b&8|Sx3r+C z*`!FD62+J9{O-u^;Z>^B#%rs!=76OFqR}S}1C6jsjjhd5B~af@lin_}+Ia~<`)r<; z(*Vip_j=bzCT4lQt2xF;83XK8WRdS8Vyeye;(nVpzwiCCYI<1|(*xWKv!&zS-h-81 z4tlHQeRozM{ZbzH?%FAiR+aYGZYn~R46nuk=tsyJr&WQbAt5$r1l%t2HtnskI!XF; zk=*2Fd&N+Een#N#mCQJ}`c+2py2?S6`6m~>et#D-a6DMxw+dq(^sBD}4A!tnEUG{k z`GxA#kAF5bGkU>RKDeO1Ji@5kM4F>7#?OS7o6K`o4#h2xtKDI07;QkehSUqrUJ#WN zWl4W~yGM0J;ZiW=paUVkzW#Q=*CdMaYL{**g^iX4TP%($I-9Q`aHtAYoLLro!kVP% zd0&~d)c>5)pDgbE9vrqdDEK?lkgT&u-f}P$0VnBhdfrXFSSNh}odz6u;m1%u;eW&V zCJ@QQc<94I9V$YV!#WMEX1Vc%o+#K3MVt8SxrUgdrY0SY2WdZz-v^rQa$@&j9`uzQ zCw^@9fgvb>G17rK7jJQ}P_=8iK~KAFmVy6gN4whqE4(K@e;-4VqJj{IbWrDg2G|h? zQ-oA7tvf<)%KqqmmeQOWs79=#@#)b&U=vq#%=Rz1@`23SR0#b>*7J3;vB8W{L8#d~ zlXkISE2GvUf4VC}w(x-22;z)I%SWrela%!>t1tUrUL&?Z0Xul9h-efi z6pe6gFhw1)CDdFI^%{_a>9@JpphP{TF=zHSUZoUShd=Qs4lGY}kDLc!!p@jVGXDDG_ ziXz=nGv-(@ZX0)sr>Tjd&uqEX_g;Z7cNLyjBaEw^gj)do^#mO?)aEze`!T!KI+#u# zJS$&Ze#!5-7SE2q+xrEF$q{|RyX#DdZ9#Jdl!lr-}$GbMS?>%%|{&(10B&{9e`& z*V+)@jZ+lPyt;7KMXGq=@g5M~ptYb48Wq~_TDAuD#Mfuz?<8HXNM!SsYCTJUBbM`y zX{O|TJ`UHbh*M5|A1buE(f=^uNnithIpcj7Y`}-W20Z-Jwfci524ul+1Z%Wr14&Vd zMC=tk2lI{H7+&#WZj~)Ry{~(niK%kY?2e`M3=ZsS0egtbUhU`s|9?HkHV-kr993}|3AjQ0xHU`ZC43F zlu!f&r3{o3m6C4hP+%x2>FzF3N)ZsG8zh8b=pIT@x`&2gL~^J>Y9t2Ep7)D${iH*=EcrCp<1R z?x%5C`pJUDrQC2MF<}z@{sy`yoFU;Rm+VOG*dMwLr~(s&#Ub6L*{!=<)32?X;_^Ay zxgsKHA0#tc8*aRzGXC;fSS=nW6KctA&?2ms6)7dU^S#pQW!0+-6?PLw=;`J%?Qb(U z`q$~c9(H=0M4pwhVs>T@gZlr>vJKY$h>yyR)ypP+qsNkasrEHo!mkkrjt1!?!BEZe zB^$1-rcfrK>fWsDGkVNS7wkrDf2dF-*nxb4oRLR2@Ga)w|B1k-`@YlN{W1*tZCQJQ zVW6l=2~JA!_i=+qsDnhol6I0W>lz34dM|bgRI^}W8`+Kv9s7BXo2hYHp?z))-yqv1 z=Gb(tDu@i@!0IRcG506znk1~{p6onwR8QM(=(63t!~62Ow|wx#gX-{@of<7<(#uW( z?ImbgODwJUCh`2<)5|yRZ$Bk^lYG&%!{1-Tc95pvYbW9!hpCP}9w16w$$3R&Fi?@p zNZLg;a{1QX2#Jkx;IG_#%VsrT|JYo^IEgCiX*oXVaOp}(0zU+#lCT4S&cEjUb$+8u z^^NY1WnxmMjz3)3yyjjWY&8qZCwE@#X^+y}z7RW>z^QYmhK$u_Ba>_elC8n+Rultr zz~bUK+;52n24Imfm4WSSj7+7#RsK9%(g<2|<-+{wQwrw1l{WKR@hRukztyqKHR~Kg<{r&s7&=; zf71`!<|G(rs&)Ss_m;@(7fq2r(05&OWraiXnlm*?En4S69?6$WGl{CuY6Zd*7s}qm zvN*4~f%^eR?o|gD3rU)R^3(a(^qYXS$qMS}8QQPZ)0+cG_+y`)%X%#@qXJJ^MtL*4 z`@|cZP)Tuk)0u=Z6J3lq)pqa|d%Tv2S6;x5`!>1X^i(pU&yr<4^+o|Y7q9>tWNV;liM@V^kM|s_Z-O!702HE;SV9VBh zapk!yFv#rm1K;cUB`L}rNwe=eyY)nK%wvu>trf%&>KZXu49jU|aZ`u@61nowM%Kf$ zuRy3YC?I`fM(zs=x}8sc)#%MK<$S~ zN8UjFGcNCPM{+16rifB2f`{~+5^y{kBv=*na^x}fZJDm&gE6HN2YzEnm;zaZAB~sD z8F2S~)wV5r zURX5GrV7xvBRz#h4c9_kPXJ>Z5T&7YNj%MB3B(p%)jt+UvQ))2B?tlaisE=Hs%krk z3g$4~r0T4W@;`2t{4t~!+9(3jarJ6^GsXfH)Inr9NdEkFGvqdvko)?Gk?q7mnwvUv z6R;1I5POvseMqoLsQ(E{_1Bb}z2ax1S2g{CuAPD|TrE|=hQ&d@ZU{$zWwI~ko?`Td zo(IEM9>`}cB>NN8P*fUQfcB+19)FPO-OzFbOfp8%9~$!y`6y5 zoFh4R(5a~KESqdlIcTWYwu_!lnvw~N-NzTqDP^La^urKIR-ty8@=%wrHw;DhMNP=1 zBHnsLYzL4wTx2EhJN0k=higo_Fs;Xda86`0^dAfz-kY#;fUrOGBn+ zFES-a8vwUqw;8&^^CI4MEGty522eyiWL&H^K^!frkf+ zxlWV@jDL9W+0UTa@=fG*+7vKRJyEDC(?@K#S^|E_#TFIx(28{I;_!X@llm*q>oo!c zqM0|s1CGgKf%8)@S7Spv0XkYc$9tb-o=di|gQe-lqmkAmxsRCsM_`9`RtM)c(uK6x z$fF+EL~HPk>eow+PtSa1#$%*u{PVV`@Gqbkb7LWc2Cprfh7V1fd~+&*u&}58QG1D}+wz0l&=+j1fY4fd)HQ#7gOi_*my=g+aSJYK$h2vPLWMx*NBiyEw*CZr!p z%U+z<(;zY+Z3v^X?x0{6Vk7lg<%6$KF4CJqBwxb-H=*>gW8Xv5lTxz4!5g*XFxGd~ zCE5M>iretl!VC%tYjy0w?1SW0gpQBJ^v{)qZuA(CXue|N^?YVwvN%^J29c z2bT@*s0Oyk)O?}qU=v~Bc7dCF#RyPRf}DTMwW``WJ8HsL-L)1yl)u?i>mNQzF_Pv zg)7&nV%c76*^ET8G%HZcNlLxDH6r`Y--n0I+|nx1!pk!Si8GE7=~i|9coN6>Bz&dG z>-Fk2e^ZC8?EqpV;i#S|U=MnERd6|TnyA>82 zEzo3#DwI5X%7qKB;0&orRFYDLCd;5!+J5 z7yYSn$CNut@m=mtbCs$)x~AOlMop0Bv$Q=Z-$eOKmr~h8nR3Wh4bY*yokyt@&jm{i ztG=ZO_Ze!-w=**Alp9oBU0NEH7)%*chZQYVik+3{9negU{7{{)rd0a%Qm&ux17)H5 zN_sn!y2M!F>3S5>aNfw1gzYQCq-%J|-ZwCELr}YXj~V^(uAe0BgWn?!TarYzPoo*= zK49<7Dx5KXKF<%^$V1#7IB|{Mm3m{J_gZnKKT3t-=$*F1&v{qyy$nROOym&)6Aq3?{SJF4k2W5sM$ce2nQ2 zvN(FL7g*LV?&U3xq^X_NY!0(1*|$ql{Q0e=HOp?IhT|sVWxe?>9Bv^%97C0C80?h8 z&yQ{&$7BoOc7MK^|AaHjgxsF34G8D$&qXm}TxDbl3=Vgj?;ojk96R}rBtR#QsvCNh zT|FtmTH$?V$J!Lkjm2b9&`qP*zuuVCuXaup-9uqQGG$~IjP(%2%o2fLRiaiY4m!Zc1GOG4Mo3aH9(B`ewqIZBO%TYrWH@d^ z%1^rF$T(dWm&y-qg!ffOYgQe=vY$pol}49esGS}AzPJ5|IV$NQ^*xc>9zk^l>NTI8=2cpbz4ZIoB;j4}|7e%f=e`4jo(VDe21_!yF@^4_5|~TR(m+Ig z*YAsU1BDAV&N&{?8JeNMGaMzmUSphFbOcvDA+;&FeSeCW>SGJvZ$b{iukWURPzS)F z#Z;V+n)7XqV3=f+!~k))nG{M9_BEbGieP0mr~PS9HQHEs9qamK^V7t<;l3Inwf+~F z{+N<0;z+%a7UAVAd6xOS^4li+iMuc2X_SF5;udW{{)fwftyyk%!nhZw1mDv=u+m`J z@ojw0k%v3{_WW3FiyStpDMs*Dwn(g{d3c$~W+6Msd*up6YG&Rg*r{0WO<83=)+}zw zf{K*qn{tyoO!ez5v5;@qI88j?S?sn@oflGN3p{FQ_LJnWZ%!7hdrvRcN3Ti8p;L5Q z`1{N3nfEqXJqs}smSJNU)Yr23+LrLLcWP7m&H8uQwJVcPJa@DrBq;aJWu&SRM$PqS z-WrA*PasheM@Xz=OiQJCx9e_z5VDarz-d!pD8;tF($xhOsi6!%IYYAphM{z@ld1O>a|{klNvgN_^yYIXNxOVE zaH#a{S$gvGA`1}D(C(pt)uO|^Q}$&xsM~sokpqs%42@K#LhTQHqt(Fmh(}WQ+E+D7l@BR)AxYc|5H-=3 z8Tl~qDdn;6>}!!%g*5YgvnimqtWpPFbJ0M-O%xAK?RTGV>DJIENKSA1(zA*dhZ3`& zAn8SnnwexZcGeo*eF&$pg8hh8xK+*$i!(j z6>8OBf9<0w?9pCPM|q07*;UXMgamG{YSlyhKo@c^{j&&g@1^0BO*TaAmRI7wH_F4& z*hOGxg}oZ?eB539J3)AV6LW>pE`H1@h-9jBwb1Lc8mG3+_u?*VhRcXo$;dsdRx2|S zMcfDr2zRXX-52%~+$}mTsp6pY4U(v{K2!rw)dnFlm>aI11!c6aIi6+J#_h^O?=@f> z{12UW4O2KQl!nWrMbfQE!Z#|u9v6L?qUB~#{wezw=GM9Yq_u_m?9>n8d(Z#u&?*aY zJV;9}*7z z3!&Z|5W!{SKHcvbnND>b!(lJNv?l6pIExH{#^fnHt19do6auwsn$>==?|)=fpjLDI zjr#1;qbPKKZ&JSe;3?~z-mx%dS+gBcmcVV%!i#}p#3^s%9y~8J8~13jQEjL1S}C5m zPJX!5T=kkN)Ra+1NQLQ%(IA?;(FM*-9jFUUGqe1o?6G7!g@i{X}*LXMxN%nT; zxW66M-Du^VXTp#(OpcgzXQWWfqb<=hQv{oIv2rSyd$X$W)#Y!;pB6$3ZN6vzVvgB);=VF~9NFd`q)O_f zc-C|kk_9GDIdV)+B^9<#Fp#;D@NYV7sbkijZ@q(T;rzMnNI%IIy|dID=So@XO{wD? zKb%G(lUBap8t@AC+84nFwj?4GI`({TH0cS-W_KMw0f$_j%|3jps=zyBx+$h&!sBQ_ zF-w(SbFQJ#w7SmjIvSx~sPO4&*^J>pVQUdr5_ih_m8}um*IR2L1Lw}`c4deZzCX5> z{e0Wz33}}3l1e$kuQNyHfu0mHN|qi_qpOCxtkg3r-{C1+@SjpRSoyMKKxVxN+> zay4z~*{v%piw#f+l8^x`PpW`_lrnrL9|cqyhTjQckx3d4UNqeg4-$T6mO-{GIZmxuVDp?o5KPbPX>!W$tMn2B$f4_iGPCj0oJI@@&81bsELVCMDga~_tAoO=Vg=?9nd(-i8PW4fGm>c2P5_q)=2_%Z#H zE;nB>WI^k(zV<*Sh5GTv0T!iW{ySorP1hQWE`*dLl%8#Ih;D5jREoEkEpvT1PZ&OF zSZVuxB93c{aGdEIGunA|*4U zE5CnH@1XP8E6vfc*7xg}fgOuov4TBBfY-*P!y#n?AoKm|i((rHLQp}=eMb#%vC?l&kF4K-d~@;$kM_G8x$XqaEjnl7oMMd&QxQs+<(c%Vr zPFkccOxaamlW2`kRXE{ChCZRc4pmz6%S;T z_M$uk!lvH$ej48uu@BumcpEm9n=7}7d)u{-_CbjL&Xy;=e``T*yrOpS6XdfTv=hBM z+@tSh%kX+=&2mtDH{N5`W~#xiE**?%Qwx_n^dw&wJ)5nvoR%e*vN7RkQYC!Nz)~f{ z0c2{A5gtc3)CXZGLk9cNB4b>rGLA7w5H|+Zj#Gnp_c#Kx3Z?G3ZYBh(Q0;Azrm8KC zOh(uvE|^ZtgXg6$Y^tCaR3fi&Q?HqBwL$h%%TQxkga>U$vzZlh6ILZy$l1*)Q|%VS zE4}g>phN2JS-O!@@(JCAW32cJiQFAslbzI>=Y6s~9+TC#ruMfnHHmz)r3t>PDi-mj^LX zfp6`#lXE3OxJzk2m4pR3e1Xpa!WTp}jl>0QM>VJL3FoP;B z_u@N2+-x|R%2vPk?6^qUfvYe_>dbNcBi`(5=uNS7ohUb7GD&SS&vxuxU>R<J3Hy^;AKBxMCxAm2RxCb>WcjT%lC`P%7xNHS^cvYa{9UDK@Em zmc}ZjglMcj(*=Y_$-TQ4?=@XhO<<#w?>g4#H7Rmrzl^!|Y`5g)+A%f1Z#wbc-~|!p zpbd+?+_w!5*-u0--Atf~`;;3D=OU=@IYwCdF0MWUrmwnAiPG@Nx6#>GChJW@S4UsY z17wdf5%|$VRy3leSfZ?E2s zi|O!Zp)62i{CU+~zdZpu-qrPNLE3(Owg|RZQLOqs>c*hiOw& z^Qu*o2varR^S3)zt^z>Gu1UEu5K%>3A*c{4r@763lU3|?;{;!&s3{K;B>%adH%d1zE;Byq$Sd|8w=m{w(mI-A9|E{k7 zA9!DgD!Bg1^htp7QE7nJXd?NKVBr6~z<>OUiWU6dD?b-Zz}vO}m;7>yaK-a)c8mXU z>q)E60h}cAF%xLP@%R4GA==gHfAcG4gTW8pmt4LER*D(XOMMf&p|&|NhSm{83KsC$ z%|V_Qs@=sMc_cj<4~nGa2HH=F_m2i2+!BntqxiB2%;9+f_f%aTFu1Q%W)#`wd^1w= zGXWU;9`|;kQ{m=1kIjtNM%)3$y=Gh<#G9rp9qq3_&v~v2nl6k+XGBkwo2B|kFe#=t zG?FggKa^plz{9ObqoriF`g9$+A_n-Q04*c65(yo5zo3^mLc;$5jr|*xcv%SE$>iZ| zgB=kSX_lnQC4252dBtym#EM7jeg_)~f((3@n6)m8tW~LZ_P7m- znQhsQ^E5>uvq!j{vp;9a`7TL%|O zdkidXBH1~`sP~c&S&FmX!Ny>k@{QeL@05owt`A>Www1EVN83LyUWB^-`gxb0|7hV= z)QxFLFSmi!gVPXCl>Oi0gZK~OWeIry*N?3m9)ZO}6r4;unJQrMTJ&&n;c2lWu5NKy zg+4#1mB}(-vyptH)-Xvy<9(pagLDZ^roi8jatCxJ%}9*_X4Q+jV*A}pjRUKPtq!(+ z*Aa6NdsjKhZ%?j6X&us^7uc-N8a7vH0p|l=4%t??Ob}&js@Rx2qqx6p@z`gc-o!qB zn(b=d!AQA9;4k-Jc2!Rjbkt=9CW?n+tww!1T47Bnbt9kr4-EyG%_JcV8X$Z;{~EB$ z>?{UKjpPMrsAY-Y2GXv0B)m1r5%NQ|HM}MHP&?unu|3^Oywe>C32h694WU7qW{xP} zu0+H8#W{c2{CGS?Na_9lQv$Kq<{S`qSjfSSZAP~}d^Re$u1W;@Tctm?fOfI3r{ZcZ zqnQ)snfn(j4tG#Ny+FAscH|EW!KM#y;(mEx_Wrn=JTd~$_4}Z2vn&LpL>epw#I;WY zp2+Sj_1cAywLaH>Y!#?gof=_!6RSFhug_ z4#IRwwdTlhO$#nxkc|exu)DF$lOycs{oFB&E{=v50frad6OC+i2FoAwzO)H}dpJtBDP`p`x+GZq6uCkQV zK$!#uBhL|BxfWp)3^NjIRA_L`k9wzOQJ~qyZ7hH)iD49cCQdKG{`%#kg>5?5r4Pa$ zwq2pSbmo2XSt0t*?pe;d^fg4<4@z)mshxB+$qXt(wvTIy9a3a;JLP(8_f>V=GX9{TJ8f?YeHMXeCtp#&~ zi^0em5qJpgYgf-o+OW$wje@~si$;MG12%BDxjG%_j-yRm6G``a6xQ<&esP_i<-rZ)7C^Xf|Z;SJHofjDhXd(@KPhQ}FBaVaN! zB-M035a53Zq0pKB36E)!<$(;%%)m88MXRXYl?FH0A3s>9>M*bN-`_|@|CGLzKWR4G zUl^uDM#sSdw*6QVrg~$(@+yZJwFT`OCE}FF`@mz`!=aT)!f{SDptJht5Gv^`k-9bG8u`%D;92g?qhI$lxs~gZ?i*>16e!*{sC5v; z+Fm%sbb_H@8QK8G$C!e1N@`^Qw`70GfmQC^;}vhiU!Pf^oianP2K3`i24M`5D$K)50oi!5qBsAn{v+V#n2@xV?|Lt$x$9!LSm&$Gv?#;=7-_@-^?T zVe%kt2rKit8-k7sc(SgErn1BknyLlWS7cI3f@~=#56#K#d7<$QVp?rRhS$*4YR!is zVYN5wq|_-;A6Sx zS&)M|+fr`~Hs{c`#DZ#1j(JXfYx8OdB)1AC3QXz9-^cUlbToH!!Q1H=R!3~j7}VSZ zGlW&47edE-nj!ka9`@_c-geFz6W(b)x*X4MH$G#y>^pm&7MV>mSRHk2T@OZq_X4Dv zyq$)_70Qu{OaYA9WB*CN+=-1cLr}#S^~7oJUj-m=2!0i>*k6N`dW;Wfc*oMfvGxt z3~Hb`UG3-yK?cLhEY%a_c`d5FsBx#msJrk0zGcGhv=w4M?UtD)?bKG}fF5RM#n3F5 z@k)`_a$wdhihkg26D^w_&0J>PSA*%j0M!iP1if*;-ZNJh&Uo*!lJJ8C$3gmG?7eC` z(;I)RnkCN>k=hfVi)E#fWEm&@R^2v^6nz5o<+a$kXDTi$^APy0y$+1wz7^V=97TZI z1W6jZq`LKj6wHsFif27;caJ)9j#%tXnrIgKR(V0fcEaI)I0eqU*5S~xhMLR;Q-a9i z2Q;!Mpc}mE^q7S0^HkDK^YsGJ`J#_~7l){hPh7@}owY`SLOjWr>YY;wv2PyxCMPIB3Is1jqE66($q@1%+ zi_WO}= zqKrYBfP;%?k$z3CJK9}jd%=ENe>-rZS->x37%=ZUObd?V{0}FA3O7c^D5$DP{(g^{ z*|k#Z-=!W<%K>^1CCLniA-GMwI_D3=W?$9nqpTK#G(ni_oE^ExGLJ^z!b9@xQoWi< zAIfHI16?pT4mVqG+R(rjFkv_9D`PqEyRTtu*i8IUjznSm;Ok>X3T1`9!H4d*|JWuG zMS=s&#OLY@60k{-1W(?a99h|&kH7-MFAFNNlLC>jRnWYKU zv8iEWROj5&s*Cne53Y4varA*Ia`I-(wVgK05;9HmDY*P-qa4Z8yP?_NB|bIzs2yf9 z0Yjf|C_M)!byJyk=kU3yU(|I4_HH^PBM$mFdFZ*N?vKhx2Z0l2@1Ba3YXc4zN}!{; zqTfttFemj{uWGrcO{!hx6%?rBa}%QZHgd)ZpM!ThZ5v~IYY=wLyoZ|SrZE|gX^+ejFEbtGJP5O%FDj>b`2oI~ENYW3YNyJ* z>S#OFpsemSA*RQ)XT;2YV0&d{Hj1IHydY_Ivt;Jdkso&XhKEIs_FKmWk4<&)U-c?! z!e8b#yllI9T9fTnhf}7e)Z2|1jz&Cr2n>W7&5k{}`wwGo3UPoW3SOFy>X)gt)c4es z8p!ITqYBywDX}6dCcTtbk$|-m3NsjYd@mX=>Wg z%JIE-43iZzMVf{AeRkTL+Pe+t85$ym|DZf`KOe01?(BT^+)!i_5f*SQg*U@(()9!1{gZF~$6ZgXbn7p*y&!E_T zp{_W=hoqJxo`+Mc5tVtWE@Ky&~hL;ZCP zXgvDT&T53JzohLiZZ12eEVZ7s+~T3*U|r~l{xHk4$QR&wx9o7Yg(@JtI^d+ca6l_` zt2{F7kb&RPrc{~GpvqG1AbHFACl~6)=HTZ1dVb;@Bm}01KWd`C5W(BDPX>W+`waDE#&zr~wWVZ_*D_rmS?W54A$S3ZIIfhW&! zhW2^k!R7}@N~QMVD4I7X^U!gCeofN;kHs<-DFpyFzWlmbPX?-GCV-BJ5*oC8Ro()l zt&7#RHW`*}hPrrle+E)F6-psq4UXB>6))9e-E@Xi`D}{c-xM2|+MswgMdMiQ8zaV@ z60J8*mJmvt>`Wr&kF3g6DHKQeRS}8;1KtY`Na@mx@a7*B*;Dhkk+gGdiD4^yOq;vY zD-{wv#-|1cTSy*EvxG|YO(9D!`BmQPB7(8qvetp#X%GC!1R|K<#6R$3L4XzbM>sI#ly8)=~c%{ zNkz8`2SYB;$$FS52RZF!k}U>xH!k0(93_(kmeX%7eSSwp+LhJ%iO6?JB__p7Q|qVV z&4oTEzQfOfKr<);*%`n$TeMwp!y|PRn8#Cav#p({1P1ywJTq>CJ@GciU0Feymmalv z)}soC2n>J?mp53`@54f;dY8w#p^`l@F+zWAb>mwK27?Z{ZQWmJvS)-sGkmPs{tQ|SS_5;fT3jMRYGeb7^#>d;T6`} zFMjMHESW(&HouMLUbsHqI?nWAVevidiiW=532J&EAbGXISvc4Or>K~;Cs@0(!c1z6{ z*S*Q}61WW6Wv%T-!)5Lmw=^wNiB#8m?!O#Nb zCvRuj%lUt{$Cv}UC8NT_bbXmW8rl7^Fb{N~m5%+-tr(PG`UgEw3$f_ogxc}yG>`Rh z_ahNKO^UU>fI04Ba1gCf7VHNGz?DJSoS|%sto>MG+&Zj>t;OW}Oro!Oe()$aXtFD5<+ySh!rm4JEfk}gG>br-T z)--v|#}vu7&*wy}NoCrvPlLpAHW2CRN+1jfm!x(H=0`IpIPk2U5ZCTa_NPJc9c|yk zmV!5<!pzw0UUNF97Zo zd2PPxaW;Rb@iSiX#db78XvO(LHSOrXl#1i)iZ4~`bW81Re8gv}EK^my@_GGCu%iG# z1A>=ScJ|GEr;-mJ#c`^Ox#-&>yf3bI} z6J4lQSJXMvkAg2!TydDHG)xt6GGo4KlXavvTK83YqSQDE+|lEb6B?U)lomhq`Jp`` zn+8ID8`HREKI_R@JujX+D#=4+zwWvOQr7y{O&9eouczdxetb&Hle&4?H2nM~1r}3L zhPv%#z4QAmDi6S*2RTexrsV3}29I?B{JP{)(n>?#VoTa@d}&<>uvv9D;N46Fj()}0 zzc2RXkOV!s!wIvIviZ|-dyW9OmC9*Y6vCv=1?#f@<58mwm^&%US4T;)DLnd@gb-f- zcM?L`v42Yl3xha~ORM=a=GK=P+!>_FarFiyoGi}~WbeEM84E2p1)JeACGa;)p!5PF zLU$^R(laD#^9~K?Z0V>wsMDSo8r^nlImW1S*g?2G2XLo9=5rCm;^wLuwI-)3_ehr+ z`6@Umi~1*BsLfA83=)dmn|cy@l-D73oZ0wX`+}bdZgI{JCeL$_Z=kh80N}-U8(mXv zJ2je8tRa#FvRN~Pv7{~qAj+`YECyN1XNfUUkF_paFANYjjJ6KtZeD(F!2w2w2BDq7 zaIWik9I2{Af|g5{3*S8A)y&;j>%O{Amm$0aY-dL<#?ky!JLvd$GYhJ7Hz@e!L2;)Q zI4k*73Rxw*5XGkYVjD(+u-w|8X@_wvUtlaBj0T5HZ(B_7@nk3?erOk}IpjO%5EQ5r z8Zub!l&%$MGLFN}5`EY}E?go#;@;bqH=b3kX&WA3F<>#VThv``kmbG<^MfSKz9Rk? zkO8tFoZo`+@$_6v7zyr>3;y$?kf&-5KvhQA@ z0c+uzRJ8oL;K@5+3pO`V&T74I^`~x7_Uf4@0xz$P2n(rxKJXr$rPSz7`4&$0amx*N zMDEsAW2XZ%QCA1ENvgy`4!N|8llL&|^uLFP;vv#KC)RAdxZL9=r#gEc(9Zn2F4KOK zA#B7s3C?dj`*r17Fuu5ae_dPw#1uJts()Z<>1>|*e+7FD{MrVww}>rPf1GY z5uNsLxt=g_#^$G-PZy%*YQ4y@DGrtVT;by_kFc@x8RN=wuLDGYb%GM+;^#yIDNv|TI|;oANR=Y0+#LG z@6W8?Xi!}EAq^rkWd=0g@SvZMk*9|U^7B}Yd#V(24z?~b&iDRq35Oq6sP$4~49>5l zclu{X)WZACQUH(X!3CTQG|lIV-(%Ydu_)oTqoS3HvK+UG%;jd=@w}YTGUFK^D-SD} zz~}jnfMd#JRL0hLb%`g{7}LgTg>jy3jW38Lbp(*;l?e6yF%uZBImc|gbY^JQfThFg z@_!aQ|LZ(be$_@>b{?-4@&v_S3pfy8Gx($NQni-+89sMjFjEtQlrA}jKK=w?5=T&z zW)~<7SE|w6!Jgh0zkUbID;Y$)k~~fyIHLuQMII0Xv%PiDP^ukS?Hh}1f?7}E=CT;r zj;uGlV;nJL(o8p+As7BXiQ4ppBttqTwJJ&NDcOjkkme3;Aa zi})?lIlmQ1l{_%?0|g$Ze_k*Y08k>q++vc0Ia0NSS%D)$ryEleG9fS1qr_>SM%R9| z_~;nkyVpFXWU%$)T}->m{=JNm0mpw!v>4V_z>3K;E8NQ2a;v)m2X16-C}i)6{kL%D z?#01EzddAc!ZT)ySpUPFxnEcJyc)qkd9{c#74%iet1`QY?R-BLa*Y~?iSIrWfPb04 zlIe zTV~vRFJJwV@cb_9gMI4^>y)HgKEoH~1?NBAHI`+-l)e)hj1vJzO&1+Y`4au)$aajH zae<~4(#u%7!TDG2%~N#5-s+UPn(jP3+$IzXmgr@EzMQdyqXR!Ld zV7d<_m9~DO@A!2EDZ8(8+mK8Ti5h`+b%V@CZi{25w${u z4YbwzBmb0lHTnV$z+3T1S33ttbhUGOn|}dJdj#~u)H^QN^~x;?o1eNdWHM`yZTC7Y zi={-Ky2B#fA z$x|dG8TFWzg`wTLo|xO*O5(XZioG|{(HMxelzBOaOD3#`Tc_yER}H+dQg~>{zImu z%z0qFl=<17;j|}n(}&u2x;CKjWDhZDD&8E{OGeYfqyLDln&nwCNmF|?gDn0K?*?+N z6X#X=DY#cqgw3Qsc`PfgJ&sq$^ANx@%h!#M>yN9~h7K>$E&fBV^osK%zoGH}qgPrS z%E_9A%X{l(N%?TPSDGC^I9!@AhM}>pdh;pOY~G&`5?fW$`uaW_Y7SqTQsU-P6E$W) zlyaWlZmpN_9hMpXp*+pUYokuSMj5rcYN~~Z(KW-r6iiNq7AvJl%T%}sqZdz|M7%)M zi<;Nc!WJeE1v3~P2A$INUn{()mLC5`;wBlwuMA__lC;x9J$|jguLZ>kGE=GoTT3tO z!k$Lj8Pxo=Q%u}A?*@azSW4i&FRJU$Ke+m*dOm^zw9C2enm-w?NV4b>z4SBT^gCFQ zgO-VIx8as!OAN5EzLYu*3g*+P;ur3sx=hp)SH2O@^ICoYGTR7$23 zizTNfI`0uwNJ+N4p|~DDho7&R*w9o1#%`5_O!muU##Of-b`0bpobGo_4cZYK1cBEPPS(Jw3HIe zWU{_{Q^Vq#Awi+kAL+GlydvONdV?}pQSb5Mqrap)WRHV3{|QB$I21@k5Ycj{-v!&= zr3o63^*J3#nGs#7S!Xo;1D~D69ls6Zpp2y4??$`u403P`H4q3huK)OgTby@C=Q%b% zbv^m!6zeDbRQ`__0>xj*(PD!WxR4n9h1e`5fSChI=}%l_h8Tr>wHnVLs5PfgHD7s!rn;e?4)W{W~tbO3`lu!hinQ z!FPkN(d z1&-sU3!vkWoCMurDDs`y|3;=F*#>S2H}idWJjosiTFk@XoEv{yxc{mHh`zFcH(;-} z5el}D&)_xY2>)~h{rhG_d;p(3=`WXuKo|2ETyn)mup9i(ebPU^OQa9zuWUJbN+7JL z1h0{N;@%+mzxkE9lHdogvAE$usz(HmxXbyiO`N*Z1nXv-d^A(}oj3uU85f9aRKB_1 zDi@j9^CO43BiV}1!10dbZ2j|M^GAwFTdXOqCb9V02andqeXPtz7Zxr5%Pon>z^jE; z+*V4#1MZLiwKXPlDvDX+8DCZ>i{R#czazkKvu`DssVbd0>me?Gjhh~WM02)#VSElF zZRuw}D8qqXEeyM%e{11eQ&ntEPx62Jl&JFYdrExTN7h%8d~|qb-$s+I>!Z53W2O@F z$f+AN+e&AFVtq#d70YvgKM>E-9m!L)cvWKz(j6=Sv)|RDZ5Oa5Z#8-eOu19-No2QC z^FP|72f|jRJVV*g9gBQOe=(luMbGA2nCE$6a(3F!LKd?It)_A zWS9qLadoVOiiX~?=($$uKjmK~?l~)aXnt}h%M-9Bu^{6p%>iGf+>-14{ny1Rq_IU+ zTMz42lTBCK@ZyP>SkEhf5{mQw^)uxc2_;SgB(BrgtW=UvU_|Vgna8sJVak{*)$-b1 zRz1X`fMDK^o)Oj6u#pU+~h^KB}sAckhSaU}`v+upn{E3`U!18YBkgj=EJs8hm9 z|3ljb@W52{8+mUvSlz*161r>gZV_yRw+{DzU&MyQgtCYaS#$Mt)+m~XTvK`&r8dfrruWSb?Gln~I zm`}|B(ENG+yY8jlpx&lK<6e+lxuH5>brgog zEig?NKNdm}jIvx>BH&R33{cAPbdwJ>y|O@iZ{pU~$q^d@x8@Hrk1hOV!A$UXH>-++ zVR~?*wXq_b%>LgOKL%t9G+{~xzvN~k7$+Ou4=b;6yx<@k=wbga+l3^{XRvD%?=Q^% z1o&PTUiZiOmS&4xvmoy#z@}^MryQ#<{lEM^HM(OK^7}KsyC6jq^A zqlM`jJImraOe<<-5B*xL=5sKomtT#y5W8VGo|Tz2c9CLbxqZIUS* zbToR{DMT(u-IXFht&HLVpX_}aEk?G9e>?PN)uHeS(lAyuCGKsSF?mB#XZrH%ZC35t0#1M2tl!sll)8AT+UjCdM#%Cd|{|A{h_?WI>$7P|zJGyY^8v3larBMS=XXUH)pFub~jH`A{W9$H<%J?Wnh#o*-I;5b|sRo)^3=eaRnhqeW=N7Qjj zlRunVuz6Jsd`t($hc5tSo>DM~Z1Cw7z;pRtNmZgif_v!SbGd4(ba4@YN87uD^7&xG zj#`-)OZU_dvmBXDFAFfXbRhAjP-S*(sA?AwEo_^6Z-=a2lbAL&Au7M-9R z&rx$2;e?hZs3J{Ov4qFw?3|ejqfX()(cLM{yS6C2LF!FdoeV_G&pmNxsb`f9!FN6X zd{KCb-6{f}MqRQ)@fPhKi6~UtBPGhHEx0yorkyr|CIdm4B}~C}ca;;B>$U>=gxr<7 zyx^W6RK`cc=e|5dN|lM^^GD?}3NQ%yrmLj#jopMwOzX2&#Eol33d3?CIhARE>8N5O z&u`JT>4Dp(KsrWDAP=BQ$$m3iVt%SLyySK^)|^~;YhmA?j-zRt9~~&@$1kNMkmz;F1vY}Z%;$~99Yvl)dC5a>Dr@nUtd$% zp>sqSqVt1X>H6R}$bXgg-D@QE&B?L%y-!JJjQMI~>^!S_D~zhKbE^SL{g6b;cW zohCq&+adnb$Y|^P(fj@EkuUs%pYC4y?(H{=-;uPyG75C$G?TS+r0!l?6a{!<(q9nr z`oGK^xgX+gP+*0RK%8&~Ct$^kk+>3yF%?pO<$e&+AOtvir0@N*`;j{!TSQ!DU*|17 ztGSMm+5`tn%~*k$-UWkX@EvW|{ZEC2`yJLkMklsoa$(F*C`v>-^#yMZ9Be6U_YK73 zlnyIGg#=XHdtZt<`L$G&qg^ftjT1~icnpo)n@nq9wM0(Dn9X{#56(j)JBrzM(n-5n zStWf?E|nrk<7kUo2AY$4-i8_*bUBQmu27pV^iV=wu%bReu`>T5_q_*YP)}k~?fO9? zK}9Z;phoeZNI9t*;lzZfH0oKaa7O?ZgpiU?LYqUyeQ6|pzy^LcMtdv4l8f7ZsYZ5F6R~CMStv1@hzSPA}3dq z>|1u_y0_H=A!Pje8IR%td@W{t4UB;k7V)0j3V#D_o0BiHX6k&<6KsdBka=!q1t0D! z7FT_JYqh3tKFkZ^tcUpkw>3)bJmG5G|EU8o;HDXfZbT0C5@{?!^PuHKO z_>2y&i;cjvZ5hqa;BFcj(06oFox5!An|sN+KcYOYo{H!ZQmr`znJhxq7kEr&eMMQ< z>30L&FN369sommeQx4V_c9FaPXaNv&$pIyH(gHd=m9tw7x4pq<@i^L7C=^mR?Jo&K zr!BRdc-WsmF~6LDWskMMetVuJOY@ZRTARzC6c+LGbHgA(%K#m8ZTjsSju||G`uqL; zj^EJ9{?E`idz9cz8x0lVkqYMhC57_Ra4XufC&yW{*mJbvS-RL%OuBA8DQWV9y)9Pm zN9+EHb0a$wu?u%rlKn7=X?rs~P4h1uMS#m9PKf=RH8;-;F3T@|;Z-n4DKelK!ML>8 zM<<>cQr^f>b^>m*f9$MP7G6KH4ev?o3B6%CRzzaqP=OGYoZB)N?)3|z#(+nFEIgM};2u1v3d z>P`*G(hpHmPeEDPscS={F#GY$FtyfBLxGpV`^ntNzRtM&M&EuIWf1iOs4ImnPR~AM zIsN__>q2psF;rR7dsH?n#|Zm-M56ZcJo&&?NiW{H|G4ZbDQn|R?&)KX%^pc-D*2Sd zcr!(pRRp+M0emL zRE*^n9KK!*7nx(-Ch67wl`*~|!z`0V*S^4f>Gh8BH7KE(9lU^!JE(2UyC#|cAi301 zv!81OCNw8_f1Pp&YBLO-ru7Lq4BxShgASvVFRRJi9}M%hy^f7zG6`IaMmpJxtC#0l zy>g%1u-^EMkz)}CBiG>+GI|k$x>ty)=py>4VRA964V;Ie#|D?+Gq-4GCS5Bf@9Ma=Hf` zT!jKXkLr=NOqmhY3=2F?$5mz%GLmYO&@P$zHbh3CS>=P0q)Y;zK^X|a&xSP5JL@p& zbQz@m?W8(yA839fR{^F-LT4R{X4j*EMqnYQt0sBnC29s~SJzM~)5TzYTU*_SHt;Ig zs}A=#9QE33llE->=G%>r;b_3@#08%Y_L}{p8ldVU^TYnG7b7#a%I}g}&uMX-Q4y0- zjRPh_bLmZHQuq{E4GkoFs>Vziq4&C=oms6uS#bE6>+V}i1oH*RW1BF%2&@0Hocm!d z*{!tHs6;*@UQq1sGFmg^yXRJ7zl3?Pk=dkB{0Q%wLl27|1{ouWvV{M|KHCA#2rZ^& z*(y0yHHhlCu!J_30j4OP#TwOV_gtH+JO+B~Ia%!Kd<)dl$Tb6T?D4pqcWTsxn;xF6 z;Z%ltQrU8rV`WWBq`iT<4sjetLIHvLpf2$J5d2{AfXPU|*=WWDXqro9yS8fUyjFK! z;dJLq7mV8yuoR|abUmU>lAJZdsikkJ<{eSeN$X=~Ah}z<4ncaDt1QK3D z=Iu5i>Tm4HjoSjgxi_}&R^maX%?><$ikJ6-qj*QQs3VJwmK;K#%CyurO+?TsOSGCQ zM@X2uuZ~sgVv_P)H=XNorPHm;SstvD^f=g2Z9NuZGUsY8>9_NJIX$2o+RjIxmflM_ z4p`#C8Bg|ngV~$m%Yb$U(B@6S5cMe;-tl`euV`TAe?4L@BF|)@J zY|Tb*FOYFG@L-fIltgDKWJH3}U#qOYCr7u6ux8lM_Cbd8x*(^^a!d=)E5f*sDrwhj zQU1+6L#$;I?7R8Bj*7<@uqzzF$6ig~HNngg0G*1?@2|Rf_ks!c0n=h_`CzuwOlC53 zxVCq3*Raru9rB&~Q?^UL36u7rI($af&jX%wJgAD%MDWECr6ghgW{|K0 zE+WxKgYYaqq(JrH8s$-|z@8jAl+^X4D{_m+-qjDte&D*BbCr-Id{LIduyH~@)x}6W zVph=Pd5}s}u4!*wWVUKPmpjA%iA?>U9B2Usgi8}d%25v7RUCk=@brZ){U=rZZ@#tz zD6RyKbthE=^P(EY-QH1W8FG@pnIYtPbl{C@1k@hFdJqB|gLfG-?q6>7UoQV-7qVb1 zK9M4h4kPe>q9?EY`_B6Nw~Yv7pk;B>2hbWN#M5zdeM9(MwV)&s!6lk(lAVM6y3F z_vW5!q9_^t8DM4?B$wB9e?rpP=z9x=@kb~dY9Y4fhyXIih1CneFoC~) zN`bqVu?H`I$!xp_+57_JdNhI-nnP``Y~>NIL!vZY=}ns#aeUbS@(lmi1&^iz@7wUp zu!0HTu1CmxxY8j4`m1XWtmjX5N5q|T-F7}=bcy};v5>4Xf}QEPvuY8*bt+tcc<}ZzF=u%)ZxxuxuY5#ISI=GWmV@g_(zbVdzr+evgJREtYS??FTpi}8 zFW7K@KVW~LG|<5cQA@fy0FYF}9k{+Ln+~U5Po<*fOEUgC!lzK3y6Moq>qQtZf2L70 zMn>((u7U$bjRUT6TyJ&r9hSIlZirPf>kFB@0}xoEsa&HaPun$~?>AWMUv~^BM3%{N z>dY5dCb)pNJ-oV;u$!9(zzXRLi@z1`QbXYc{l8A;e}3`5*B2wtC+rTzwVMk*Ea9-4 zL%l$5PPsoveeGNG<+F?*3nGLW?3Ocn9d>OvGO-_lk1o%p)>rnUIn?^n=wEfOTz6lv zo$31;5T%LOB+LOnm=6Rkd@5_WU$B>^*KlO~5z_1uQD!?R_a9LH4lF`;3w92SoA*xZ zQ*F2oMt1C;Qc0T%WG_0NBcwAOdR^ZC&+Fu=Z2Dv0qMqY18Djz18@5Kbe?R3XWOI5 zzSMK0)4KNee*MV%#s~dco1c9Gmr|a_4E7Y)=q%$ zZo{d0nc?Y{@vAib7r#hffYOuNY=`#bSy$R4WTiPrj-tUzL!6I~lygw}L!_RZ8w|mi zL9<5`e(rb})FGmthy(|m$y`Ft?6P0x$9wQ}cF1w8op^oVPvV9a9S`nf#onU3;8MY!X#+v?~& z5G#^}>;2Ky9r@qNL>$-FeR`h2|35(J=ZNFHR%P^lt7=hGeQxNtRD{QV@G7wzH!H6| z%s~V;Q2yu5`Co^8NyfKFlqJI+tS7d^jT&3lw0nhdcVt(K97MwCU{`A8OzpIae z0Jt@=`R_q$E2(Bvf}*$-Qq)CHr^4S=vtXcFj+eBdzpZ30fVv1oegJT)(M-qtw3-=d z>iq7%f+LwUKM#Eo`gGxMal*?iEs)qq6KKGt_?0RBM$cQUD*1nvxTr5(;t&IodZC-w zu9CQfy5u_?nsvD?@aqNCF1m62RboG#bEx@MAng+6*h&McB5@GN4xnv>m_IB6{hNKB z%#e-cVp+-d^0?UU+v2#~O_yvz=UD7_hwgu*UjAmq(8ca*RX$F>EgLQMcN#7{gk)Ik z)nZg96^B)iM49CTp;XM0R(m;dbh|tHHLj&yp~}s^nNQXq#)!wabuk71+nzwuF5@1KGcXA(q-MV*a*4^5s?w-8FC z>+slZb!8m0ht)n`HX76Bi*Z~TzLr8a+BVa_X1Hm{w^z=6e6gTEPlrJ~g1XevmM;^b zC95fyJ^78RcA*wF#yd|DEpj*k!r+?R<%Z)k+THJVCS6BNAa53ZUnl>klr(`x>4)kE z57U-EBuC!z)s6n7f|tycFw8jzC3sOF0%-azr4YSDDvpEgNz;?APLX0?h`uEfooB^c zfs7f+q&%us3uxEhd5Yp6LJ)Ql)MJSl5+%?O5!$g|Cr9{(vkB}v1>k$Wn~5k!VYyOEvXh~zBMz_V3EzV zE+O_})by79S99m}4mp>%Lj$e=SA!fmmqoCCGBG{OCgR;2Wdle=SfN@*nD%cifZ*34 zCb_v*fBK7rF#-xjh`Jso*ZT3>DWc#ozu z_`S?60*0c@t2Hh=Z>K?>WXKWfqcU8S|KWycuktR@gDNtcQL4*eFNPmgf&Kkn>&2}@ ztDh1}d$f7;&xqcZc%sHICM@9$_heDK^QyYzXLiTqIHoh9XznfdO?~?JRqF_0Z9v;}CNYthD~?X$Sq?wL z*vgkz#L#rL9z^fF&^hsb@w=Lwqfzej+Zg!cBHp=+IOz{X(j!;HSFNyLuVS7PtV-8R z)2vFXE_`@!{hXkjoOskxm+iV%*J#2}!dQZ@zO#5MPe%L4IY}IAI1tZAzRvjB_+w>Ff{9%ZjWofe$CoD zw~wBH^A@e*`tHg|IHx&Z@j{``AX*Bg!CY6ufKXbRxGsmdOQrpYjOjpKwkQz?FdS&y zT^t8~&~QT~Bh#HVokFTd!8c?!!TcMRZSLZ!zxbGG9^*iFRhO36Z*`Z$YM74Kb;tGs zAGWdOSj}+LmEN`}^K`@36jF-QNx~1N#bw@K-K~Ugd`cERY;BitL;Xf@6(xntlZV>6 zCaizRub{>9MmH{1U>np#7@nR8b8#o(1~vqJ?=hk$2@9`I9FhS8#BMn%P;m64J>O_M z_9b$pMUZ@@8nlwnNem>>%+sxB3W{Nf0UqU|`#$|3on~ECO*mcpXpKFaQZgBl?*oQ> zuFNU-c6>aUoBRsse@xhfU?ksW)~)+tQLTDs(B|3{#~J!3e>^V;jYLr&#}0}^TUK(U z6ZPw>BG(T zD_v0PxrU#Ai`wVMmN6(xfp1 zzTUul-Q{y&41rUr&|qU$MDu2@=xzS?Y zUz4yr<|vSvt5zm}OCvKqZIA?XSUjIccCP!I)PrUtRd22<>jWjbEYi|Mk`QfC4bo{< zJVzSwVB>$|@R9#iC;eVzx}jR}_)@1`A()}I;XX;88kK|x8$m};w>`rtj^m&zYslGI}#IcYEY&pFvDMetumj`(^I9~D~4XyVU-V2(ZwrK;gGM1y`9vax9FCm zRG1S{Z_sRII#yO(ykhAKT}zM-=g>9oH$T|h$Q=Blc#Mklokltd3$ovk|BmPLZKTq@ zwwKF?8jr63dkTHTlOT2S6R&_vmnb3_)fW4z>&Y)9748v%d3m)ICw(w+>Jbjr0pekf zm)oA7kUdz4VbYF<>e_j^XV}i2aol6*VF5zp7c?_$R!2FfQ*mB&WXM9J)r!4p_d_PA zs1M4ke$pUC^+Y0LfTD^{yDU&A|J2ITbbOh&HiRErrY~pg>yU%L@zvaXne@9VqXtE` z1Ei*XRS}}OR6!=(-Vc1jI^NbkkHDIW!g(jChbq|PdxcdAWTSFTfs{d=GFKYx3HA39@sdj<~*gTt0Z7Cj%p;UF}Z3#6L1^ao<8t^esmv{p96;W&uT3wLEgYt?WkNX=*6n;%((T z1yK(Lof5q=1nr5?c%I4jJqenAav1<4T~2*-!6sn_lhCx>9|h_+qaoCvFOsJkY|9if z7kaRx@Pi;qwBRS}({;3iqb=qgzVkaY!hUwFmThWdH!rA?aM=wk-7`}PbBrY580_XJ zixIs(GDEM@zP(UvPjmvU?uCi&e_+0!H}QJNmf%4$)|k)(Crj7NdF^g>ne(T)-?LQ} zH8`x7Zwo(Ftu=N(*l0G8d7F=S=<@cSd1CPXllc4;DVo2m)W84x&(vIW6#^S}gzTRa zmOu-vI1Vdv4B~$Kw_a*jQgREfCZpWXsi)i(RQKT$X%Q~(R2?tUai&U-2!8F`oB1ln zZejds&+=C{v5rLEpkNYrRd)QhXmqUJjNERI_pC_LgbrA%WXtrf{2&c*3$FB)vRYG2 z-svmB>F?pC){hePn7UnV*e;<_p;4u;lm*g%j%ZA zG1ryHd;_$Dn=kzce`7!XG%yWPaMbCMlI%j`H}XN~VuWWwiMxL?-4+D`*f*EiYs_}I z7KWDcni*-D+S}@Q<7Nt*NqafL|D>jpfO^L^Oj`&j>|4XKUxW0O>gDS#OOYg87LgqZ z{AdNpl5zOoKBj2JV|lco60~EzcbvhHGHx2J+4KAfm$o>!;8(K z1wOfeFa}Io=jDde)v?h%{+m(K5*?zH|GuF9J{nzxU=&@w z-*7{Z$N*|sR)NET9^J4RL$h!b$jt;WI~f%2MPf6^~5naGn>VmYP-YHb-!Wy;wb zd0oX#(-i0QF>uDes)g=xOLF2p`ZgFw$u9;(*eL(h4^_Mo9*f)g3VCk!4Rr=zg0px< zKEK=wZjA;zAkR1miU+k)Z`i*qT>16DXpyGK@{kGP;VNZmjia*!2*LQ8-VVmwi`A-h zQTBgQ^X#XeBAdgq+NMsEi$a-{_`&SRTAERIh>VWN1`LrpA11Ocs!#)cl#%LX1ps^ig)geJrXB6QG3qW3)qm^D^`BO6 z{%uXyv=6}8rO@y+Hgxf(23D2{1#bKV zU4nH_p@`0a6eM~>%nNf^Pv}o3Ve?%nlI?Y$3`$326S!vk8=-+Xl_j;R|H4>+31@AmB=v+GEoB7WyAG zYQ|b(_h>5xlPh@d(T{u${m?f7*_6D&RvaDwShSQ|BTqYqZ1H>M;uA>U@A#nOwz#hK zMG;)13XfcLbD#3{samK%sUkIkGkUqhRSkCv%?!8`v21x`zxz6CRL&sGTq%~y-qb4=%Ko4XG zdKfIO1d!kGQk(ruaLu`WS4^|aEo%GV>FbEu-IN)_3Kdwo6i}W0Ou;f=Napb!dTz&+ zFzB)<{v~+9XmPrlG+!tyy7Z4VMY8Jjmlj6k**@}+E#YV z_rIQmQ;MoMhGCC*;ad2bNRcy{ji!~>9Lz3lzQi@E#Q0Gf%AZJuoG&J(-{iAcmF-;e z6SdQTSx?cnFEKyh`JUmxt}=i5X}YjdiS3~aR=4V@PBk^BEgq&S_Y^IU96H{_GjzO_ zUslXY4rp@K@wJ%G^ur#{L_S@A8&UK0=|SnZ9a{jLw3+DWCsyR@nfc&{=B3B za(ORtDASx4$|pQx#3M4>GbL4hp3ti1E%(g5SjD}+T1>GCT9DGN#J@JPt%atIT3*XI zV-ZbGF<$mXoE9&dWZ=Ipa$(=xS!I(`$<@ic$h$Pa7-jNvbmuN!%G4>Yg}y{_21dHz zF&F8>spR)YOCbvRR?@ABj6tHLJfe8?@`8Cf$0OphNvjyN8fmZ7Ja+IZ?YE+j_Ip|0 zaGEFPXcT^>y>_P|+Vtx{?7Oua5#4+v45`gB-#=4q4i|Vhqb2%ddD5Q0gb|_6mT_le zXspoJKNAvxko|PL_!Nr9Uzb_Fb&WrNXRkGw6XD~~km%#ItG=hLKR&5mFiVTMky>SM zHqYXhS;DedVce~bz2M+}?~i=*1n#np^`}N{jpjqmy(SBHlc;l_r8=g6b9HR=ddpsX zfimFG#q)ujKPUTw+K-CGdd-ohrlwGkf? zix@W7cQvHuGSh%RPMYc8`dR*${aZxr-%fBPD!8Qc!f-&%vqevFhJ!3XtERP4U5L|5VD?;CfbdmbXCC{Um*iM{(POc zHF{94{aV%U>gtl+wPbXPUG$PSEY5Te7N4JleoR$R(rCNh+Q<1?5SwUgeNOjLD~t0I zzmGUt)4?%T17RMPkdP0`!yBI~=on`5O}g8@k$FVs_f?XX@vykab2EZo?0-!0AFGR4kv6#Uakd7D5g}LGeWPUPVziYqsMVd34N3)?x z87iZ|LF)+_$f`d*5w)3P{GlGEdHX3Dy1@M2P?eDk7M)TKZL@5=U0EeKiNB4)1W@*T z;Ldv&*c=g`QW|lgq%Vd0+E_R-a3n`aV}Xehz9oKAL#qC&%GH>)e8-`sS6A@EX1SPUv|F`*>C3B zAzC^Td8zaI^A(d{48$5=Rz`%FwUf_1)~P_NiQIn(nf`ptDYa||VNUyB15f)*^#84` z^?y2EqoKi=qIieBf?M<42E*VYcImA@A+^_oXNh6`xo=zJtka?f3k}*Wch;a8DtI^Q z$ToD_8{3m1rd7Nh<9o&U!N`=hGq>dUx{AK@nQjVqN7F&MZgFvKu)qZ7qCpgv@g(uJ z-8=i}&;&s?Lz<8vIb>XgsjN*k2Pe5IN90U12Hzwo4&U_dCcZ-;_`PDtPDwl8WObHM z58fa7`S=3y`B07024!J?>5+qVyc>*ic!u@zpEZSb7g?s04F&_Sp;tNKKdYQJtFQm> zy=wonxBt0FA%H(h8F1Qs0xnTXG^|7U_L$s=GX$)EzlA^v4Lo=W*<(_2u?Tyd8jP+nZxj)q#R_LY(HbjU4kj@oXd zTThT~2K*`qbyGrC3#9@wlta-oP53v&IDGRAcJ_h6_dD?u^zzyQ6L0%DCTjXPl;vbb#dchw8hlyyOq&+zH!`9f{X9riXWyHs_K& zojVO~oyiDZh=lh%_qp{8vK){FMcQ$0yw)~VU=nKCFj}Ki5q)ALNXy2`P+dDIvY8ov z$9>nSZMfW|n3BDiTdl~>h!b4jgz22Ug)wc3=sO?EJr7qLX?2QHBKGd)*K9n}Sb&09 zt;0QC=tEf$&t=c`mV+T^i3-3deCUurNbjCFv<524EiIn54-HzZZ^@J zf?3URDNCxKM958b`wh2?fs)WZaylZEW`2b#i7Ce8*#FmvQe<=x8GoW%nbn&C_mA}P z%(CjKd_)YDHpY7z-M3w~ZkK_3=)BoVBaM^PPRe`Owg`1CvurQUv|mvWgY{g`kP`L) z7W$uy!;3@z2Xy9)zYgBiVqflp&&w;FntjK78crH*NzCP+0!bvR9IWQ-XxJO@8|zA+ zd=ry%?25lANdL$dbCxuhLFa*DP)tEJ#OmS^#+Iw!5}WFYHRYTZdW>3i3w$PCxqU6p zj%3o-iV@xKMl!U1JMBBWJ}-L>HzOB3%JHHuPRU=nMaKY@B4jV|mhxih*cu`Ww=20vYgCpA=tlo}Z2qVwcT5#Ei&9Od_A8gHq=?kwqskMbO z)(d=^YJW_JrTT_7BL=o`E0`>5(bK(D3s8pp(Huk*MHdxSYJR^(w%KD>gM?ybye(1? zm&!#@=43PICf~Mix19Crr9_Wkw0K`wGN4X5iEC-{q*{N&4ZRGvK2|KKT1xXh0C%2m zIe^lyK=Dq?OCDB}M}Bfk zAou$?X(AaC2h4HI%v2uoR^v6aMs3QrLN73hhjoL9J2$T0cp!z(Shv(*z7Sp+zV3Sg zL<7tZAZ(CgX^p;D98AouW$ix@_of%^jFEp0GP}$y(U22#iv4SLJ;HA@i|1)^8g?hT zZ*FzdM6NH&L)(#(cva$xd`SiE4>Q1td6WI>~@7Hd} zFe)k(89QrkHYg5x>co>{4@2K*#aW<-S9w}JBNm$PjO7zlNS9cMEL9~Rear7zh@?yt z7+w+A(!Uya9iX%vT>rMYGceH>%hEKRHCV{IxmY(MGAFq2$I=>pU^Ck5%PpY7sN!$t*EhG_(o;FE8Jo@twv=nk` z{RQ$@pp})r#r&=k;}y8?o3-dRMCbo95XFjE9x8q?bn$Z3)v3yk)+6hS zb6t>iv{QR@TRBT*Als3{k=N=>E}=jg;{TcYSbzzgq=0z8zlmkg;1Na?l-zKkUeW(b zLG|GPD)p!PRCPDj4UMjpeNf3FM>8=Q`IgGxmepI2B%&@PuUjD4-p>Wo&qWtQRH-5x z`0}+X>Cj$1x$@-ooOD57nLQh=MoGb@)lMX)c!14aUGLmR-z#F}Oe+`laV&*zJX{{} zkq{Xt(Dh*4A!hIW{3Gl2b8}VZXCh2uJkNd*c$s8hU6_cxfSx!o(~;Dh*)2B5#5*&x z7809=#yGW-18Ef*ZM1lDI~NLll$C{cw6qWsZlg9d=h_Odw#m14&Brbl^5xYDa~O*uayy{jR{ zTB4x|ci_&^BqROYIv?Y|)Z~an>0Qxjm?Y`o@QG(oN%2m&PPGJvb*3vp7~=Pq1%jFy zJeSOp;uZ6Y;{z_c^Et&+4;fSwt4ZhF`t;A1;<|vundcf>9>MuWiVI%%!&9$EH);|_ z2DnjOz`e_oE6l*xe)9=}F$vH%+@by?tzI-Uu!PMz9%k(-h8g;PY@hiTE#~8+3o-#B*8z!TjT-xbr|Zoa>A2>2xU1ch zp-nLMJ>?=>Y0Fh&Z0#r}&4-x-p5a$}T zs3j@*UeQW#(&T!1n)?Z|-k9hCTep-yT;H6&Ci_9|?Husm8^YyYe%3>?4=C`nip&49 z5~ZrZ)e2a0*swiO>$6gRv?ZwD%=59B2wJfIWoIqMg6GZVTGkQHpst{eHAnW#`0C@d znRZp@_)u!*qV`YB)ZuvTyuzwKjy^lz?8BFD=wC9`jR?(rWqZK~d*GgF|0B$s4{jRA z%yqq?&d>087z6FmU$a{+(w1EW&F$iYP_S}c|jf-8RAg!nO2Kz6V%wRd2!UbaWmY>3l)&w3q&5;2O}c2Jm< zXb^Jzb>C)R0K~8|7Xs~8cD;6ajBtrK-cw4)jp9A^?N?Q7PBKcDn7PnVODMtxYBS%H z3H&jMSCpMfou;J36M5d`)p?#$FfMNNM<-kg!{@n+$ME|8cRAljP9_7Ns~88XoHoMo zYqjg#K5H|4;;1QU&?LV4qxR_HnhX9F`3E1-Z`4E88eO-QZ2MGq{a2l4a@-)Zpa8uq z`m8k4`(mW7%Pg6$FD`|?zIcV-zRW%MQwWJ<4b_=xu+H-~YBAWVyLZg;y^`o?9juYnu0r?xcMLOMZzJJ^FCso2`o?*_DjZHwo zor`IK1r=79x)%6F=$hceM+HW%KNrVyUnt$teYDqrZy_DWs={h0Hn*K?6UDXJTbPt! zSw4{W_1B`(rmfL(tbW^}QnZ|;@C8(lbz>ty5O99R%3(Kn=MA@Y*ia->!iWSE$Rw=%g+WMcHAwL@G=K;6q({*Jz+ z?k(2|6AtU$UB$w5yVf7!Ovl8VM_b*k*H{ev-t(DmHnTqKX(nAL(`%3U*p@empC~9W z0C@9Iv11Yw$Fs&ef}VPH!^NMSaquj(>pirK=4yT}NUbpho{}pWK%>+PQ#eyui(vdk zHb~dl$w(XUyEbr|Eb;nwu|AnPWz3FL>6gP;kdl#yh z;gQmavl`x$9w-^rHj{AI_Re(;-GoO+YFU31F}$8X&?}8>zw=CpOi1 zFWVP;U{u9c&l&n>U@;OR0-CSeamydv6L(PNNRs{<6a@*~eh6U+V zDt_u;MJL-ONxLY>rFi`2onXOGV1bdv_|a!p!+B9-w(nFtuCIu> zM^mLB7(dU2N&3q6EKxDJ3u1^}Lg+)0JfInpyuGLOBDYWQyo)Q^^RK|m34w0RF;qMB zS+`YkXcCx&?}jsq3f4k(FDW6%r(WU#LMoJBGck(cw~TC@nLOMwNmbsmn^PX{k@X10 zOB1WQI0aSNEfgIcZ9QXT^_FYqn6?KSyF6~&JktB$VM3B?vn#Wi(e$$aXaRJ_6+z#V zh0)R&s}Bn4Ha&TkS3=!&DxS&{-M9P`HQXIE2<5EiwO=fW%$5@lYQav#(I+}C6hJR~ zbd`)8SWP|EKBDP;=Kbbaa+6n{U8@lyB<3|vkvcUj8~QF8U_oBS;>U4vyr#bKe|R*TCEv7=!o<;V9)tj*71BEGP+fS z>7`xG(b>oHxxX*TdDb+O(5Lj1{SE}d;xSgWNSM1=b6Z>|oGQqeJF!&iV!$PRz@Iga z{PnN0M33iy5R5{e7A;LXNq=wadQnfkE;9lFMZBo%d*AnQX!i1%l4bi3A%R|hw-6io z^b;hDd6Nc(2ZK?UOV(fI%2GfKqb?PkG1J#=UBB6|YSFKsoM#sJLayf}*P%O7X&zc) zHYUz$EXAxyGqK}@r{Nyp$7<4<88k?Nvcd%{#eu}4Nz)F?+!2RpK%33;b({QmW5AQH z3i27mjMOv#i>1?ry;6PcL|La;A~NTW=dpbQCB5ye6zem$d7VgdT)J%;Q)rwSxLZ@| zGII;-+gs5WIvf(wc?{UENe0XNa4?-|Ga|n7FSo}vWDh&wacylhlkG*Xq?MZWiU+mC z`-tUL)Mh4ELPkM>sAi7#N8q%Tojtdf;gglzQ;P_EniMI2C=GKdeXakfwokY4F(hnE zo|iKW)jW&(S%~?gVAMItiE)nc8Bj1F|LW))v!3U;Js&zA$O?3kGbCEvuD#Q1zfU}i zWSgJIf%|H8>zix!-}qrD_OsalB)j;A-MPxG%Fb8ZCc4tbD29*BE(SQ3_B|b!dGPJ! zb@J+t7KtOK8Q2WPXej+KUL){#n*K}>b6q&7EK5LSo7FqTnC+P4RRm-e#zpmdt1KSk z=Hd|1^QV?>TPrwOisfPBX8k3}?C*7(Q*Sh4fTDmrq`Tk{cXs(&1&Ku@|24VLss`TS z6KmltUH6ApV>ML#Cw5ZCU2o@bteE7ybMv%^CDQ|qI`e9orY>G3{Zx0j>Y9JBPSh{% zoaZ8ho)X-^eT|3HeQU+>Z5HD;of>)m_QG+Uzv#X1)!+e19Xe&q8h+UaXhfmJ$$4x! zUR{w&`t5gP0*Ywtu3W9k{>*o1csjC&Z-Z~Ld=JDbPX+-p&*caB{q)Qv;KRu!ZoV~C8 zYa6N}sfdiQl9+%25~b*0?{pA}K|@u`kP_!~o4>+&(9TT9Fr^8ae?lZZ*c3GN%C{Ju z$~T)6Jx@5j2A97t=q5aMty^pu8AgC7)u=y~Lbp8UzxtQGZ&$4qNdH;EmL{7?xXShjn*K3HYyVu4jhO=gK9648~`wW`-QpId6uy+ zAiN(S?xKfT;bNZ@%G7>wVUtfGFx+T-Lrt$GiB~EENGfg~7lRavyx==X3WE9(CtikK z==Qwx{bduSq*Ah}C-`uKX(7M|+gt^u>qQ2=@e!2Zjc@KClfpx?WaBBABg;7G| zgNL{{5nCYIAWQCXy2r23`iuZTIKwi5&!9;l-!xNDpa?T2C;>A@ zPIAK`=uEcrFZ)V*9lnA04&`SL7`_YV!RPeWzdi_%{@ES?KIZfs$$3x|KcrDc#(MH7<=mvNYA5)!8rWU zXo!r-S%22=!u%}h-?S%XgUJ#>@>KFRd*$PqL33iG72R#%qoqp?eE|JdHl&1XROQrV zkMIt({HmxYar?MO%I&~F3tDYj5JG?=JMBQ8Hr6nu%l0?XbhU4d;dj1WDRTR41{ySO zUw7^~Z>GVFKX4YQO(Wqt@-12WDs34Wg|w_jJW`7TGn<{o3Bg`#9bG}T>wC47SdBaO z6BLE#|9+QuXzOoUdjWq#^#U(avOnj|3{+(Fl&EVuuU78&lv`stiyUi0-4BRT(7D{a z6YBln2J#ZGAKw_>ket z$MTqGHL@1`h40pbqeC8Qs(c4d-Utur*LtRG5+kr*u4-IgyO%>FO*AlYeE1i7`QqJ@ zdC0Q5QOA68l`hox$OJ~cCm$)#yN32t@3W**g)k_`@3Hg`wfM3PV9WHwl)iv4(5^~thBjX+5y?gKG|Bk2Xye%qn!cYbq@Q9Hj|E6omzeZ+#0q}LhgmS zou=BKkRcF*vKT4|u(-^(W>;7}ekeH>-Wi)^W0Tl;^!{1E{AGeRW=J2!Kue+)0L3W3 z%Q(^T9&aluNF)R;j5}!qL5F<8!|^sxpE?H`TFzfJ2WhT7S+-c>;nPN@Ln(#hCyUwE zX*$Olf?p*ay#D*7JV91niEvX0VwxzBo%mGlXubz>tvfzfYpxl!%ggy;uF^v-QQtk+ zomFNa*kP37xjENccN>dt8xb(6R~5x`+V;!1%=fD@>5?^esvJ?*3||H*UI3A6Jpbio&tE zHEGl>vl7x5LAUEPrRvw(nio-9j(I=cvgzTxpMP*7)T~GTi6hu zLkV;Dc&HbHC52aGQhV@7<0a0|5T`F=8=fb}1I{uPTdO+`hI#qxyQStay9!y4)kDoz zA_qG4_O5Em)fxaBT@~E6sb~DO$>Wt>9~|@b3XWaNpJ1tGF#pXgW{ul3JRs+13#w6+ zqf@y0c)j_x&Ad>d*Xz zzc(N1jn{Be+0K?vXJtNaFQ2#>mQcOh5+iZ+gM+7-)kuPwg>6H zY~GK;SBJ7Me%YtlE&K>qb5Hd0*Bk^b5lWDV-MF(fE(QXjpMr=7C<&9UcyjF)i@tTG z)TycHDHJwuO$lL!vqGkf{|BklWhV8a_rW(p{B_^k-^v;;8c1-o)m?obFrcq;}5Y z42iewS@s^eTx;lj?K?{&iq_Vkpfg;yv{=l<_CI*z0`7zq+596jYSSYAfz8aKa%)_EER^azT5or`QR8=$XWKcJcd*p<1rH9J zaez&FyHZ$heIk0;$z3W2jk}7zOOhE_&Y+vdsyA71hx^D>RVj5hLEHOv`~n0RrT0a{>C*~Z0V1fH+dLd#g#+@K}iBI@a+p1FK9LxPF2=T zzQiCM8g-D6{@9UFEywkK>Iwx$Vj^yYLvyO1_HK^eGx*?`4Jmr)2gQqTCVGq`>oY9yoR!kn z-M9S8-xVJ&O6C)V)d1y=Q8LDIknaI3+wcoj2CXUVM$VlkY#;-vg&er zI3bI)7IiFYc8KfmypWhgp=SoAHp|c)lk!w>{EpJyq^qRc`mz4cxAZ0_r@Xt-3S4%~ zB-&>kc{JX%N%Cnk9sk1WYy5U`i;tzX3pXX7XVLXsz&By;x!`hx%#@g@f0zg|knGtp z;n;F+-foh-_dgZ8d?bP8vLW@S$8icaaE3oykIuC5dMvZnQlGN!;t4^6Fk`~(CqdbFP}#V?}Kc(*p%x*xo~-o!k5A3DuF z4DyM;aj1-bOOjUqHsd}z&z%9)^@w0MEF1|&$Gl~_8huk5!(4vvTm(|TxH%U#pyR4c zr%JbG=i|+Y`p`%9*5Og$ddQ2K$3Ry!n({6XR{VJvQ0$u-H`$ zamu~awf`2+iOV~-mDDw2PN!;OW41eCHTY$9&X8a8G|=>}?4I^WzBD@Mai(rxVrA$q z;j}f3_?GH7UxjT-DC?<@US@%l=h}>|IMb;sg&_I~>E*BXcp{rToesPYCy4bI??H-GwZCrFIwsnVc?TmRJ8glvm`^~j9XW$?8}Rgk{>%TOV-)aSZS z<8*DwM(aB{YrbC4+%LkX5YTbISBlyBRLptPTQ&3R)oa=Zo3r7Ow2CcAQ6Bai5}y4^ zp3QkmE{3k0p)Gg6fToyZwC45Q5yF{+xx@%a&s}M}s~29UlKa60_v10Wg%sCgZ&HVa zA4Uf&d)3Ki*g9zfCpEu<)-j07(5S^Mnoh#Azv9ft6)I+WVEjLfy?0cTUDxjWJP6VR zf=H2G1O-GoC{?;h6Qy^gcaTm92vS8vrFTJ!fHdhfROy7?d+(tK0)ex5-fy41-*3O; zJLew;ahQ>W-1k~*&UszGOVD2Ha7D3VVXHf~APJw!l!3e9IMes!r0O>qXfziu!gS5q z>PgqI14YfK5CbjM3GZ~6#c==dS#+-)jt57)JKLv}=wJe8l5Y>XdTK?cI{cbyoENcd z4VJd|AJ=&xE0Re{KX<7rcPgh#=*xqVA?D&_=xxr?`t=v)_U_O^mgDe1CItU-`ai@RZ<=@Rp#OKI*d17DItI^ zbB)~X=k>Zg(^pAqrlH3UdFE;v{p0#VeXMBq`$J906fRlr1quB-ze42B2Atd#W}(qi zqf`2}i$vi%_hIJ~<5O5;?KB?J1BA{!*tN5HFi42Mihu((J3ul_J4M}qD%>O+T$&}E zi`F%B?}fkSe<0AA*LANSnKX&NsSo-R|4H z6DEka((J?N{Ox<|^Z4$@a{vl-!|W~sEyg!F{m)C^)AUb1wpDiV@2IJELjFzHkGB}P zlz-&b9LQ=E^-K)K;F$|`Lp}h;xuLEryh>$Q3Y4tF-(0BTmB6nrVb$_r%VE#c)ENeX zr0}HRF+@FpW_2SZ)19XdD+9L$^v8OA?nE(2ve$X+4vxD5-N@!TcYhz*Lx21OT4{Y0 z`to4R-+zet@RDy`WJ-8E7a!+Dx&`~N+((Va8=fMv8symmBQD?W&zs0^{1hCA9&4?a(}-Dj_r6F zTm0hJtqL>bPl6|ZGT<;yNu4#uk5+^Oz%9|fF0e9;+YQc|dleOTehSCT;Wv-ryGO3{ z#VbMm0~*s7!W>!+X2Gmh1KpNrpVq;Zk;nm(!$J55%^p8<`_-2RM?V^{ih z6<>GBe2u(cerwYiTmP$T+$U3_Y2p`WVnBmA~&Y)*@vO{o>LS^RzqY&he_D-Cl~#q3xD-is5B}^xeI= z3hk@2Bej$JtkJ}jR(zzj#FA+N-bdep1{Smh*B zM)(TY)=#D)SkC~eNRVBt{0qw-zETN}Q(wghR3=jvKNT9+@If(JGA=w?I^>~{#r|9{ z0*^)E&InTf1xleL_}$qeso#5S!b-&L({&l&&+Q(iZ;_9j#t}EQLWP2m7_!U~rthbX zLb-1XJsrQFu)r@OQAHy|@$!+zQPKH`7GTvrqzCAtk(}tip3>- zkhq2MT0jKQCQIc#c6qzx97@oPD;3M@6v*YfDQa!Pp!k#R&8&yIcZLsqJ=93%TuNg! zQBIr_kmT5S~X{to}ntCRySf}(4e%{-meH`=+ zAOK{I_M5XeK2_Kt3Co^S&wR+Q9;8Ube-Pqh%-K9%e;$$QlcVxjyV&3X_oKlGho8}a zT_DQdh6Y3THyO{+oe?kpS{5ovI6Y8~>Y`GI$q}a zL}9Fq9J&=g4QW650*krfXL^JiGpn=H2~Qt^2^2kdP~MGl$@A-b2Za^1bf-cXndwuK z8?ZvL*>CqM0!dr@pNE-3LM7QEJnkF&_huK;g;lY3AT6MKHs%rFRuluRk zY8dY|i#JorMix%bVOS*&A>Z-;}zM4X*jQnw}0Gzju05F^O7meZFwcX zXge3NJbtARv{@92U)+cZ-FPL=hM=HjBtlUscPBwZ*l`aG^vQkJdx{%*75n)z$0>u{ zWN=ZVbIU1zuJoHvaH6m)y$DcP%bG1Q9hgi2Ug#GLb(<+p60+ko_C1JY&xx;&MHoR^+z)?0rB!&7`0RG7~+f~rg93W)nN#zpbC>79FbIt0zo*yJ3X+2c9R{;9=t59NIY`t2GOk5TB4I|&n+ z55qKEqAH2TUlvXg$#@-wck(wo3PZL*zVFMDU=)5B*Z$R3BktlXX01;jZ8!%bDFr7s z9)C5Q|3mF=Hf%<^I1qS+dbmj8+-4$!STHSDu& zig&eCahE-TxM&v~IEKNV|3z}pz&lgMp^=|A4_BT76Ia^BtI0CBV)*OW@_mn<98LOk zvsyi)$o=I+HA=)FmhW)?_huHcKbE5(gY}$0_!=W<-;9s{Hkkb8M_8#$BU)MLIj6|f z)PkW|D;}>@XCNZ3G9g4A(QS3m$+z9+-L_wScIKB{XT0Y6!+X7Bof)rW2hsWHmYS|d zwZ=uGDU`f7x9z-jbluggQimu#x!&6@NVw9e@mw|h4}lV#1iF<^mFxL2PO$H3&*tzS zyJoh#1E;QAp+Y>zqCh0;>*Li{r;LB1iv+_3bce#v4~-JQ-uT$Ca%kJ?O-#mrFhIO# zd)`c;hJ8~w?%&AhugZMBD7E}(PnjG};ck`@r2y~0B~yqaMMh`s>g_Du;q4P%^_4V} z3V(ISQ(li_o-SaLp1k9Z&-C%&cZEYw5+w7EmvmRsszb5ECk?WAeydr0(LjtT+!07I z5C75>$in?uZZq8Z>wffXJK%|6zp1gMOPvQv-8QeT`)d&d*A{1J@+07vmM)Pc4=UWZ z00P`2Jpavtlv!=Er8aF{QP?ax5+oQpiLx_pLj+(0J+3Xp()h|oI1zFhC97O3;ZBZ2O2l&-IfZw@e1c*FC{%5r&_haz^V{1^<7q$PrM8>Ft&+VZ`S312yJ+xQm3<& zhJ8TZI}Lkn<#oR7kafk z-R&iDBB+dR*Ag$(9y1%3iUEqSV>$MeEyfv zPNPDw2_d2vuux7o!rVRQ4J6rSDeMykPta9}C*Y|!j%$5#c z{I)p~y7@XhCY}peM_BLBG5Y3W=2i95z?e2F)gX{pK|KS$PVTyYDvHYX7$&sF$ zHjhI0n{T|Hi5E;0-CxBED7t^Bo!t?z+LN}KtS{Znys?%*vE1(h0iJA0u;p!{jlu@h zTRKEL(ARIWRwCo2bgJBslAMwBMp!g?1E2rwT!g@inhR#?pLsUKNrmA^QS`=}Y_D{^ z!E$m21(!2-bCmqPV<1#+bnC>txn4~<-h8LJ3mqz=iuIqw$BeITq{jCYD?w*`p9k#K z+on_&s|Qw-Sc)lUMGCCSV+J+vG(+XxBG~MkwW!I9%uajRKbK&^{pL$ut9Ja-MP3wWQ@kOLOkaL zHBg)n2(dljSu{i8rja!ULn3Y7vdlS#1{C=bdGwt>{ollL<3O%=ACO8~xjf7_`cG z`*7`_nW1I9mm|yRcVU&LWp0~OJuJI^juX-23HO@MEqhO=ETOd08;Jl3knq+Azx1#A z$mdz+&?PC$vb9~e>iz%>bbCKDhnejRyj#EBC^xUS(m#3la~O{489@fsps6lIdXx(7 z{UW$k_EDqT=Df2Q3ll^1PWwn!5qGgoPGm`(W5%CMIVFpx?~8g(qm71~yL6CsR$qjmiotjkxlHcV_&#ySl1rVc>WwPk+e~} zRcZB;Q@a?*<+5p=pw|5L?z;!wUQgK3O;+u( z`0N%U0VhHtc6_LwW46xItqtKw@(CunlOev809zKDDD#AiXHkImQiBtMepWcgdGPsUtDxA+OZ!Bj1gB^}->m*Y zz^#@krux*<&NRtIm?d+T2aptt&vgw_8pJ;}@-h_+fjFPg1Yuf9-^|Uk`nd%#7&q493HR%>z+Hq4>9C|Q<_oBvWsHA+JA+kd%+Fue|pR6Ow zP2$I52i6aHwJ)H#_-s6TS8nWhxZ%@J6TebT0B;K4H zX+CYnThqPLawN?@t*0{P3+_63nhfHSl7Q-D-|rL3Jb}BJ;t+z~2MP|t(N>Cj&DI}J z?l7r?YrSyZq4)d~Y6&E?Z}?sT!o)wFxc}p4!26rw<@cwPRA5{raAW8bU@&p_+IvM0 z;fgKRkqNyM$Y>7qy!2$L07?Q!3uWRdzmDP3R#7b&@ci@J+*^s~Z@W7gTet7f<_PcB zzG$Lsw;OjDnX_no9i5jL6aO6~xNSL8dL$GBt*MXlg2``&=&L{PR=bO23KH>+o#tFl zccwO&sGH2mu?Zo3j640J9Q9p3t3=%r6vGm69zWwix95?a+KLIyqDF}%Rm*MZ6wBTq z#ekCAZz~0CmkKiphRxX?@}B$3svKIsA)5>ZdKKO}=uQqDbo0u+k-IrAaYevgn9~V| zE@m9dEa8FN#GPSEPcIQ@=HFztkk1P#B@KMl4~bQ#zZx=^TTYMli6`M_budPSuvD9> zRR@m+aZUVwc%YsOJZ~btIeFIreKC~f0xM(3n+`Va_?LRwqeJNfX_rdM{#SE9(dZ|3 z^%y{!fNXh#9~3Q9Tp`e$SDAg&ReM3{x;)op>%OTx zE=k0~)*NPhRB6bJPp}K3gQpaDH9~%s zGd0{yGulAumWr7`ks2sKA`O5hEL_nj8pMq}Th%MyVXV`9WI%<@QRnICcF5=)KzY|- z8u&0`FAvt!yE)}qybZ!_NwGK~AtiC%8jnQGXkxzKjt=>AGa`zh=ywjXQ;IJf3e4)9 z^H;vmL32JT-8K;y0~+uS7@FPPaf!P10bjq&Cw%E|Ev^K;T#rgyUog#6;F!H0BZito z2df#@Nzjy}5j#f(cIzH`ufkx<1_`ygeIr75jBohp`$SlA6;kf+>1KZ-se2z? z?=J2-_e-3&MRRDJdfyj(S3*`qh`c{1?VZ#Zw2uC`kzitcDjF>Q#PMrHm1k0z7+LQD z&RZLl_&^}nC%U3rX4S>Zx_>iO@rnxk)_=Z25jQdZ72{ZMx=e9H3$uC6eaq$Q?4q}g zcs5;aUv)q}m_1}NZ6x!h#&7Rsj)%%+(=^}q+Wh@@P@w<8-Ysl%v{hm_x^3iQe!pi-codon_ z3}~*n(a$N|CZW5ZZ(`TAG}#{|392WphG7KdC|8Ts2{`_XF z`6|W!6A(ZqfgUbr5=n`O81#@xLDYTfPBUfSpWDaR&()F=9P~>p)ZCaEj3w%J!xMN@ zACTR~#-9_jNDNF+Zl~;nytqRrr00E6n_0%AUIJ#GRzss)wmc_Hf8Im3T8t^NGv;_q zEt19Aad>qwA!%V8$7gb`HMhu7^idL1zaEen#dke;*Fz@yD78L`Qv0g;5D+2Qyn z)0A0)>03725(l+c)Fg^8LaTC2WzeRYG-4#x9jz;JZsA8*>o!FxlbX}QI2n`|Ck)hs zYnfST;u(}v#GgzEGqF=>6;#=nzVO$HV&cc{Gxepw;bc4vQMxLyuQ_j}kY zD&eZjp6fXA=~a=lRab70jB`*4UWQ!cWSXtAo>%-Ct0Xqzn$GOWVhu^O2FPsFZs{kZ zpTt|ADpR0T1Q3e!6ZK}U$F78xGc9UC-g4HGW`BO9ObRLa7jvU3$i^D(M!VnzTlF?d z%%U1@xLO^bkLZ^zd($=qZ7joKDX&^eT*z{s8sVuJ$;Lz4skpfs_gElfeVU7~1)Bn! zqsl45Z!q7+t(yB&D26)tDL)aGIiBBT={`XvV?tY2&hB|j1B3p=7KL6D8Ff%$rBqnJ zJ0AU#Rf$um`_%+2qSHq<&A7V5ezLs#Aj_hqLC9g~mG?yckGe&bd!`W|_KCj=`|~z8 zv`$>s3|E8-ml@Wnvar6tYF9M#(JUZ+esA6Qr@Va$A|87_vybQ2)|=BddJwq?!@kYy@k_@u&2YkT zro4josWVl95m4^_L)SD~nK4Re*xR9JodU=|=eCPd829_o2}+|Ek9y+!hsEEORrdI7 z^VK_6*dgy<%>w+PLB(g^TCdMXs?|3mg{WMUGt*X44%!l4ub4_43(yFOquDw#eth!l z#m~9nPu>@gO%AIc{D&PL@8-sA+0glVqssc1U&O7dgH|+);`1C`Q7?u4Wc0nOFYiLR z?u%L`m?Pp?NAeRcpQrOkMeIY@A{;fqMT)PcS4mRQSf#B0AUIrGxhbTBnPcLR%@Jf$ zlpeKL@1C?CKV&AG%Xt!arTY1%?8Q&TBT)dhvhw7?ZAS#fe#$Kp5guq}#>GYV_N0Fr z9CGS1)TUm0=c@XlcIqeV#Oa^YAML62JMA_USia(W{P@nls2~?SRfUlO$2w$1zM`QZfR>A^;IxP-EJx? zfWK>{P~3n4Dd?CdL(e=<$JVb;7flgo(gT}!Rr^@1k+He3*z0g*WDJ@&u>u@Hzu;?W z#}zm#jz~{DXqcjIf__OX`x~ECajP&_hl?rrZe*wSegC0dD`_vbK^EwB=>-M7RJb8G z*x)>07}wE3XVbm}e<2DKGTSf5i;Ae6T(&2y+#5ePLvb;0g9g|n(|m7*NdyHWM`-no z&;`BK*2H;b)2+>FTxoj~_?wAUSmE^IPv2O4Cp0LrfkRN9j{}iystfR0mvm%e=_X)T zqC?*|&0#VT0C$S)TB6AmVcXCi^gE6|p5SDOsbOyNz8>LV92_k3GYDjC4`pWrhZ|Qf z*95`H`7)B94CYK&Y42n^Tj&02@mtULgrIfZADm|g5Bm!8Ec$oyRJ%-D73`X$qfH7Y zu$b#}n{LICHtV)a|6=a7`+?e%xTUP)<|%b?A4!1@VV&U1cs!U>>{+{yS2=@B*=tPN z1rw2%!z}b7WVFFg_AlHbMe)Lu#yokK(jE1sLyz_sCymBuS&WI6T+@(l8bFBxL|D-7|&tS|$V_rIut~uNwZHUSJvR3a& zG9r{rNwDGy91rc({ zk8MHX8>bp6ovqL>4`y&DOh?%^Q?$8M3lYgEXPz?ffMDp^qK7&+poWC7&>H0VsbFL{ zCZ%THsUK5)AIM@o8duEJ|Cvwx5A!ir+G@_ zG0&hrP*+gnwGdKIm#YWA`ApVfzoqD>48%?MZe(W?iM$=`X2lD1xG08CO5eHifm(c# zP?iDWxi?v-#?ak{|Ks?AN+=UFd@C_@WHA-kPY`h1zF!X_xAuId|4s?ORZ-;m8obsf*i7J z3_3OQs9DLdXaDf$p~Cm*rv8gxd%Iw2M$#9IoGe0FjX`MR@gDzx!0KZRx&0#SEttXg zmmzGLdI-`YDp5k7Upw(CZ-jS@%A&pXsM={%B~j_aE%DRzCfIPC13d!I$-O0-;zjdV zOdh{^{h^M$vVmpfjhoZ&S+b*t#iE23!&&@pH-7oqtoYg)($Hs9Iha}EwT|?Mb zZ`|K1o2wWA^jZg8fp6gCKSb}6qfE^ONJOq^kA($O8-Pm#FIQK5D$CK>RQ#r{_ND~; zrOou00vH4QNSoaEeAl}R!#1@7P0CH>k1U^tAgp`*YZP7+WHBhc5}d{Pj^RJaMVqYB zrfEQ7chhMC{Xh$-vCRCekp-mXHkrep&$Q=KwquS|rX>0h(Mjohc#hORA+nI?%?l0Y zX#||Nd4o%z2jR8~!hI)w1KwM-wB-ecb4&nIUADM&x#Q@_j;cNKF5bmAfz2LO`ZlRG zy^f$!bVW}JMBK47OuqyN$qfm_xb0Z&YC_1C*zU1=n9QS6=<>2Y9ouIZ;3T{>hX88`ju}DpxdO;W9dTqW{&yml~DYD2R6#9=siP` zDl;yn$a#s1X;0Yf{r!D%kULKeDzD*;r$H5h_ELtN0&EE>#Uctd~HQ4VX~ef`oBInmsD)k=e9C4 z`&vZ*IZ5UC2Z!=9=1$o>yE93?QCVZtMIx^Ox5Z0J?M15Xt#ywYg3*c>O)I!>?*pzU z&~md#H@Ohf#l?1=bW7M0HRf^mTf{0Ik zmN#z5NV?CO4%J9u$EiQ~^I@`L!cC;eJumfUFxI*)Oqx! zUW<=YgO5twCiCBIuW`N6Nb};$QLvUkyen50ESzp7jEiv8kkvrdM zraG_32=1qX-|DkzRz@Rb9TO!_{LL!8Vc6pnRTT8Hg%-Xaf&r02d=^s=`RvAgb8ETE zdBP_i)|XL{aJ^8VmhO@I@^%_vUc|6n&qhVO+QUd&*+U#eu?g8~ja3hS{}lhpea>)l zmN|b+J}2$5H^YTzhbL40M44b>m-)^r>1qZx+3m^~xT}3xuk$t7&A%P?o4R&L$bX_` ziRU&Z4Js@}Fa;G!J<5OX!=Ctbk%h}Z2L7o~_ad==ep#l>dp?f)fimN8tnm9FTbnyO zq!cdg`4xP=*8c8BvnS}l@J2v0v@88A`j}yB8GR;~y3PKYEq;BeLs(_Y0zhtV+TTML zANKSNf@M=^09-jsoPdh-LBF6EQ%@XM*^L!)yxkA?4(*dfjm}>~FH=lt*!BT|{&8`` zX?};!lAeFAwXVVgi*bBJE!}V269|E2OPdW^BJfKdXxJ_qa6;~mX*2UBOfS>0K}cwy z{whAGn(JP0nOm*}HpvWMfvwLa;6@)J%9ng(HDC6{kx;&r?&~&TbWn98(2vloShqIv zBodzjtdd)sMQ4z6<)@_QA8OX{WQD7GOsIL*(mkielxr2Gsf?_zPoSS6yi!A7e zO)N)AuJN+l?Ri(j{WJTaqA~Q+L~Pn~9nQtRKwK~OhyL;C>W^T!vEM<(0y=QfsOq4s zE_$FkZIP4+A+Ow>{N-(J3Hlb&3qoxATOizrOn+S>YT`zdFJ|i}kTg&A15C>+o}=meIBBk*Ot1f6Yvb3NMrJ-X z8;uPeucvLI;k+~Hw@J=l%yTmN06@_mlwJCKDHUt611w}b7au$CBBDJUk_raOO5IPa z(!8f^Zof5|*#=Zo?#-b>?dM`>ujP;X9q}WsBEUc@c4jhvLz%@i*SUUW#t|px$O7FHki%hN@lk9xIKYq320%8%^ zwKGCkh=n>x6SL_)I@cXOp8{Bh^uC&T85g=#E#w2QT>tacHm14p-Y9|@`r(pW_4*hL zVjk@p14A~~sn2>QZ%8Cugkrq^%!ux&P9bEbTPcF}0ds2@c?r2Qjbaq*85qN7eH20k zS;xY*(T$)`30tDLF->U_imlS<8isY+AygFPh!9quc0bcSl#RZCXRhqZ)l{Z-c@RR4XwEJ}+&nSP<;eoA zw62-t)mgOLCOSYlfoEu$w%MI+cn}A8XLncJ@`$VnM(>B=bAJ-} zKRW0Cs%My7-lpZ5QG-KFt=14bB&ljOVKeMvR?`FotgxwvQWo0yBWMn>}WLKx|vhu^VN`RuC z=zE%aocCUz?4&;9cePvt;koTr@C#9Nk2oq5-kI=pQ^|AKP}gbVdu1NmioXldhhKn^ z0#ZcQ;YtEJZ>l%?)(?s^$(I(csdwOiP}kWRdqU%S4gI;cseWk!3oG;t&{`?fv0H|r z+I(5$FXIj5#DST0Xi%LYJk$+2_ROGTn|tkqKVQj9a@eC|@6~HB`1&SybHVs8q{c{i z_qq~ic;A`jZ?#85jl$R3qFMRFsEuREtBBj$f1=VK1G?_Pyv3gi^UdS{ZBG-dABmx^VXVjm+=G99uvvoHSDe03|ml^9g@95%RITOq7==r&UdwTmk z#aN@3(|6*+3AI&(oj9KFm?POblM(%Vz4n%V?X_dQKh~_a>=ZNls0WS&@dX!`clBFy zsi{cHu?b4CMO0gVgAMs)l6=np%?9`45&5IVdJu(_Ju#G;>2TALhxRZX8VrCvDI>+bq-ZxDXeHuEn$M?@GvDmv9VN$zH>X zu8j^z@+Jegt{LNJb1|V$(m&JMax_fs?u~ow&PR`q`Kw`@9i)FNwLS5X80GrlLWF+) zzr%5Fe%mkkD}alHgt#i?FRZkiDC8QZ@JgDYyz>y9H?*|{n2q-G%t%$0({jzf zjyERN*0^=-D^HYa-E7Pk6dGVTK0_WD|B>`((6Rh6_s%50Z5IC7gv#x^X^@z!_Ij=N z07i%Ci#9Mmxh9>hP$fm*fe zg_eS1^H|bW+~E7=1M8z7d2glR+}8SEEC5`Xy{Ao1%!skaW~LNbs-+yK7T8k6-ViOK zV7OU!RYq&v2dU8poal&}eq?3zI}4?A?7ZXRV!@5?{wI>w`w#oMzf_2Px%wvhC89vT z%of*eX4l6FU^azDM`w0xi(;8Wh-pA)>uhW?8~*9SZ3V(WgRy9S%Bh^Gnds)D1iK9# zycjdypU9x86Xz&KUtOXL!uz?qfq5ofht!A-#Z%Tus3q!}i{}yiw>e_vWq*VS#pmdH(Ba8BcUiv$5YK z*J!?0xmtAlm_X82+;${wj!N-%+UHlfN@xcZnui1 z)-M?>(m~YvmDX=L4r}Jkd^oA8Qm#%Ozd4aDc)9_zle>=zE_$-p6xaCB3X%2@__PG) zuZs-po`6W%lA?;HTgbs%;)x}PE{e)#9(_K9^ahmhI1u1r6Meq(SfB4< z8_FWpw#4aLN-H~rSHdg{kr&%`n*=oGM?ly)2SttyoEyD^mbkQTdIc9(pT4te6?mFI z4jjw#PRn%+CUP$<41%bF<(zbqcTO4aH|anVT@Zsf*(#Nyg>{-X6$)c4H)8>=f@ z0s4h+0rNQgU7PJNqm#8HRQf_V_)2Wp*zMX}g=o40>huP~HgLi+l=Fmo?X=irX+HEy zx-n35@BvkXs@+Uk2QE`!pq{Q=u5qD$Zpab8ybP1v$5{UO$nJGcJ~Mzv7aDvHubayK z9H5mp!jXd(pT}V4uIKN4#Uw(=%?fqi!2P(=FAkAv$Rk+6*7|P?PK>9VkAAU!uBtl& zXB~f`Sb^mp`(|T`h-zBj{wG+N_b9TN8Zw?ICFr$ev||%j!S(x}70({n{1fM9V3KG| zg~c}UT0{P*@N7f~djthHLkuE!)(OCBvfgx5p62lPr4B<(+nRkzD zs`&`_MPSU5nR};V;lyC3tulnz*@`9XL-0V2ziSh9Y5$@{qr^Nl*-i%1 z@%JP*@JDhhZ8ViH<_FMDg<|I)(Ow}QMqbminC)li`Di^N$B{7oYgpE(1Q^FZHdejA zeu+v6`90NeWE6J&dFWv1FU|O(hF&gf_aU%mhz`HsW?9!_PJGyhDC$}c@3h?J+*$rDXCJwKK^P%;#>++U{1tLYd?%Pq=Gbq}`tEagC zp4`oN8)TM#4EXSei{*Wj;Z1`1_y%$+_=GYH_*I185Estx8#d*IO4mKZ_;-TrzZCPz z9Npo(#!CHB&l$Lhb2?ZAo;YA6gR7h_nv?qX{l{WFign=CelyCh8E+afZ-dK^<0nkH zrVvWpjf^&h0}1rSnpEXWG1?TK!CZxBS#X}uJDE&)7>nc(*A_Ff$?XkqkvKgVdxT(` zLKJ;Go#f>sbc7){w+LpzVmqEV10y}(gQ8D^4(G8N`y6-wBAE+ig&hBjl%;lpWastU+tAOdJqZ9)sF?ELE3Kr5^yyV4veMtZ zc>b1=z!RIk^=rVKz(2Z(6D`Sx|Li`Hm%7l}n<(&0JAuaErg8tMi&?cUi61Y6v#}R; z8&ONEIH_QA;nl;Nw(jT*7#t~#WFopNJHB+4_Lwa())-7a8Y|JG+UBqWm-)8`+XXF$jZG zkB#qVXr&JE4*E+5udjc&3Q*V+p8vAa72UnoH{XL8gCDh^y*C>j?TyA!^C9j^sqZsG zrbDC79``DYNy_S#+NZhBgndm<{*pfoiO?QVzOFOi^r07X*7>aO|7jt(UGk1@-aChM z$*sc{@pmNuEy{23CdM`?V3QtX_r-$jJ{`dnv0gfgfqaYBkYmpr{6XW>UbIKrr6Ue& z{XLGl*2f0K0d*YT7t4Iy?OsN13Fi`xyu{PvRnF}hh96o-7~BH$L{K7q`W+ zx{5O&<6e&-_wKQ(6)sHU^_XefXe39R6iemhroH6WQrPwj9hhqd%6k|g91dAKbK@~CxPv%aBL=2DL;=spanW=2cuwu5CyV;gmuAQ4tW`$7t;sf+Jsp+cbCJRk7DO=0`pb3vDF zKlv>qjo;=_z>Z`7YsQ|H!afdF^g{B`@+{eg364U+m2h^s)~wykiq@Coz1h(QcZn(TXfA{3K(WY4(|IB!+rY_c&GzL3dDQyTKiFF z&Gd*r(;0UE{wV|Mm~L_!f4ZuGkCAs1Xuj3yGViHxvb)uuIeZvYxv%tLz}#Aa5skey zmdC`UFMxl7C9-RE<5`|X5MyU@e1CJxk016eZ{qGx?$pdOyQ;b0=9?d%*O|E|hp-E3 z+@HD$g=2r!DV!qc*o!x48<)X_{a89WywT&&1GN3-`5vBi=V?3T?}Ui>CvW7go*u5x z)_jE?=c~*+J_6D>A+_Q+J=^z=77Y(qd$>STw>$>|zkw&-LE6w#Mq-Jifs!xwghoga ztk9VUFWn;lJ*9a;7kqVTbkvR@mR<}b$@hHt(8AS@7hpSPyu5wQk8ZqJ3aXw5Dub8n zD4))3rFiL`AAkRiovFrX+JZe)rRI4K*T|DLSXs)B0CqIDM7W7d7)*c>q8!lTME-?< zy!*wWdDqHfD8o0`_5Ng=O212&+{D|w1LgkB^^MvW-QQTF-H=n0d6`R%2tvvyooC!^ zJ(+SnGkxC#wp#y1nQ{{qJAv-`IO{;B%vvejfFLl=Yjx z!PnSjvH$*`{$nyd54Z}vSQtV0_2NzbsvqZP*?&LO|F<*#e?1KN>n6(m`#=!*`v)_!8(*H-S@xPwQ=nJ0Mvfic+R-yM`16+cEm;P@n(tmoWev=5xDxoU>R{&J`ebbn@ z(1O^L|36P`zDex1S4V@i_lcX8PAKXL0@Nq}={5fC-MYB zL$?5+{cmF4e4C>M5i?d8r{=(RK9hkC&$dqyj1Z%MgyVApB)<#o|MaFHH<{r);ZJYA z9A^+;{_GyFtpym7y-Pp2r53MUw))k@OukczyTPPV;WIT0NGuZAHUd)>o4ek1E-+B6 zMv~VnKG7~VipAg(cWVz@b$_Ol^x5?=+r_kHp%61pxk>6X5& zxO7=Tl&F%4bE~Bpd}CiO`?mj8VWn#Rw99keA$A_cfOj+l6yKdkx_n*H?5{f)uQ8^L zR=#itg@kpv)%VJYd@s{xoI01I#M$6`7?m3qNvI{W0QG~`aTj2`8!5!FC2?J6Dd-Ov;$3Q)gz8n-by?|D&r>gu`pLv!h^`b*KeRXane}ckz+v zH%6&I$i)?UI|iuIl^Un(1=|`sg#dH@g?uCx^L;)shd(p3QGQWLeD1YC(l#`^Q$`;x zVJsosfBS#D@X5iwr#`rBfDNf02N!;OFR5{2!V)r{VU6tN%(JnVL++u#>@ zH|^B$pHng<;txQY;vG_s(ua%H!=K|%K_i`tvqgh1H!BV5q-V$ zx0kt=(6wLBhCmgKTi`fLJ@os$Z%g0f2kev9w zl{nqem>oG8`K>rcKi@h<)%0g9I0SKSl`LhMriI?F@VJ$(Z^1t3Mi^XaHQ!xkXMF$7 zg{NMybrsF=q==4YmzFMh)l9_g1vXVGI-aK%48pqw_W|>P2RiIcCsLihQpuqjTzR8D z{a0+x+3n&fJ=4M@!GaKvB%7Ng_j_DMa}Tk#n+P(AOR(~)^pBqvjN3CpQG0c>ri*jJ z2N8$|N^9$m(E5-zipK_b_Y-bbnX|tHnE$8s_A4n^Ebj~UOMz8Z^L>+6FReE;shSKV ziM^MZ714UN3b$E$8Iy0$LFm2NWrVo8p{h;PfbA-->%CN~GLH-=W@p}?$6&bR8C}U; zRNZO&Dc#c*i$iWRF~=MCFF(dX;1_ZRU=@s+R)G~)#}%C2~nij}^0`CE;yp;*?A z?PA4v5|Q(rGGP!=A?$rRX|Z+W zfX}9)xHXg{+17aPr3S<47Izsu73o$y+BjBhXv?uX8L79pb#<}aNv0;|C>$R0o#5Uf zQ7r6a72A6)G1vF=`TDVqqM3@Yjao%MB4?4^y(GRjF)dn=OtMT^rRi{?x%KdjEb1J4 za?EMsL7|?f8%RRunlF5puer&unsS^?E9UiLYrKGUb2!)i^XR65yPh^wmj_9+?tPrL zhA@5%Tn@3XSwFsP^sU)#mF0A^vF4=e0bVazIQQnsF}<+fu&iZCy%BVmJoiEzt~Xvl zdgjBsd9*49;rzF4>A&q<&oyxH?Bz7S9Faa>FuK_y`4#jz?}%UfT9(S(qBfus_+I6m zU|+mWsFU|HmrUvFlI54>t=Iy9EHzzUbWf_ulp_q+>Nxl4zkHP|FkI5h$qhFT*5^xj zHBspx>Wg*}Io5Z(@vcr4g3a3{fuBswPChc=U%c+d~G1__gwsyDZWzO`T?JJSK`SQHwipj^J zB-czpw#~l{K20nLL%>uiwE2ttVUPZEraV2-pOo)XA!zYK3qM&BMWYz^IPaOZAtCvb zU1iSmB|=^E%W8F1csfkNNZ#M$h<>TVUq`FhU_&g|Z*FVMX^Yk_szPYz(xPglXn10> z3gp^(gS3p^QxGz^(kB+23RBk|Tz_PuB3NV6{5Bo=rassP_L1o4F!4iEc ze)RFbU3dTXP*xW#6*2gm;B2zT^v_ADL@+CwQ(JYi+@=hutU80}y5l)nA6|=C1WH_~ zWyc1TOMijh7dRt32NWLjvwbOvQJ3Qck&`*0i=Aab=Rvr!+Cy5?s_pGWeyi0FMGW2O zljNB`OG6nRJwv5iDj;;n{dD_TtwE!&?hV8;k;n978Nw(}C*xDO!?1yyh}^Z@2YqUa z^W`WOiMpNdL3O1I`q^1JgH_7LsCj>Q*|zcalt}8zXo0p$a)^}(e7Zl0x;TrB-i5eQ zE3R0F%dqa`6yLG${FGBM_E2%1C-vj&+}E!2Wg#G)jm}hBsz2bm&>AP((QsZ6Wu-0j zI|>k^lML!SW`j)o(KZT~`~)1K)+_3he7PN)ufO@MmY$1t!*^ z6K{WydvZ1QOsl|-=;F-sJtsKRWJ*1{z`)Hn2{tc{JlAznUuw4I)NaItY8K3+B`VDa z?Qb~elb)An_#j|9P&cZGdwcBh*HDJWbIgmrktYLpM-kfJC2VB=r;0?38su?Rb%Rj^1aw7-wUn4+?;e#HS)+n5;TYF~;ZB>74%R zTS(lP{^*AmJ#dyV34F7+e94JjgPPnPFE$o_^W7$_)o5msxX$4y4#^6w)ZSG|AJ1r& z@6PHPmi_Q1g0lbH1I4=+JE-}mu(eP3DssApU%sfV&hQ2%J#(QVO=&5w2H7|~vVwX(&`*fqC74pZvmcigZGw|G#}@~G^gQa@iD zX1k`!JoM$2lejN2H96L_yr#mLifRY(daDg6C+{Z!Oa*}P>aO)657zZZ3(9Q`M!U&< zIg&^7XD2k8%0&09BD?g*CMJ6RL?#z@8{4TQLqE1~7orxLti4EVy$Zo$GLxwtboja( ziO4vHo$nL=Pba?jP5w7UDd+z#N(&Nvvu+DWcol!hD$S7?F$uoTj16(Cl@Sd(>Ixgo z+0y*;>Aq~PRN1t9cN+k|bw`ceb9K@qZv+⁡Z2UWE3^?$4>B59_!j`HkEQP%{mS~ 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 LE!C{cW>5bI<2sZ* literal 0 HcmV?d00001 diff --git a/docs/images/urls.png b/docs/images/urls.png new file mode 100644 index 0000000000000000000000000000000000000000..283faf2ac995285b6c09b97f9b132b89d6c50b10 GIT binary patch literal 73697 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 KoXj)782N9uFnO)dIM;1wl6S)PsYovDe5 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

3<#(@=pgEcrXk@q#zp0NRtB47XW>p>i{ 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" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " \n", + "
Lecturer - Healthcare Sciences
\n", + "
(Clinical Bioinformatics)
\n", + "
The University of Manchester
\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Introduction to SPRINT 1 notebook B\n", + "****\n", + "\n", + "## Overview\n", + "This introductory notebook, and subsequent SPRINT_introduction notebooks will continue to introduce the concept of retrieving data from Application Programming Interfaces [API](https://en.wikipedia.org/wiki/Application_programming_interface) which are web-hosted ([web API](https://en.wikipedia.org/wiki/Web_API))\n", + "\n", + "Many bioinformatics tools and data repositories can be accessed using web APIs including NCBI and Ensembl. \n", + "\n", + "Although we cannot hope to demonstrate how each an every useful bioinformatics web API works during this 10 week course, we will give you a broad overview of the tools we use to request data from these resources and the tools we use to make-sense of the data that are returned " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "

Table of Contents

\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Notebook B, section 1: [Introduction to JSON](#json)\n", + "- [What is JSON](#watsit)\n", + "- [The JSON format](#jform)\n", + "- [Reading and writing JSON using Python](#jpy)\n", + "- [Section 1 Summary](#s1s)\n", + "\n", + "#### Notebook B, section 2: [Introduction to REST API](#restapi)\n", + "- [The REST framework](#rest)\n", + "- [Building a simple API: Part A - Build a simple REST API](#builder_a)\n", + "- [Building a simple API: Part B - Request data using Python](#builder_b)\n", + "- [Building a simple API: Part C - Create a new VariantValidator API](#builder_c)\n", + "- [Section 2 Summary and Assignment](#s2s)\n", + "- [Marked assessment](#practical)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
Learning Objective: Create functioning, standards compliant and well documented Python code
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + "

Introduction to the VariantValidator REST API

\n", + "
\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "***\n", + "Image by Peter Causey-Freeman" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "

The REST framework

\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## [An Introduction to APIs](https://restful.io/an-introduction-to-api-s-cee90581ca1b) \n", + "- [Gonzalo Vázquez](https://restful.io/@gonzalovazquez)\n", + "- [Restful Web](https://restful.io/) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "

Using the VariantValidator REST API

\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### API structure\n", + "\n", + "#### Framework\n", + "The VariantValidator REST API is built on the [Flask](https://en.wikipedia.org/wiki/Flask_\\(web_framework) web framework. \n", + "\n", + "The REST components are built using [Flask-RESTPlus](https://flask-restplus.readthedocs.io/en/stable/)\n", + "\n", + "> Flask-RESTPlus is an extension for Flask that adds support for quickly building REST APIs. Flask-RESTPlus encourages best practices with minimal setup. \n", + "\n", + ">It provides a coherent collection of tools to describe your API and expose its documentation properly (using Swagger).\n", + "\n", + "#### Namespaces and Endpoints\n", + "\n", + "The VariantValidator REST API has several tool-sets. Each set is divided into separate namespaces. \n", + "\n", + "For example, the namespace \"hello\" is used to test whether our services are up-and running. The namespaces and endpoints are most easily demonstrated by looking at the Swagger documented API on [https://rest.variantvalidator.org/](https://rest.variantvalidator.org/).\n", + "\n", + "The namespaces are\n", + "- VariantValidator; Core [VariantValidator](https://github.com/openvar/variantValidator) Python library\n", + "- VariantFormatter; [VariantFormatter](https://github.com/openvar/variantFormatter/tree/develop) extension library\n", + "- LOVD; Adapted endpoint for LOVD specific access to our resourced\n", + "- hello; Simple handshake allowing external users to test whether services are alive before submission\n", + "\n", + "Swagger documentation displays the namespaces as follows" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![title](images/ns_and_ep.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each namespace contains endpoints which access specific functions of the VariantValidator libraries. For example the VariantValidator namespace has 3 endpoints\n", + "- gene2transcripts\n", + "- hgvs2reference\n", + "- variantvalidator\n", + "\n", + "### Building Queries\n", + "\n", + "In this interactive mode, the endpoint can be clicked allowing us to access a human-friendly query builder\n", + "\n", + "![title](images/query_builder.png)\n", + "\n", + "Currently the data can be returned in 2 different formats, JSON and XML. These are selected using the `Select the response format` drop-down menu. \n", + "\n", + "For this example I have selected the simple `gene2transcripts` endpoint which searches for all transcripts associated with a particular gene. The documentation tells us that me must input either a HGNC compliant gene symbol or a RefSeq transcript ID. However, this documentation will be improved because the tool also accepts RefSeq transcript IDs without version numbers, LRG IDs (*e.g.* LRG_1) and LRG transcript IDs (*e.g.* LRG_1t1). \n", + "\n", + "Once all the required fields are populated we can execute the query" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The API response\n", + "\n", + "Let's take a look at the response which Swagger has parsed into a user-friendly web page.\n", + "\n", + "![title](images/response.png)\n", + "\n", + "#### Server responses\n", + "1. [Response code](https://developer.amazon.com/docs/amazon-drive/ad-restful-api-response-codes.html) 200\n", + "2. The Response headers provide additional response metadata, *e.g.* the content-type and the time of the response\n", + "3. Response body, *i.e.* the JSON or XML the endpoint returns\n", + "\n", + "### The API query URLS\n", + "\n", + "Swagger also displays queries that can be used to trigger the response in a standard format, *i.e.* a non-interactive mode.\n", + "\n", + "![title](images/urls.png)\n", + "\n", + "#### curl\n", + "\n", + "curl is generally used in terminals and programming\n", + "\n", + "In this screen shot I have used a terminal to request data directly from the VariantValidator API using the provided curl. I have piped this into `python -m json.tool` to provide a pretty JSON display.\n", + "\n", + "The full request is `curl -X GET \"https://rest.variantvalidator.org/VariantValidator/tools/gene2transcripts/COL1A1?content-type=application%2Fjson\" -H \"accept: application/json\" | python -m json.tool`\n", + "\n", + "![title](images/curl.png)\n", + "\n", + "#### web URL\n", + "The web URL can simply be pasted into a browser and in the next section we will use the web URL to recover data from the VariantValidator API using Python\n", + "\n", + "![title](images/web.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "

Request data using Python

\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The Requests module\n", + "[Requests](https://2.python-requests.org/en/master/)\n", + "> Requests: HTTP for Humans™\n", + "\n", + "> Requests is the only HTTP library for Python safe for human consumption\n", + "\n", + "***\n", + "Courtesy of the \"requests\" © 2019 Kenneth Reitz [Apache 2 License](https://www.apache.org/licenses/LICENSE-2.0)
\n", + "\n", + "OK, we have to take their word for it, but we are going to use requests because is's simple, easy to understand and is well maintained\n", + "\n", + "#### Method\n", + "\n", + "1. Install requests into your environment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "! pip install requests" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Import modules we will use" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import requests\n", + "import json" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Create a simple function that calls the API using responses\n", + "\n", + " - *Note: This function is in a format that can be expanded*" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "base_url = 'http://127.0.0.1:5000/'\n", + "def make_request(base_url, api_function):\n", + " # Tell the User the full URL of their call to the rest API\n", + " url = '%s%s' % (base_url, api_function)\n", + " print(\"Querying rest API with URL: \" + url)\n", + " \n", + " # Make the request and pass to a response object that the function returns\n", + " response = requests.get(url)\n", + " return response" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4. Make a request to our API using the function. We need to specify the base_url and the api_function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response = make_request(base_url, 'hello')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "5. Look at the response content\n", + " - response status code" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response.status_code" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " - response headers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response.headers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "5. Finally, extract the body which the requests.json() method formats into Python dictionary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "body = response.json()\n", + "body" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "

Building a simple API: Part C - Create a new VariantValidator API

\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## A bit basic isn't it?\n", + "\n", + "The simple `hello` API is a bit basic, but it does show you how an API works and we have also made requests to our API using Python.\n", + "\n", + "So what if we want to pass some data to the API?\n", + "\n", + "To `application/app_v2` I have added an additional **namespace** called name_space.\n", + "\n", + "I have also added a new API to our REST interface called name\n", + "\n", + "```python\n", + "name_space = application.namespace('name', description='Return a name provided by the user')\n", + "@name_space.route(\"\")\n", + "class NameClass(Resource):\n", + " def get(self, name):\n", + " return {\n", + " \"My name is\" : name\n", + " }\n", + "```\n", + "\n", + "To capture data submitted to the API, we tell the name_space.route to expect a **string** object\n", + "```python\n", + "@name_space.route(\"/\")\n", + "```\n", + "\n", + "and the NameClass Resource to expect the string object\n", + "```python\n", + "def get(self, name):\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "

Have a go

\n", + "\n", + "Activate `app_v2`\n", + "\n", + "```bash\n", + "$ python SPRINT/application/app_v2.py\n", + "```\n", + "\n", + "\n", + "### Exercise\n", + "\n", + "In a browser navigate to [http://127.0.0.1:5000/](http://127.0.0.1:5000/) and see whether you can figure out how to return your name using the API\n", + "\n", + "*Swagger is your friend here. It makes it very simple for a lay user to use an API*\n", + "\n", + "
\n", + "\n", + "### Exercise 2\n", + "\n", + "Now write a script that can make a call to the API and return the JSON that displays your name\n", + "\n", + "*Use the script above as a template. Remember, you may want to make a call to the hello API again, so keep the function flexible*\n", + "\n", + "Once the script is working, print out the response status, headings and JSON\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "

Section 2 Summary and Assignment

\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Summary\n", + "In section 2 of this notebook we have learned about the REST API framework. We have learned how to build a simple REST API of our own. We have briefly touched upon the concept of how Swagger documentation makes APIs accessible to mere humans\\*. We have also learned how to request and make sense of data returned by REST PAIs using the Python requests module\n", + "\n", + "\\**We will look at of Swagger in more detail in week 8 of this unit* " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "cell_style": "split" + }, + "source": [ + "\n", + "\n", + "### Over to you\n", + "\n", + "#### Aim of this exercise\n", + "The aim of this exercise is to keep you into the mindset of working together as a team. We will concentrate on aspects of working in an Agile fashion.\n", + "\n", + "#### Structure your team\n", + "Assign your team roles:\n", + "\n", + "1. **Project lead**\n", + " - Initiate the project on Git Issues (Note, there are two separate short projects here)\n", + " - Lead the group discussion in Git Issues and Slack\n", + " - Provide final feedback on the group's activities and close the issue\n", + " \n", + "\n", + "2. **Team members**\n", + " - Coders who will be responsible for writing the Python functions\n", + " - Testers who will be responsible for testing the code and providing feedback to the coders\n", + "\n", + "***We recommend ensuring that you most experienced coders work with your least experienced coders. Don't forget, this is a team assignment, if you can't figure out how to do something, ask your team on Slack!***" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "cell_style": "split" + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "cell_style": "split" + }, + "source": [ + "### Work-flow\n", + "\n", + "1. Group leader creates an issue on Git Issues\n", + "2. The coders will work together to write the module\n", + "3. The testers will review the final code and test the code. Feedback will be given to the coders within the Git issue\n", + "4. Once the coding is completed and tested, the project lead will summarise the key work-flow points and close the issue\n", + "\n", + "**Details about how the assignment will be marked can be found [here](LINK)**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "\n", + "### Team Assignment\n", + "\n", + "***Remember, you are working as a team. Make sure you assign tasks in an agile way***\n", + "\n", + "#### Coding Workflow\n", + "1. In `applications/app_v3.py` I have created a `vv_space` namespace and `VariantValidatorClass` resource (Endpoint). Your task is to replace all the sections of the module marked \\_\\_\\_\\_\\_ (5 underscores) with actual code. The namespace requires 3 variables.\n", + "\n", + "When you have finished filling in the blanks, the answers can be found in `app_v4.py`\n", + "\n", + "
\n", + "\n", + "***Refer to the existing [VariantValidator REST API](https://rest.variantvalidator.org/webservices/variantvalidator.html#!/variantvalidator/VariantValidator)***\n", + "\n", + "2. In `applications/app_v3.py` create a new namespace and Endpoint that incorporates and returns the data from the function you created in SPRINT_1_introduction_a. When you are creating the namespace route, add a field that allows the user to select whether or not the sequences your function returns are displayed.\n", + " \n", + "*Note: for non-coding transcripts some of these fields will need to return None*\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Concluding remarks\n", + "We will cover methods for reading and writing JSON data to-and-from files in week_6, but a key aspect of learning to program is learning to use the internet to find out how to do things. Google and stack overflow are your friends!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 3ad928b..3a07abe 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -4,16 +4,14 @@ # Import modules from flask import Flask, request -try: - from .endpoints import api, representations, exceptions, request_parser -except ModuleNotFoundError: - from endpoints import api, representations, exceptions, request_parser +from rest_VariantValidator.endpoints import api, representations, exceptions, request_parser from logging import handlers import time import logging.config from configparser import ConfigParser from VariantValidator import settings as vv_settings + # Change settings based on config config = ConfigParser() config.read(vv_settings.CONFIG_DIR) diff --git a/setup.py b/setup.py index 2151dda..95271f8 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,7 @@ install_requires=[ "flask", "flask-restplus", + "Werkzeug==0.16.1", "gunicorn", "httplib2>=0.9.0", "configparser>=3.5.0", @@ -55,6 +56,9 @@ "mysql-connector-python", "requests", "dicttoxml", + "notebook", + "jupyter_contrib_nbextensions", + "jupyter_nbextensions_configurator", "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs", "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@master#egg=VariantFormatter", diff --git a/vdb_docker.df b/vdb_docker.df index d7f7802..003aaa0 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -10,4 +10,4 @@ ENV MYSQL_PASSWORD var1ant RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget https://leicester.figshare.com/ndownloader/files/17714429 -O /docker-entrypoint-initdb.d/validator_2019-09-18.sql.gz +RUN wget https://leicester.figshare.com/ndownloader/files/21717135 -O /docker-entrypoint-initdb.d/validator_2020-02-13.sql From d1aace121aef34fe5e0b6dd1bf846513c0c42618 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 2 Jun 2020 16:01:58 +0100 Subject: [PATCH 044/218] Sort our issues with Werkzeug version --- Dockerfile | 2 +- docker-compose.yml | 13 ++--- docs/DOCKER.md | 54 ++++++++++++++----- .../endpoints/variantvalidator_endpoints.py | 7 ++- 4 files changed, 54 insertions(+), 22 deletions(-) diff --git a/Dockerfile b/Dockerfile index 74ced38..a9540ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,4 +24,4 @@ COPY configuration/docker.ini /root/.variantvalidator # Start the application with gunicorn # CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_VariantValidator/ -# CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --chdir ./rest_VariantValidator/ \ No newline at end of file +CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --chdir ./rest_VariantValidator/ \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 5a16a3a..e25e544 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,17 +6,17 @@ services: context: . dockerfile: vdb_docker.df ports: - - "3306:3306" + - "33060:3306" expose: - - "3306" + - "33060" uta: build: context: . dockerfile: uta_docker.df ports: - - "5432:5432" + - "54320:5432" expose: - - "5432" + - "54320" seqrepo: image: biocommons/seqrepo:2018-08-21 volumes: @@ -33,7 +33,8 @@ services: ports: - "5000:5000" - "8000:8000" - - "8080:8080" - + expose: + - "5000" + - "8000" volumes: seqdata: \ No newline at end of file diff --git a/docs/DOCKER.md b/docs/DOCKER.md index bdb9822..c3b9815 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -28,10 +28,13 @@ Edit the file configuration/docker.ini You will need to provide an email address and an [Entrez API key](https://ncbiinsights.ncbi.nlm.nih.gov/2017/11/02/new-api-keys-for-the-e-utilities/) -*Note: If you have MySQl and or Postgres databases already running, you may encounter a build error e.g. -"listen tcp 0.0.0.0:33060: bind: address already in use" In this case you will need to alter the ports used in the -docker-comose.yml file. -The relevant section is shown here* +*Note: If you have MySQL and or Postgres databases already running, you may encounter an error during the following +build stage e.g.* + +> "ERROR: for vdb Cannot start service vdb: Ports are not available: listen tcp 0.0.0.0:3306: bind: address already in use" + +*In this case you will need to alter the ports used in the docker-comose.yml file* +*The relevant section is shown here with recommended changes* ```yml services: vdb: @@ -52,7 +55,33 @@ services: - "5432" ``` -*Note: configuration can be updated (see below for details)* +*Note: You may encounter a build error relating to other unavailable ports e.g.* + +> "Cannot start service restvv: Ports are not available: listen tcp 0.0.0.0:8080: bind: address already in use" + +*In this case you will need to alter the ports used in the docker-comose.yml file* +*The relevant section is shown here with recommended changes* + +```yml + restvv: + build: . + depends_on: + - vdb + - uta + volumes: + - seqdata:/usr/local/share/seqrepo + ports: + - "5000:5000" + - "8000:8000" + # - "8080:8080" +``` + +*If you encounter these issues, stop the build by pressing `ctrl+c` then run* + +```bash +$ docker-compose down +$ docker-compose up --force-recreate +``` ## Install and build @@ -81,22 +110,19 @@ You run the API directly in the docker container directly via bash ```bash $ docker-compose run restvv bash ``` -Then start the REST services manually, bound to one of the following commands. Note, if you get an error saying +tart the REST services manually, bound to one of the following commands. Note, if you get an error saying there is a conflict on for example port 8000, try starting with an alternate version of the commands provided ```bash -# port 8080 -$ gunicorn -b 0.0.0.0:8080 app --workers=3 --threads=5 --chdir ./rest_VariantValidator/ +# Start +$ docker-compose up -# port 5000 -$ gunicorn -b 0.0.0.0:5000 app --workers=3 --threads=5 --chdir ./rest_VariantValidator/ +# Shutdown +ctrl + c -# port 8000 -$ gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --chdir ./rest_VariantValidator/ ``` - ## Access rest_variantValidator In a web browser navigate to -[http://0.0.0.0:8080](http://0.0.0.0:8080) +[http://0.0.0.0:8000](http://0.0.0.0:8000) ***Note: you may need to change :8080 to one of :5000 or :8000 depending on the activation command*** ## Stop the app and exit the container diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index d19e594..f534748 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -2,6 +2,7 @@ from flask_restplus import Namespace, Resource from . import request_parser from . import representations +from . import exceptions # Import VariantValidator code import VariantValidator @@ -77,7 +78,11 @@ class Gene2transcriptsClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) def get(self, gene_query): - content = vval.gene2transcripts(gene_query) + try: + content = vval.gene2transcripts(gene_query) + except ConnectionError: + message = "Cannot connect to rest.genenames.org, please try again later" + raise exceptions.RemoteConnectionError(message) # Collect Arguments args = parser.parse_args() From 30675fbd478ce4c3394074fda5e993b4e93918cd Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 1 Oct 2020 13:48:04 +0100 Subject: [PATCH 045/218] Update for new vv db location --- vdb_docker.df | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdb_docker.df b/vdb_docker.df index d7f7802..2ed7d23 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -10,4 +10,4 @@ ENV MYSQL_PASSWORD var1ant RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget https://leicester.figshare.com/ndownloader/files/17714429 -O /docker-entrypoint-initdb.d/validator_2019-09-18.sql.gz +RUN wget https://github.com/openvar/VV_databases/blob/master/validator/validator_2020-10-01.sql From fbd2ed6f468054509a70f0d5965a4b68621c476d Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 22 Oct 2020 11:44:21 +0100 Subject: [PATCH 046/218] tweak to gz --- vdb_docker.df | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdb_docker.df b/vdb_docker.df index 2ed7d23..dfd6c31 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -10,4 +10,4 @@ ENV MYSQL_PASSWORD var1ant RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget https://github.com/openvar/VV_databases/blob/master/validator/validator_2020-10-01.sql +RUN wget https://github.com/openvar/VV_databases/blob/master/validator/validator_2020-10-01.sql.gz From 58c8715ac5a2a4c653821c00f615b65433f8c6b1 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 22 Oct 2020 11:46:58 +0100 Subject: [PATCH 047/218] change to gz --- configuration/docker.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration/docker.ini b/configuration/docker.ini index 3462c5c..01587b8 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -22,7 +22,7 @@ console = DEBUG file = ERROR [Entrez] -email = admin@variantvalidator.org +email = 'YOUR_EMAIL' api_key = 'YOUR_API_KEY' From 60646662243d1e0cea3a2bd6fbc0f8e4069870d6 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 22 Oct 2020 11:50:37 +0100 Subject: [PATCH 048/218] Mare changes --- rest_VariantValidator/vdb_docker.df.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 rest_VariantValidator/vdb_docker.df.py diff --git a/rest_VariantValidator/vdb_docker.df.py b/rest_VariantValidator/vdb_docker.df.py new file mode 100644 index 0000000..84b91d8 --- /dev/null +++ b/rest_VariantValidator/vdb_docker.df.py @@ -0,0 +1,13 @@ +FROM mysql:latest + +ENV MYSQL_RANDOM_ROOT_PASSWORD yes + +ENV MYSQL_DATABASE validator + +ENV MYSQL_USER vvadmin + +ENV MYSQL_PASSWORD var1ant + +RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* + +RUN wget https://github.com/openvar/VV_databases/blob/master/validator/validator_2020-10-01.sql.gz \ No newline at end of file From 9726d6a3a9c214143a9a4be92469bdfa7f67b8d6 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 22 Oct 2020 11:57:50 +0100 Subject: [PATCH 049/218] Re make vdb_docker.df --- rest_VariantValidator/vdb_docker.df.py => vdb_docker.df | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename rest_VariantValidator/vdb_docker.df.py => vdb_docker.df (100%) diff --git a/rest_VariantValidator/vdb_docker.df.py b/vdb_docker.df similarity index 100% rename from rest_VariantValidator/vdb_docker.df.py rename to vdb_docker.df From 78820ee0edfcb0d126587779cb962b46c494e79f Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 28 Oct 2020 16:04:15 +0000 Subject: [PATCH 050/218] fix vdb_docker.df to include /docker-entrypoint-initdb.d/validator_2020-10-01.sql.gz --- vdb_docker.df | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdb_docker.df b/vdb_docker.df index 84b91d8..0d0c61d 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -10,4 +10,4 @@ ENV MYSQL_PASSWORD var1ant RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget https://github.com/openvar/VV_databases/blob/master/validator/validator_2020-10-01.sql.gz \ No newline at end of file +RUN wget https://github.com/openvar/VV_databases/blob/master/validator/validator_2020-10-01.sql.gz -O /docker-entrypoint-initdb.d/validator_2020-10-01.sql.gz \ No newline at end of file From 2687a3eba4bf98505522325dd807a5c3640b92f8 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 28 Oct 2020 18:36:28 +0000 Subject: [PATCH 051/218] change to unzipped database in docker --- vdb_docker.df | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdb_docker.df b/vdb_docker.df index 0d0c61d..e1aff2b 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -10,4 +10,4 @@ ENV MYSQL_PASSWORD var1ant RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget https://github.com/openvar/VV_databases/blob/master/validator/validator_2020-10-01.sql.gz -O /docker-entrypoint-initdb.d/validator_2020-10-01.sql.gz \ No newline at end of file +RUN wget https://github.com/openvar/VV_databases/raw/master/validator/validator_2020-10-01.sql -O /docker-entrypoint-initdb.d/validator_2020-10-01.sql \ No newline at end of file From 43e2b8aa76ca4e4e9202182070ac118261a87cdb Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 29 Oct 2020 12:11:21 +0000 Subject: [PATCH 052/218] fix docker --- Dockerfile | 2 +- docs/DOCKER.md | 5 +++++ vdb_docker.df | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index a9540ed..17fa3ef 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,4 +24,4 @@ COPY configuration/docker.ini /root/.variantvalidator # Start the application with gunicorn # CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_VariantValidator/ -CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --chdir ./rest_VariantValidator/ \ No newline at end of file +CMD gunicorn -b 0.0.0.0:8000 app --threads=5 --chdir ./rest_VariantValidator/ \ No newline at end of file diff --git a/docs/DOCKER.md b/docs/DOCKER.md index c3b9815..4ca6947 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -112,6 +112,11 @@ $ docker-compose run restvv bash ``` tart the REST services manually, bound to one of the following commands. Note, if you get an error saying there is a conflict on for example port 8000, try starting with an alternate version of the commands provided + +Note: When you start the rest services you may see the warning +`rest_variantvalidator_seqrepo_1 exited with code 0` +This does not mean that rest_vv is not running. Navigate to [http://0.0.0.0:8000](http://0.0.0.0:8000) and test to see +whether it is running ```bash # Start $ docker-compose up diff --git a/vdb_docker.df b/vdb_docker.df index e1aff2b..eae5dee 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -10,4 +10,4 @@ ENV MYSQL_PASSWORD var1ant RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget https://github.com/openvar/VV_databases/raw/master/validator/validator_2020-10-01.sql -O /docker-entrypoint-initdb.d/validator_2020-10-01.sql \ No newline at end of file +RUN wget https://github.com/openvar/VV_databases/raw/master/validator/validator_2020-10-01.sql.gz -O /docker-entrypoint-initdb.d/validator_2020-10-01.sql.gz \ No newline at end of file From 8a94724a8d391ef0c7ebba1084d803bfde1437e6 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 29 Oct 2020 15:40:28 +0000 Subject: [PATCH 053/218] update docs --- docs/INSTALLATION.md | 14 ++++++++------ docs/MANUAL.md | 34 ++++++++++++++-------------------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index 2fee2c9..83e78d4 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -6,6 +6,12 @@ For any other systems, or if you cannot install the databases, we recommend inst ## Pre-requisites Installation requires [VariantValidator](https://github.com/openvar/variantValidator) and [VariantFormatter](https://github.com/openvar/variantFormatter) +## Installing VariantValidator + +When installing we have found that some of the VariantValidator dependencies do not load well using pip. +Instead install VariantValidator first using anaconda and the pre-configured environment.yml +See the [VariantValidator](https://github.com/openvar/variantValidator) installation documentation + ## Download the source code To download the source code simply clone the master branch. @@ -15,13 +21,9 @@ $ git clone https://github.com/openvar/rest_variantValidator $ cd rest_variantValidator ``` -## Python 3.6 environment - -When installing we recommend using a virtual environment, as it requires specific versions of several libraries including python and sqlite. See the [VariantValidator](https://github.com/openvar/variantValidator) installation documentation - ## Installing rest_variantValidator -To install rest_ariantValidator within your virtual environment run: +To install rest_variantValidator within your virtual environment, activate the environment and run: ``` -$ python setup.py install +$ pip install -e . ``` diff --git a/docs/MANUAL.md b/docs/MANUAL.md index bfd9d91..c36ebbd 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -4,13 +4,13 @@ To run rest_variantValidator -### Python +### In dev mode upsing Python ```bash -$ python rest_variantValidator/wsgi.py +$ python wsgi.py ``` -You will be provided with a link which will open rest_variantValidator in your web browser. +You will be provided with a link which will open rest_variantValidator in your web browser. [http://127.0.0.1:5000/](http://127.0.0.1:5000/) ## Swagger documented functions @@ -30,37 +30,31 @@ Mounting rest_variantValidator to an Apache web server requires [mod_wsgi](https Example [Apache configuration](https://modwsgi.readthedocs.io/en/develop/user-guides/quick-configuration-guide.html) +***Note: you will need to configure the file paths in the example below*** + ```apacheconf - WSGIPythonPath /lib:/site-packages + WSGIPythonPath /local/miniconda3/envs/vvenv/lib:/local/miniconda3/envs/vvenv/lib/python3.6/site-packages WSGIDaemonProcess rest_variantValidator user=wwwrun group=www threads=5 - WSGIScriptAlias / /rest_variantValidator/wsgi.py - WSGIPythonHome + WSGIScriptAlias / /local/py3Repos/rest_variantValidator/wsgi.py + WSGIPythonHome /local/miniconda3/envs/vvenv - /rest_variantValidator/> + WSGIProcessGroup rest_variantValidator WSGIApplicationGroup %{GLOBAL} - Order deny,allow - Allow from all + Order allow,deny + Allow from all + Header set Access-Control-Allow-Origin "*" + Header set Access-Control-Allow-Methods "GET" - +LogLevel crit CustomLog /local/apache2/log/access_log for_pound -``` - -## Run in dev mode -To run rest_variantValidator on a dev server -```bash -$ python rest_variantValidator/app.py ``` -In a web-browser navkgate to `0.0.0.0:5000` - -Exit the app by holding `ctrl + c` - ## Additional resources We are compiling a number of jupyter notebook user guides for rest_variantValidator in [rest_variantValidator_manuals](https://github.com/openvar/rest_variantValidator_manuals) \ No newline at end of file From 823b6886316738e673a93c0d50bd1504f6ce151c Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Mon, 9 Nov 2020 15:41:38 +0000 Subject: [PATCH 054/218] update database location for validator --- REQUIREMENTS.txt | 2 +- setup.py | 1 + vdb_docker.df | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index cfa4bae..c863c24 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -1,6 +1,7 @@ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs git+https://github.com/openvar/variantValidator@master#egg=VariantValidator git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter +Werkzeug==0.16.1 flask flask-restplus gunicorn @@ -15,4 +16,3 @@ dicttoxml notebook jupyter_contrib_nbextensions jupyter_nbextensions_configurator -Werkzeug==0.16.1 \ No newline at end of file diff --git a/setup.py b/setup.py index 2151dda..9478deb 100644 --- a/setup.py +++ b/setup.py @@ -45,6 +45,7 @@ # List run-time dependencies here. These will be installed by pip when the project is installed. install_requires=[ + "werkzeug==0.16.1" "flask", "flask-restplus", "gunicorn", diff --git a/vdb_docker.df b/vdb_docker.df index eae5dee..c763bc5 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -10,4 +10,4 @@ ENV MYSQL_PASSWORD var1ant RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget https://github.com/openvar/VV_databases/raw/master/validator/validator_2020-10-01.sql.gz -O /docker-entrypoint-initdb.d/validator_2020-10-01.sql.gz \ No newline at end of file +RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2020-10-01.sql.gz -O /docker-entrypoint-initdb.d/validator_2020-10-01.sql.gz \ No newline at end of file From 64d6d8a44bef2db949e17d93ddfd40b641d3481b Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 12 Jan 2021 11:37:07 +0000 Subject: [PATCH 055/218] Add metadata to hello endpoint to close #47 --- configuration/docker.ini | 4 ++-- rest_VariantValidator/endpoints/hello.py | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/configuration/docker.ini b/configuration/docker.ini index 01587b8..4e2a23b 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -22,8 +22,8 @@ console = DEBUG file = ERROR [Entrez] -email = 'YOUR_EMAIL' -api_key = 'YOUR_API_KEY' +email = admin@variantvalidator.org +api_key = a991783f8c1e9e1958d1e6bd90b6f4046f08 # diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py index 0473048..3b9f628 100644 --- a/rest_VariantValidator/endpoints/hello.py +++ b/rest_VariantValidator/endpoints/hello.py @@ -2,6 +2,9 @@ from . import request_parser from . import representations +# Import VariantValidator code +import VariantValidator +vval = VariantValidator.Validator() """ Create a parser object locally @@ -13,7 +16,8 @@ The assignment of api changes """ -api = Namespace('hello', description='Endpoint to check services are "alive"') +api = Namespace('hello', description='Endpoint to check services are "alive" and display the current software and ' + 'database versions') """ We also need to re-assign the route ans other decorated functions to api @@ -29,24 +33,29 @@ def get(self): # Collect Arguments args = parser.parse_args() + config_dict = vval.my_config() + config_dict['seqrepo_db'] = config_dict['seqrepo_db'].split('/')[-1] # Overrides the default response route so that the standard HTML URL can return any specified format if args['content-type'] == 'application/json': # example: http://127.0.0.1:5000/name/name/bob?content-type=application/json return representations.application_json({ - "status": "hello_world" + "status": "hello_world", + "metadata": config_dict }, 200, None) # example: http://127.0.0.1:5000/name/name/bob?content-type=application/xml elif args['content-type'] == 'application/xml': return representations.xml({ - "status": "hello_world" + "status": "hello_world", + "metadata": config_dict }, 200, None) else: # Return the api default output return { - "status": "hello_world" + "status": "hello_world", + "metadata": config_dict } From cfd08e35bc2b09f1cdd530ef256547f29a08fe59 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 12 Jan 2021 12:38:45 +0000 Subject: [PATCH 056/218] overwrite key --- configuration/docker.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configuration/docker.ini b/configuration/docker.ini index 4e2a23b..37d044d 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -22,8 +22,8 @@ console = DEBUG file = ERROR [Entrez] -email = admin@variantvalidator.org -api_key = a991783f8c1e9e1958d1e6bd90b6f4046f08 +email = +api_key = # From 04824b9de84dea667bce3edd58d8500225f37420 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 14 Jan 2021 10:00:25 +0000 Subject: [PATCH 057/218] Update docker instructions and also add shared directory/volume for development of VV in the container --- Dockerfile | 6 +- REQUIREMENTS.txt | 3 +- docker-compose.yml | 9 +- docs/DOCKER.md | 267 +++++++++++++++++++++++++++++++++------------ setup.py | 2 +- 5 files changed, 212 insertions(+), 75 deletions(-) diff --git a/Dockerfile b/Dockerfile index 17fa3ef..0ae2f0c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # Use an official Python runtime as a parent image -FROM python:3.6 +FROM python:3.8 # Set the working directory to /app WORKDIR /app @@ -8,8 +8,12 @@ WORKDIR /app # Copy the current directory contents into the container's /app directory COPY . /app +# Update apt-get RUN apt-get update +# Install git +RUN apt-get -y install git + # Updrade pip RUN pip install --upgrade pip diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index c863c24..44c0bec 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -1,7 +1,6 @@ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs git+https://github.com/openvar/variantValidator@master#egg=VariantValidator git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter -Werkzeug==0.16.1 flask flask-restplus gunicorn @@ -16,3 +15,5 @@ dicttoxml notebook jupyter_contrib_nbextensions jupyter_nbextensions_configurator +Werkzeug==0.16.1 + diff --git a/docker-compose.yml b/docker-compose.yml index e25e544..5272602 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,6 +30,7 @@ services: - uta volumes: - seqdata:/usr/local/share/seqrepo + - share:/usr/local/share ports: - "5000:5000" - "8000:8000" @@ -37,4 +38,10 @@ services: - "5000" - "8000" volumes: - seqdata: \ No newline at end of file + seqdata: + share: + driver: local + driver_opts: + type: 'none' + o: 'bind' + device: '${HOME}/share' \ No newline at end of file diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 4ca6947..6d5c400 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -1,8 +1,12 @@ -# Docker +# Running rest_VariantValidator in Docker + +## Prerequisites To install rest_variantValidator via Docker, first ensure you have both docker and docker-compose installed. See their [documentation](https://docs.docker.com/compose/install/) for information. + +## Clone the rest_VariantValidator Repository Create a directory collate your cloned repositories. Move into the directory then, clone the repository. ```bash @@ -14,27 +18,72 @@ Once the repository has been cloned, cd into the rest_variantValidator directory $ cd rest_variantValidator/ ``` -If you have cloned the repository previously, update it +If you have cloned the repository previously, update it prior to installing/re-installing using Docker ```bash $ git pull ``` -## Configure +## Configuring the software -***Essential step*** - -Edit the file configuration/docker.ini +Edit the file located in `configuration/docker.ini` You will need to provide an email address and an [Entrez API key](https://ncbiinsights.ncbi.nlm.nih.gov/2017/11/02/new-api-keys-for-the-e-utilities/) -*Note: If you have MySQL and or Postgres databases already running, you may encounter an error during the following -build stage e.g.* + +## Build the container + +*Note: some of these steps take >>1hr to complete depending on the speed of your internet connection, particularly +compiling SeqRepo* + +- Pull images + +```bash +$ docker-compose pull +``` + +- Create a directory for sharing resources between your computer and the container +```bash +$ mkdir ~/share +``` +i.e. a directory called share in your home directory + +- Build + +```bash +$ docker-compose build --no-cache +``` + +- Start the container + - The first time you do this, it will complete the build process, for example, populating the required the databases + - This step can take >>1hour and is complete when you see the message `rest_variantvalidator_seqrepo_1 exited with code 0"` + - When this is completed you will need to shutdown the services and re-start (see below) + +```bash +$ docker-compose up +``` + +- Shutdown services when you want to stop the container + +```bash +ctrl + c +``` + +- Re-start services + +```bash +$ docker-compose up +``` + +### Build errors you may encounter + +***If you have MySQL and or Postgres databases already running, you may encounter an error*** > "ERROR: for vdb Cannot start service vdb: Ports are not available: listen tcp 0.0.0.0:3306: bind: address already in use" -*In this case you will need to alter the ports used in the docker-comose.yml file* -*The relevant section is shown here with recommended changes* +If you encounter these issues, stop the build by pressing `ctrl+c` + +- Reconfigure the ports used in the `docker-comose.yml` file as shown here ```yml services: vdb: @@ -42,25 +91,36 @@ services: context: . dockerfile: vdb_docker.df ports: + # - "33060:3306" - "3306:3306" expose: + # - "33060" - "3306" uta: build: context: . dockerfile: uta_docker.df ports: - - "5432:5432" + - "54320:5432" expose: - - "5432" + - "54320" + ``` +- hash (`#`) the conflicting port and add the new ports as shown above +- force-recreate the container -*Note: You may encounter a build error relating to other unavailable ports e.g.* +```bash +$ docker-compose down +$ docker-compose up --force-recreate +``` -> "Cannot start service restvv: Ports are not available: listen tcp 0.0.0.0:8080: bind: address already in use" +***You may encounter a build error relating to other unavailable ports*** -*In this case you will need to alter the ports used in the docker-comose.yml file* -*The relevant section is shown here with recommended changes* +> "Cannot start service restvv: Ports are not available: listen tcp 0.0.0.0:8000: bind: address already in use" + +If you encounter these issues, stop the build by pressing `ctrl+c` + +- Reconfigure the ports used in the `docker-comose.yml` file as shown here ```yml restvv: @@ -72,88 +132,158 @@ services: - seqdata:/usr/local/share/seqrepo ports: - "5000:5000" - - "8000:8000" - # - "8080:8080" + # - "8000:8000" + - "8080:8080" + expose: + - "5000" + # - "8000" + - 8080 ``` -*If you encounter these issues, stop the build by pressing `ctrl+c` then run* +- hash (`#`) the conflicting port and add the new ports as shown above +- Change the command in Dockerfile to reflect the changes e.g. `CMD gunicorn -b 0.0.0.0:8080 app --threads=5 --chdir ./rest_VariantValidator/` +- force-recreate the container ```bash $ docker-compose down $ docker-compose up --force-recreate ``` -## Install and build +## Accessing and using rest_variantValidator +In a web browser navigate to +[http://0.0.0.0:8000](http://0.0.0.0:8000) -*Note: some of these steps take >>1hr to complete depending on the speed of your internet connection, particularly -compiling SeqRepo* +***Note: you may need to change :8080 to one of :5000 or :8000 depending on whether you altered the default port above*** -```bash -# Pull images -$ docker-compose pull -# Build -$ docker-compose build --no-cache +## Accessing the VariantValidator databases externally +It is possible to access both the UTA and Validator databases outside of docker as they expose the + default PostgreSQL and MySQL ports (5432 and 3306 respectively). In the current set-up it is not possible to + access the seqrepo database outside of docker. + -# Build and load restvv and databases -# This step can take >>1hour and is complete when you see the message -# - "rest_variantvalidator_seqrepo_1 exited with code 0" -$ docker-compose up +## Accessing VariantValidator directly through bash and reconfiguring a container post build +The container hosts a full install of VariantValidator. -# Shutdown -ctrl + c +To start this version you use the command + +```bash +$ docker-compose run restvv bash ``` -## Launch the rest_VariantValidator API -You run the API directly in the docker container directly via bash +When you are finished exit the container + +```bash +$ exit +``` + +#### What you can do in bash mode + +1. Run VariantValidator can be run on the commandline from within the container + - Instructions can be found in the VariantValidator [manual](https://github.com/openvar/variantValidator/blob/master/docs/MANUAL.md) under sections **Database updates** and **Operation** + +2. Start the REST services in development mode, bound to port 5000 + - For example, this is useful if you want to develop new methods and test them + - Note: Under the terms and conditions of our [license](https://github.com/openvar/rest_variantValidator/blob/master/LICENSE.txt) changes to the code and improvements must be made available to the community so that we can integrate them for the good of all our users + - See instructions on VariantValidator development in Docker + + +## Developing VariantValidator in Docker +The container has been configured with git installed. This means that you can clone Repos directly into the container + +To develop VariantValidator in the container + +Start the container ```bash $ docker-compose run restvv bash ``` -tart the REST services manually, bound to one of the following commands. Note, if you get an error saying -there is a conflict on for example port 8000, try starting with an alternate version of the commands provided -Note: When you start the rest services you may see the warning -`rest_variantvalidator_seqrepo_1 exited with code 0` -This does not mean that rest_vv is not running. Navigate to [http://0.0.0.0:8000](http://0.0.0.0:8000) and test to see -whether it is running +ON YOUR COMPUTER change into the share directory + ```bash -# Start -$ docker-compose up +$ cd ~/share +``` -# Shutdown -ctrl + c +Then create a directory for development - ``` -## Access rest_variantValidator -In a web browser navigate to -[http://0.0.0.0:8000](http://0.0.0.0:8000) -***Note: you may need to change :8080 to one of :5000 or :8000 depending on the activation command*** +```bash +$ mkdir DevelopmentRepos +$ cd ~/share/DevelopmentRepos +``` -## Stop the app and exit the container -`ctrl+c` +Clone the VariantValidator Repo ```bash -$ exit +$ git clone https://github.com/openvar/variantValidator.git ``` -Note, that each time one of these commands is run a new container is created. -For more information on how to use docker-compose see their [documentation](https://docs.docker.com/compose/). +Checkout the develop branch -It is possible to access both the UTA and Validator databases outside of docker as they expose the - default PostgreSQL and MySQL ports (5432 and 3306 respectively). In the current set-up it is not possible to - access the seqrepo database outside of docker. - +```bash +$ git checkout develop +$ git pull +``` -## Accessing VariantValidator and reconfiguring this container -The container hosts a full install of VariantValidator. +Create an new branch for your developments + +```bash +$ git branch name_of_branch +$ git checkout name_of_branch +``` -VariantValidator can be run on the commandline from within the container. +IN THE CONTAINER, pip install the code so it can be run by the container -Instructions can be found in the VariantValidator [manual](https://github.com/openvar/variantValidator/blob/master/docs/MANUAL.md) -under sections **Database updates** and **Operation** +```bash +$ cd /usr/local/share/DevelopmentRepos/variantValidator +$ pip install -e . +``` + +You can then use the containers Python interpreter to run queries, e.g. + +```python +import json +import VariantValidator +vval = VariantValidator.Validator() +variant = 'NM_000088.3:c.589G>T' +genome_build = 'GRCh38' +select_transcripts = 'all' +validate = vval.validate(variant, genome_build, select_transcripts) +validation = validate.format_as_dict(with_meta=True) +print(json.dumps(validation, sort_keys=True, indent=4, separators=(',', ': '))) +``` -## Updating rest_variantValidator +## Developing rest_VariantValidator in Docker +The process for cloning the repo is the same as for VariantValidator + +```bash +$ cd ~/share/DevelopmentRepos +$ git clone https://github.com/openvar/rest_variantValidator.git +``` + +Also, branches are created in the same way + +```bash +$ git checkout develop +$ git pull +$ git branch name_of_branch +$ git checkout name_of_branch +``` + +Navigating to the Repo is identical + +```bash +$ docker-compose run restvv bash +$ cd /usr/local/share/DevelopmentRepos/rest_variantValidator +``` + +However, instead of running `pip install -e`, we can test the install using the Python development server + +```bash +python rest_variantValidator/app.py +``` + +## Updating rest_variantValidator using docker-compose Update requires that the restvv container is deleted from your system. This is not achieved by removing the container If you are only running rest_variantValidator in docker, we recommend deleting and re-building all containers @@ -176,9 +306,4 @@ $ docker-compose up --force-recreate ***If you choose this option, make sure you see the container restvv being re-created and all Python packages being reinstalled in the printed logs, otherwise the container may not actually be rebuilt and the contained modules may not - update*** - - ## Removing the containers -```bash -$ docker-compose down -``` \ No newline at end of file + update*** \ No newline at end of file diff --git a/setup.py b/setup.py index 9478deb..4e5ce63 100644 --- a/setup.py +++ b/setup.py @@ -45,7 +45,7 @@ # List run-time dependencies here. These will be installed by pip when the project is installed. install_requires=[ - "werkzeug==0.16.1" + "werkzeug==0.16.1", "flask", "flask-restplus", "gunicorn", From fabfa1b4d273fa47aeb9da5e9da1e36173d34d51 Mon Sep 17 00:00:00 2001 From: "Peter J. Freeman" Date: Wed, 7 Apr 2021 16:30:47 +0100 Subject: [PATCH 058/218] Update REQUIREMENTS.txt --- REQUIREMENTS.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index 44c0bec..143c4bb 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -1,6 +1,6 @@ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs -git+https://github.com/openvar/variantValidator@master#egg=VariantValidator -git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter +git+https://github.com/openvar/variantValidator@1.0.4#egg=VariantValidator +git+https://github.com/openvar/variantFormatter@1.0.2#egg=VariantFormatter flask flask-restplus gunicorn From 7327eca8a37be004062785b359475ec5398fe30f Mon Sep 17 00:00:00 2001 From: "Peter J. Freeman" Date: Wed, 7 Apr 2021 16:31:55 +0100 Subject: [PATCH 059/218] Update setup.py --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 4e5ce63..e9fdd2f 100644 --- a/setup.py +++ b/setup.py @@ -57,8 +57,8 @@ "requests", "dicttoxml", "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs", - "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", - "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@master#egg=VariantFormatter", + "VariantValidator @ git+https://github.com/openvar/variantValidator.git@1.0.4#egg=VariantValidator", + "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@1.0.2#egg=VariantFormatter", "biocommons.seqrepo>=0.5.1", ], setup_requires=[ From 5ae2af792d3c285610bc913a20c72ed7c2e930cf Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 13 Apr 2021 08:26:56 +0100 Subject: [PATCH 060/218] Final tweaks for this version. Pre- VV 2.0 --- REQUIREMENTS.txt | 5 +++-- configuration/docker.ini | 4 ++-- docker-compose.yml | 3 ++- docs/DOCKER.md | 4 +++- setup.py | 5 +++-- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index 44c0bec..039be06 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -1,6 +1,7 @@ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs -git+https://github.com/openvar/variantValidator@master#egg=VariantValidator -git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter +git+https://github.com/openvar/variantValidator@v1.0.4#egg=VariantValidator +git+https://github.com/openvar/variantFormatter@v1.0.2#egg=VariantFormatter +biopython==1.74 flask flask-restplus gunicorn diff --git a/configuration/docker.ini b/configuration/docker.ini index 37d044d..4e2a23b 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -22,8 +22,8 @@ console = DEBUG file = ERROR [Entrez] -email = -api_key = +email = admin@variantvalidator.org +api_key = a991783f8c1e9e1958d1e6bd90b6f4046f08 # diff --git a/docker-compose.yml b/docker-compose.yml index 5272602..f713cd9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -37,6 +37,7 @@ services: expose: - "5000" - "8000" + volumes: seqdata: share: @@ -44,4 +45,4 @@ volumes: driver_opts: type: 'none' o: 'bind' - device: '${HOME}/share' \ No newline at end of file + device: '${HOME}/variantvalidator_data/share' \ No newline at end of file diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 6d5c400..791c1eb 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -14,6 +14,7 @@ $ git clone https://github.com/openvar/rest_variantValidator ``` Once the repository has been cloned, cd into the rest_variantValidator directory that the clone creates. + ```bash $ cd rest_variantValidator/ ``` @@ -44,7 +45,8 @@ $ docker-compose pull - Create a directory for sharing resources between your computer and the container ```bash -$ mkdir ~/share +$ mkdir ~/variantvalidator_data +$ mkdir ~/variantvalidator_data/share ``` i.e. a directory called share in your home directory diff --git a/setup.py b/setup.py index 4e5ce63..9c3467b 100644 --- a/setup.py +++ b/setup.py @@ -57,9 +57,10 @@ "requests", "dicttoxml", "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs", - "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", - "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@master#egg=VariantFormatter", + "VariantValidator @ git+https://github.com/openvar/variantValidator.git@v1.0.4#egg=VariantValidator", + "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@v1.0.2#egg=VariantFormatter", "biocommons.seqrepo>=0.5.1", + "biopython==1.74", ], setup_requires=[ "setuptools_scm", From c74fb58313cb7a2b5a18dd74b055216a20983c68 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 4 May 2021 11:47:31 +0100 Subject: [PATCH 061/218] Update logging info so that file logging can be set to False --- rest_VariantValidator/app.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 3a07abe..3d8839d 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -26,15 +26,17 @@ log_console_level = logging.getLevelName(console_level) logger.setLevel(log_console_level) - # We will also log to a file - # Log with a rotating file-handler. This sets the maximum size of the log to 0.5Mb and allows two additional logs - # The logs are then deleted and replaced in rotation - logHandler = handlers.RotatingFileHandler('rest_VariantValidator.log', maxBytes=500000, backupCount=2) - # We want to minimise the amount of information we log to capturing bugs - file_level = config['logging']['file'].upper() - log_file_level = logging.getLevelName(file_level) - logHandler.setLevel(log_file_level) - logger.addHandler(logHandler) + if config['logging']['file'] is not False: + # We will also log to a file + # Log with a rotating file-handler + # This sets the maximum size of the log to 0.5Mb and allows two additional logs + # The logs are then deleted and replaced in rotation + logHandler = handlers.RotatingFileHandler('rest_VariantValidator.log', maxBytes=500000, backupCount=2) + # We want to minimise the amount of information we log to capturing bugs + file_level = config['logging']['file'].upper() + log_file_level = logging.getLevelName(file_level) + logHandler.setLevel(log_file_level) + logger.addHandler(logHandler) """ Create a parser object locally @@ -98,7 +100,8 @@ def log_exception(exception_type): @application.errorhandler(exceptions.RemoteConnectionError) def remote_connection_error_handler(e): # Add the Exception to the log ensuring that exc_info is True so that a traceback is also logged - log_exception('RemoteConnectionError') + if config['logging'].getboolean('log') is True: + log_exception('RemoteConnectionError') # Collect Arguments args = parser.parse_args() @@ -115,7 +118,8 @@ def remote_connection_error_handler(e): @application.errorhandler(404) def not_found_error_handler(e): # Add the Exception to the log ensuring that exc_info is True so that a traceback is also logged - log_exception('NotFoundError') + if config['logging'].getboolean('log') is True: + log_exception('NotFoundError') # Collect Arguments args = parser.parse_args() @@ -132,7 +136,8 @@ def not_found_error_handler(e): @application.errorhandler(500) def default_error_handler(e): # Add the Exception to the log ensuring that exc_info is True so that a traceback is also logged - log_exception('InternalServerError') + if config['logging'].getboolean('log') is True: + log_exception('InternalServerError') # Collect Arguments args = parser.parse_args() From 2255f051405aa7d486cdc187b1221587842bf502 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 4 May 2021 12:27:24 +0100 Subject: [PATCH 062/218] update logging controls --- rest_VariantValidator/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 3d8839d..33eef13 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -26,7 +26,7 @@ log_console_level = logging.getLevelName(console_level) logger.setLevel(log_console_level) - if config['logging']['file'] is not False: + if config['logging']['file'].upper() is not "NOTSET": # We will also log to a file # Log with a rotating file-handler # This sets the maximum size of the log to 0.5Mb and allows two additional logs From 66671865a62797e440a8b3ee8af782e3a764394e Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 4 May 2021 17:48:43 +0100 Subject: [PATCH 063/218] Set logging path correctly --- rest_VariantValidator/app.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 33eef13..d469da9 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -7,10 +7,16 @@ from rest_VariantValidator.endpoints import api, representations, exceptions, request_parser from logging import handlers import time +import os +from pathlib import Path import logging.config from configparser import ConfigParser from VariantValidator import settings as vv_settings +# Set document root +ROOT = os.path.dirname(os.path.abspath(__file__)) +path = Path(ROOT) +parent = path.parent.absolute() # Change settings based on config config = ConfigParser() @@ -26,17 +32,18 @@ log_console_level = logging.getLevelName(console_level) logger.setLevel(log_console_level) - if config['logging']['file'].upper() is not "NOTSET": - # We will also log to a file - # Log with a rotating file-handler - # This sets the maximum size of the log to 0.5Mb and allows two additional logs - # The logs are then deleted and replaced in rotation - logHandler = handlers.RotatingFileHandler('rest_VariantValidator.log', maxBytes=500000, backupCount=2) - # We want to minimise the amount of information we log to capturing bugs - file_level = config['logging']['file'].upper() - log_file_level = logging.getLevelName(file_level) - logHandler.setLevel(log_file_level) - logger.addHandler(logHandler) + # We will also log to a file + # Log with a rotating file-handler + # This sets the maximum size of the log to 0.5Mb and allows two additional logs + # The logs are then deleted and replaced in rotation + logHandler = handlers.RotatingFileHandler(str(parent) + '/rest_VariantValidator.log', + maxBytes=500000, + backupCount=2) + # We want to minimise the amount of information we log to capturing bugs + file_level = config['logging']['file'].upper() + log_file_level = logging.getLevelName(file_level) + logHandler.setLevel(log_file_level) + logger.addHandler(logHandler) """ Create a parser object locally From b6eb212274002d0860a49684283be01d64c393c9 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Fri, 23 Jul 2021 20:21:55 +0100 Subject: [PATCH 064/218] Update to run on vv 2.0 includes docker testing --- REQUIREMENTS.txt | 10 +++--- bin/vv_configure.py | 2 -- configuration/docker.ini | 17 ++++++----- docker-compose.yml | 21 ++++++++----- docs/DOCKER.md | 34 +++++++++++++-------- rest_VariantValidator/app.py | 14 +++++++-- rest_VariantValidator/endpoints/__init__.py | 8 ++--- setup.py | 8 ++--- uta_docker.df | 11 ------- vdb_docker.df | 2 +- vvsr_docker.df | 13 ++++++++ vvta_docker.df | 11 +++++++ 12 files changed, 95 insertions(+), 56 deletions(-) delete mode 100644 uta_docker.df create mode 100644 vvsr_docker.df create mode 100644 vvta_docker.df diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index 039be06..98344e1 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -1,6 +1,6 @@ -git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs -git+https://github.com/openvar/variantValidator@v1.0.4#egg=VariantValidator -git+https://github.com/openvar/variantFormatter@v1.0.2#egg=VariantFormatter +git+https://github.com/openvar/vv_hgvs@master#egg=vvhgvs +git+https://github.com/openvar/variantValidator@update_to_vvta#egg=VariantValidator +git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter biopython==1.74 flask flask-restplus @@ -17,4 +17,6 @@ notebook jupyter_contrib_nbextensions jupyter_nbextensions_configurator Werkzeug==0.16.1 - +pytest>=3.6 +pytest-cov +codecov \ No newline at end of file diff --git a/bin/vv_configure.py b/bin/vv_configure.py index f2834a4..b303560 100644 --- a/bin/vv_configure.py +++ b/bin/vv_configure.py @@ -54,8 +54,6 @@ def read_settings(): config.set(section, name, newval.strip()) values_changed = True - print() - if newfile or values_changed: with open(settings['CONFIG_DIR'], 'w') as fh: config.write(fh) diff --git a/configuration/docker.ini b/configuration/docker.ini index 4e2a23b..0ec688d 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -1,29 +1,32 @@ [mysql] host = vdb +port = 3306 database = validator user = vvadmin password = var1ant +version = vvdb_2021_4 [seqrepo] -version = 2018-08-21 +version = VV_SR_2021_2/master location = /usr/local/share/seqrepo [postgres] -host = uta -database = uta -version = uta_20180821 +host = vvta +port = 5432 +database = vvta +version = vvta_2021_2 user = uta_admin password = uta_admin [logging] #Levels control verbosity and can be set to "CRITICAL" "ERROR" "WARNING" "INFO" or "DEBUG". log = True -console = DEBUG +console = WARNING file = ERROR [Entrez] -email = admin@variantvalidator.org -api_key = a991783f8c1e9e1958d1e6bd90b6f4046f08 +email = None +api_key = None # diff --git a/docker-compose.yml b/docker-compose.yml index f713cd9..d174299 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,25 +9,25 @@ services: - "33060:3306" expose: - "33060" - uta: + vvta: build: context: . - dockerfile: uta_docker.df + dockerfile: vvta_docker.df ports: - "54320:5432" expose: - "54320" seqrepo: - image: biocommons/seqrepo:2018-08-21 + build: + context: . + dockerfile: vvsr_docker.df volumes: - seqdata:/usr/local/share/seqrepo - stdin_open: true - tty: true restvv: build: . depends_on: - vdb - - uta + - vvta volumes: - seqdata:/usr/local/share/seqrepo - share:/usr/local/share @@ -39,10 +39,15 @@ services: - "8000" volumes: - seqdata: share: driver: local driver_opts: type: 'none' o: 'bind' - device: '${HOME}/variantvalidator_data/share' \ No newline at end of file + device: '${HOME}/variantvalidator_data/share' + seqdata: + driver: local + driver_opts: + type: 'none' + o: 'bind' + device: '${HOME}/variantvalidator_data/share/seqrepo' \ No newline at end of file diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 791c1eb..2765ced 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -56,27 +56,25 @@ i.e. a directory called share in your home directory $ docker-compose build --no-cache ``` -- Start the container +- Complete build - The first time you do this, it will complete the build process, for example, populating the required the databases - - This step can take >>1hour and is complete when you see the message `rest_variantvalidator_seqrepo_1 exited with code 0"` - When this is completed you will need to shutdown the services and re-start (see below) - + - The build takes a while because the vv databases are large. However, this is a significant improvement on previou + s versions. Install time is approximately 30 minutes (depending on the speed of you computer and internet connection) + - The build has completed when you see the message ***"Successfully built "*** + - example: "Successfully built fc9b83c8d21fa8bdebd52e0e87b9fde967933a043dace1a31916f8106110c8d8 +" + - Then complete the following steps ```bash +# Create the containers (This only takes a coule of minutes) $ docker-compose up -``` - -- Shutdown services when you want to stop the container -```bash +# When you see the following message the containers have been created. +# "vvta_1 | 2021-07-23 16:29:17.590 UTC [1] LOG: database system is ready to accept connections" +# Initial shut down prior to re-launch and working with VarinatValidator in Docker ctrl + c ``` -- Re-start services - -```bash -$ docker-compose up -``` - ### Build errors you may encounter ***If you have MySQL and or Postgres databases already running, you may encounter an error*** @@ -152,9 +150,19 @@ $ docker-compose up --force-recreate ``` ## Accessing and using rest_variantValidator +Start the container +```bash +$ docker-compose up +``` + In a web browser navigate to [http://0.0.0.0:8000](http://0.0.0.0:8000) +When you are finished, stop the container +``` +ctrl + c +``` + ***Note: you may need to change :8080 to one of :5000 or :8000 depending on whether you altered the default port above*** diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 3a07abe..cbca4d1 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -7,10 +7,16 @@ from rest_VariantValidator.endpoints import api, representations, exceptions, request_parser from logging import handlers import time +import os +from pathlib import Path import logging.config from configparser import ConfigParser from VariantValidator import settings as vv_settings +# Set document root +ROOT = os.path.dirname(os.path.abspath(__file__)) +path = Path(ROOT) +parent = path.parent.absolute() # Change settings based on config config = ConfigParser() @@ -27,15 +33,19 @@ logger.setLevel(log_console_level) # We will also log to a file - # Log with a rotating file-handler. This sets the maximum size of the log to 0.5Mb and allows two additional logs + # Log with a rotating file-handler + # This sets the maximum size of the log to 0.5Mb and allows two additional logs # The logs are then deleted and replaced in rotation - logHandler = handlers.RotatingFileHandler('rest_VariantValidator.log', maxBytes=500000, backupCount=2) + logHandler = handlers.RotatingFileHandler(str(parent) + '/rest_VariantValidator.log', + maxBytes=500000, + backupCount=2) # We want to minimise the amount of information we log to capturing bugs file_level = config['logging']['file'].upper() log_file_level = logging.getLevelName(file_level) logHandler.setLevel(log_file_level) logger.addHandler(logHandler) + """ Create a parser object locally """ diff --git a/rest_VariantValidator/endpoints/__init__.py b/rest_VariantValidator/endpoints/__init__.py index e05f42a..0de72aa 100644 --- a/rest_VariantValidator/endpoints/__init__.py +++ b/rest_VariantValidator/endpoints/__init__.py @@ -40,10 +40,10 @@ def specs_url(self): + VariantFormatter.__version__ + "\n" " - [vv_hgvs](https://github.com/openvar/vv_hgvs) version " + config_dict['variantvalidator_hgvs_version'] + "\n" - " - [UTA](https://github.com/biocommons/uta) release " - + config_dict['uta_schema'] + "\n" - " - [SeqRepo](https://github.com/biocommons/biocommons.seqrepo) release " - + config_dict['seqrepo_db'].split('/')[-1] + " - [VVTA](https://www528.lamp.le.ac.uk/) release " + + config_dict['vvta_version'] + "\n" + " - [vvSeqRepo](https://www528.lamp.le.ac.uk/) release " + + config_dict['vvseqrepo_db'].split('/')[-2] ) # Add the namespaces to the API diff --git a/setup.py b/setup.py index 9c3467b..81ac2a8 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # Prefer setuptools over distutils -from setuptools import setup, find_packages +from setuptools import setup setup( name='rest_VariantValidator', @@ -56,9 +56,9 @@ "mysql-connector-python", "requests", "dicttoxml", - "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@1.2.5.vv1#egg=vvhgvs", - "VariantValidator @ git+https://github.com/openvar/variantValidator.git@v1.0.4#egg=VariantValidator", - "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@v1.0.2#egg=VariantFormatter", + "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@master#egg=vvhgvs", + "VariantValidator @ git+https://github.com/openvar/variantValidator.git@update_to_vvta#egg=VariantValidator", + "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@master#egg=VariantFormatter", "biocommons.seqrepo>=0.5.1", "biopython==1.74", ], diff --git a/uta_docker.df b/uta_docker.df deleted file mode 100644 index a7a4216..0000000 --- a/uta_docker.df +++ /dev/null @@ -1,11 +0,0 @@ -FROM postgres:latest - -ENV POSTGRES_DB=uta - -ENV POSTGRES_USER=uta_admin - -ENV POSTGRES_PASSWORD=uta_admin - -RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* - -RUN wget https://leicester.figshare.com/ndownloader/files/17797259 -O /docker-entrypoint-initdb.d/uta_20180821.sql.gz \ No newline at end of file diff --git a/vdb_docker.df b/vdb_docker.df index c763bc5..8c9ba84 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -10,4 +10,4 @@ ENV MYSQL_PASSWORD var1ant RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2020-10-01.sql.gz -O /docker-entrypoint-initdb.d/validator_2020-10-01.sql.gz \ No newline at end of file +RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2021-07-21.sql.gz -O /docker-entrypoint-initdb.d/validator_2021-07-21.sql.gz diff --git a/vvsr_docker.df b/vvsr_docker.df new file mode 100644 index 0000000..4bef0f7 --- /dev/null +++ b/vvsr_docker.df @@ -0,0 +1,13 @@ +FROM ubuntu:latest + +RUN apt-get update + +RUN apt-get install -y wget + +RUN mkdir -p /usr/local/share/seqrepo + +RUN wget --output-document=/usr/local/share/seqrepo/VV_SR_2021_2.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2021_2.tar + +RUN tar -xvf /usr/local/share/seqrepo/VV_SR_2021_2.tar --directory /usr/local/share/seqrepo + +RUN rm /usr/local/share/seqrepo/VV_SR_2021_2.tar \ No newline at end of file diff --git a/vvta_docker.df b/vvta_docker.df new file mode 100644 index 0000000..4a56269 --- /dev/null +++ b/vvta_docker.df @@ -0,0 +1,11 @@ +FROM postgres:12.6 + +ENV POSTGRES_DB=vvta + +ENV POSTGRES_USER=uta_admin + +ENV POSTGRES_PASSWORD=uta_admin + +RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* + +RUN wget --output-document=VTA_2021_2_noseq.sql.gz https://www528.lamp.le.ac.uk/vvdata/vvta/VVTA_2021_2_noseq.psql.gz -O /docker-entrypoint-initdb.d/VVTA_2021_2_noseq.sql.gz From c00612d2c8c10c775b5c067603b42f93be83a2e7 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Mon, 26 Jul 2021 13:18:55 +0100 Subject: [PATCH 065/218] ready to transfer to master. VV locations updated in setup and requirements --- REQUIREMENTS.txt | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index 98344e1..f4f9321 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -1,5 +1,5 @@ git+https://github.com/openvar/vv_hgvs@master#egg=vvhgvs -git+https://github.com/openvar/variantValidator@update_to_vvta#egg=VariantValidator +git+https://github.com/openvar/variantValidator@master#egg=VariantValidator git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter biopython==1.74 flask diff --git a/setup.py b/setup.py index 81ac2a8..436486c 100644 --- a/setup.py +++ b/setup.py @@ -57,7 +57,7 @@ "requests", "dicttoxml", "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@master#egg=vvhgvs", - "VariantValidator @ git+https://github.com/openvar/variantValidator.git@update_to_vvta#egg=VariantValidator", + "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@master#egg=VariantFormatter", "biocommons.seqrepo>=0.5.1", "biopython==1.74", From b669f849409be9edc8d55a25958c7524a8149b86 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 27 Jul 2021 18:03:04 +0100 Subject: [PATCH 066/218] update docker manual --- docs/DOCKER.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 2765ced..0a31c54 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -28,8 +28,14 @@ $ git pull ## Configuring the software Edit the file located in `configuration/docker.ini` -You will need to provide an email address and an -[Entrez API key](https://ncbiinsights.ncbi.nlm.nih.gov/2017/11/02/new-api-keys-for-the-e-utilities/) +You will need to provide an email address + +**Optional (from VariantValidator v2.0.0 - September 2021)** Generate an Entrez API key. This will be necessary if you +do not update your container for more than 12 months; else leave as `None`. See +[Entrez API key](https://ncbiinsights.ncbi.nlm.nih.gov/2017/11/02/new-api-keys-for-the-e-utilities/) for details + +Note: Reconfiguration can be achieved by accessing the docker container through bash. See below for entry and the +VariantValidator [manual](https://github.com/openvar/variantValidator/blob/master/docs/MANUAL.md) for details ## Build the container From 933deff612f0142d28e574e237a9d17b3dc4a0b5 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 5 Aug 2021 11:30:02 +0100 Subject: [PATCH 067/218] Update (C) statements with range of dates --- bin/update_vdb.py | 2 +- bin/variant_validator.py | 2 +- bin/vv_configure.py | 2 +- configuration/docker.ini | 2 +- rest_VariantValidator/__init__.py | 2 +- rest_VariantValidator/app.py | 2 +- rest_VariantValidator/endpoints/__init__.py | 2 +- rest_VariantValidator/endpoints/exceptions.py | 2 +- rest_VariantValidator/endpoints/hello.py | 2 +- rest_VariantValidator/endpoints/lovd_endpoints.py | 2 +- rest_VariantValidator/endpoints/representations.py | 2 +- rest_VariantValidator/endpoints/request_parser.py | 2 +- rest_VariantValidator/endpoints/variantformatter_endpoints.py | 2 +- rest_VariantValidator/endpoints/variantvalidator_endpoints.py | 2 +- setup.py | 2 +- vdb_docker.df | 1 + 16 files changed, 16 insertions(+), 15 deletions(-) diff --git a/bin/update_vdb.py b/bin/update_vdb.py index 6453bc8..f6a3543 100644 --- a/bin/update_vdb.py +++ b/bin/update_vdb.py @@ -16,7 +16,7 @@ update_vv_db.update() # -# Copyright (C) 2019 VariantValidator Contributors +# Copyright (C) 2016-2021 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/bin/variant_validator.py b/bin/variant_validator.py index 88d3531..29bf472 100644 --- a/bin/variant_validator.py +++ b/bin/variant_validator.py @@ -54,7 +54,7 @@ def output_results(valoutput, outformat, with_meta): args.output.write(output_results(output, args.output_format, args.meta) + '\n') # -# Copyright (C) 2019 VariantValidator Contributors +# Copyright (C) 2016-2021 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/bin/vv_configure.py b/bin/vv_configure.py index b303560..3c739d2 100644 --- a/bin/vv_configure.py +++ b/bin/vv_configure.py @@ -59,7 +59,7 @@ def read_settings(): config.write(fh) # -# Copyright (C) 2019 VariantValidator Contributors +# Copyright (C) 2016-2021 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/configuration/docker.ini b/configuration/docker.ini index 0ec688d..048ad4f 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -30,7 +30,7 @@ api_key = None # -# Copyright (C) 2019 VariantValidator Contributors +# Copyright (C) 2016-2021 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/__init__.py b/rest_VariantValidator/__init__.py index 6f5a9f8..7ef5469 100644 --- a/rest_VariantValidator/__init__.py +++ b/rest_VariantValidator/__init__.py @@ -13,7 +13,7 @@ __version__ = None # -# Copyright (C) 2019 VariantValidator Contributors +# Copyright (C) 2016-2021 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index cbca4d1..8c33eba 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -163,7 +163,7 @@ def default_error_handler(e): # -# Copyright (C) 2019 VariantValidator Contributors +# Copyright (C) 2016-2021 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/__init__.py b/rest_VariantValidator/endpoints/__init__.py index 0de72aa..c08527e 100644 --- a/rest_VariantValidator/endpoints/__init__.py +++ b/rest_VariantValidator/endpoints/__init__.py @@ -54,7 +54,7 @@ def specs_url(self): # -# Copyright (C) 2019 VariantValidator Contributors +# Copyright (C) 2016-2021 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/exceptions.py b/rest_VariantValidator/endpoints/exceptions.py index 5cdb740..5028051 100644 --- a/rest_VariantValidator/endpoints/exceptions.py +++ b/rest_VariantValidator/endpoints/exceptions.py @@ -2,7 +2,7 @@ class RemoteConnectionError(Exception): code = 504 # -# Copyright (C) 2019 VariantValidator Contributors +# Copyright (C) 2016-2021 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py index 3b9f628..d4e78fd 100644 --- a/rest_VariantValidator/endpoints/hello.py +++ b/rest_VariantValidator/endpoints/hello.py @@ -60,7 +60,7 @@ def get(self): # -# Copyright (C) 2019 VariantValidator Contributors +# Copyright (C) 2016-2021 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index 7eb3db3..33b550d 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -85,7 +85,7 @@ def get(self, genome_build, variant_description, transcript_model, select_transc # -# Copyright (C) 2019 VariantValidator Contributors +# Copyright (C) 2016-2021 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/representations.py b/rest_VariantValidator/endpoints/representations.py index aa64202..c9d202d 100644 --- a/rest_VariantValidator/endpoints/representations.py +++ b/rest_VariantValidator/endpoints/representations.py @@ -26,7 +26,7 @@ def application_json(data, code, headers): return resp # -# Copyright (C) 2019 VariantValidator Contributors +# Copyright (C) 2016-2021 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/request_parser.py b/rest_VariantValidator/endpoints/request_parser.py index 00f784c..2dcc23f 100644 --- a/rest_VariantValidator/endpoints/request_parser.py +++ b/rest_VariantValidator/endpoints/request_parser.py @@ -11,7 +11,7 @@ # -# Copyright (C) 2019 VariantValidator Contributors +# Copyright (C) 2016-2021 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index 6e03b33..fa59a62 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -72,7 +72,7 @@ def get(self, genome_build, variant_description, transcript_model, select_transc # -# Copyright (C) 2019 VariantValidator Contributors +# Copyright (C) 2016-2021 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index f534748..71b7a14 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -127,7 +127,7 @@ def get(self, hgvs_description): # -# Copyright (C) 2019 VariantValidator Contributors +# Copyright (C) 2016-2021 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/setup.py b/setup.py index 436486c..dfd48de 100644 --- a/setup.py +++ b/setup.py @@ -68,7 +68,7 @@ ) # -# Copyright (C) 2019 VariantValidator Contributors +# Copyright (C) 2016-2021 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/vdb_docker.df b/vdb_docker.df index 8c9ba84..ab57143 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -11,3 +11,4 @@ ENV MYSQL_PASSWORD var1ant RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2021-07-21.sql.gz -O /docker-entrypoint-initdb.d/validator_2021-07-21.sql.gz + From 2c472dd19adae45dcae54e7afdcf0af408784cb9 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 5 Aug 2021 12:30:57 +0100 Subject: [PATCH 068/218] Add batch validator programatic so it can be used in docker --- batch/input.txt | 0 batch/output.txt | 0 bin/batch_validator.py | 101 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 batch/input.txt create mode 100644 batch/output.txt create mode 100644 bin/batch_validator.py diff --git a/batch/input.txt b/batch/input.txt new file mode 100644 index 0000000..e69de29 diff --git a/batch/output.txt b/batch/output.txt new file mode 100644 index 0000000..e69de29 diff --git a/bin/batch_validator.py b/bin/batch_validator.py new file mode 100644 index 0000000..8ed1d1f --- /dev/null +++ b/bin/batch_validator.py @@ -0,0 +1,101 @@ +import os +import sys +import VariantValidator +vval = VariantValidator.Validator() +cwd = os.path.dirname(os.path.abspath(__file__)) + + +# Check Args +if len(sys.argv) != 3: + print('Too few arguments. The command required is: python bin/batch_validator.py genome_build select_transcripts') + exit() + +# Check genome_build +genome_build = sys.argv[1] +genomes = ['GRCh38', 'hg38', 'GRCh37', 'hg19'] +if genome_build not in genomes: + warn = '%s is not a supported genome build' % genome_build + print(warn) + exit() + +select_transcripts = sys.argv[2] + +# Loop through file and detect fails +filepath = cwd.replace('bin', 'batch') +infile = filepath + '/input.txt' +outfile = open(filepath + "/output.txt", "w") +counter = 0 +with open(infile) as fp: + variants = fp.readlines() + for variant in variants: + variant = variant.strip() + print(variant) + try: + validate = vval.validate(variant, genome_build, select_transcripts) + validation = validate.format_as_table(with_meta=True) + if counter == 0: + line_counter = 0 + for line in validation: + if line_counter == 0: + outfile.write(line + '\n') + elif line_counter == 1: + ln_cat = '\t'.join(line) + outfile.write(ln_cat + '\n') + else: + copy_line = [] + for element in line: + if element == '': + copy_line.append('None') + elif element is None: + copy_line.append('None') + else: + copy_line.append(element) + ln_cat = '\t'.join(copy_line) + outfile.write(ln_cat + '\n') + line_counter = line_counter + 1 + else: + line_counter = 0 + for line in validation: + if line_counter == 0: + pass + elif line_counter == 1: + ln_cat = '\t'.join(line) + pass + else: + copy_line = [] + for element in line: + if element == '': + copy_line.append('None') + elif element is None: + copy_line.append('None') + else: + copy_line.append(element) + ln_cat = '\t'.join(copy_line) + outfile.write(ln_cat + '\n') + line_counter = line_counter + 1 + except VariantValidator.modules.utils.VariantValidatorError as e: + outfile.close() + print(variant) + print(e) + exit() + counter = counter + 1 + +print('Processing complete') +outfile.close() + +# +# Copyright (C) 2016-2021 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# From 8370962696105f2c3e5ce514cac603bf4251cee4 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Fri, 20 Aug 2021 13:40:01 +0100 Subject: [PATCH 069/218] Documentation for https://github.com/openvar/variantValidator/issues/298 part A added to swqgger --- rest_VariantValidator/endpoints/variantvalidator_endpoints.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 71b7a14..086e897 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -19,6 +19,10 @@ @api.route("/variantvalidator///") @api.param("select_transcripts", "***Return all possible transcripts***\n" "> all\n" + "\n***Return only 'select' transcripts***\n" + "> select\n" + "> mane_select\n" + "> refseq_select\n" "\n***Single***\n" "> NM_000093.4\n" "\n***Multiple***\n" From c4c4ee50096f98b3459f9371bcea472dcbe95627 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 1 Sep 2021 11:12:40 +0100 Subject: [PATCH 070/218] bug fix in hello endpoint --- rest_VariantValidator/endpoints/hello.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py index d4e78fd..f010ee8 100644 --- a/rest_VariantValidator/endpoints/hello.py +++ b/rest_VariantValidator/endpoints/hello.py @@ -34,7 +34,7 @@ def get(self): # Collect Arguments args = parser.parse_args() config_dict = vval.my_config() - config_dict['seqrepo_db'] = config_dict['seqrepo_db'].split('/')[-1] + config_dict['vvseqrepo_db'] = config_dict['vvseqrepo_db'].split('/')[-2] # Overrides the default response route so that the standard HTML URL can return any specified format if args['content-type'] == 'application/json': From 785aa7a56a83007503cac719e5e32030d627ea4b Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Wed, 17 Nov 2021 11:06:54 +0000 Subject: [PATCH 071/218] fix locust for python3 and remove biopython==1.74 requirement --- REQUIREMENTS.txt | 4 ++-- locust/__pycache__/test_set.cpython-36.pyc | Bin 7684 -> 7675 bytes locust/locust.py | 9 +++++---- locust/test_set.py | 4 +++- setup.py | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index f4f9321..70314c5 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -1,8 +1,8 @@ git+https://github.com/openvar/vv_hgvs@master#egg=vvhgvs git+https://github.com/openvar/variantValidator@master#egg=VariantValidator git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter -biopython==1.74 -flask +biopython +flask==1.1.1 flask-restplus gunicorn biocommons.seqrepo>=0.5.1 diff --git a/locust/__pycache__/test_set.cpython-36.pyc b/locust/__pycache__/test_set.cpython-36.pyc index 3c6321cb5f620d97a396184d20bb5808fbf3e875..dfd094c29f208e81f88de72f4af16989298a74af 100644 GIT binary patch delta 72 zcmZp%`EAW<%*)HQkGnH5Y$NAuNf{UY(BjmhV*P@wG)w(}%H)j1qTGO@{H)aElH$pd cQs#^-n**f+nHha1FP4pBXXdD9sAup30BkT8d;kCd delta 81 zcmexu-D1OO%*)GVGvj$|@J7zplB&M?p~b01#rg$VX_oo{mB|^2MY#b*`B|ySCB^#r lIVtf)sl_Fe1*FUwnKye#1u`>wO`ati#m>Z0&rr|c2>^*&8e0GW diff --git a/locust/locust.py b/locust/locust.py index bfa2f38..fe7635a 100644 --- a/locust/locust.py +++ b/locust/locust.py @@ -1,4 +1,4 @@ -from locust import HttpLocust, TaskSet, task, between +from locust import HttpUser, TaskSet, task, between import test_set """ @@ -23,7 +23,8 @@ """ -class UserBehavior(TaskSet): + +class UserBehavior(HttpUser): """ Endpoint load-testing for the VariantValidator REST API @@ -51,6 +52,6 @@ def profile(self): self.client.get(url) -class WebsiteUser(HttpLocust): +class WebsiteUser(HttpUser): task_set = UserBehavior - wait_time = between(1, 10) # seconds + wait_time = between(1, 10) # seconds diff --git a/locust/test_set.py b/locust/test_set.py index eb61070..42a257b 100644 --- a/locust/test_set.py +++ b/locust/test_set.py @@ -1,5 +1,6 @@ import random + def variant_list(): tests = [ "NC_000016.9:g.2099572TC>T", @@ -327,6 +328,7 @@ def variant_list(): odd_job = tests[random_number] return odd_job + def gene_list(): tests = [ "BRCA1", @@ -340,4 +342,4 @@ def gene_list(): random_number = random.randint(0, 6) random_task = tests[random_number] - return random_task \ No newline at end of file + return random_task diff --git a/setup.py b/setup.py index dfd48de..c2166cf 100644 --- a/setup.py +++ b/setup.py @@ -60,7 +60,7 @@ "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@master#egg=VariantFormatter", "biocommons.seqrepo>=0.5.1", - "biopython==1.74", + "biopython", ], setup_requires=[ "setuptools_scm", From 4b4247c33fe5ccc1fef89985ff0fd3cc5ef4e753 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 23 Nov 2021 12:45:51 +0000 Subject: [PATCH 072/218] begin creating fast validation endpoints - currently variants in journals but may be re-named --- rest_VariantValidator/app.py | 1 + rest_VariantValidator/endpoints/__init__.py | 6 +- .../endpoints/lovd_endpoints.py | 2 +- .../endpoints/variantformatter_endpoints.py | 2 +- .../endpoints/vijs_endpoints.py | 282 ++++++++++++++++++ 5 files changed, 289 insertions(+), 4 deletions(-) create mode 100644 rest_VariantValidator/endpoints/vijs_endpoints.py diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 8c33eba..c6fe3b3 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -39,6 +39,7 @@ logHandler = handlers.RotatingFileHandler(str(parent) + '/rest_VariantValidator.log', maxBytes=500000, backupCount=2) + # We want to minimise the amount of information we log to capturing bugs file_level = config['logging']['file'].upper() log_file_level = logging.getLevelName(file_level) diff --git a/rest_VariantValidator/endpoints/__init__.py b/rest_VariantValidator/endpoints/__init__.py index c08527e..187f31d 100644 --- a/rest_VariantValidator/endpoints/__init__.py +++ b/rest_VariantValidator/endpoints/__init__.py @@ -6,6 +6,7 @@ from .variantvalidator_endpoints import api as ns_vv from .variantformatter_endpoints import api as ns_vf from .lovd_endpoints import api as ns_lovd +from .vijs_endpoints import api as ns_vijs from .hello import api as ns_hello # Obtain VariantValidator related metadata @@ -29,8 +30,8 @@ def specs_url(self): # Define the API as api api = CustomAPI(version=rest_VariantValidator.__version__, - title="rest_VariantValidator", - description="## By continuing to use this service you agree to our terms and conditions of Use\n" + title="rest_VariantValidator", + description="## By continuing to use this service you agree to our terms and conditions of Use\n" "- [Terms and Conditions](https://github.com/openvar/variantValidator/blob" "/master/README.md)\n\n" "## Powered by\n" @@ -50,6 +51,7 @@ def specs_url(self): api.add_namespace(ns_vv) api.add_namespace(ns_vf) api.add_namespace(ns_lovd) +api.add_namespace(ns_vijs) api.add_namespace(ns_hello) diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index 33b550d..99fbf45 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -49,7 +49,7 @@ "> - True - (liftover to all genomic loci)\n" "> - primary - (lift to primary assembly only)\n" "> - False") -class VariantValidatorClass(Resource): +class LOVDClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) def get(self, genome_build, variant_description, transcript_model, select_transcripts, checkonly, liftover): diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index fa59a62..3cb3907 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -40,7 +40,7 @@ "> - True (return ONLY the genomic variant descriptions and not transcript and protein" " descriptions)\n" "> - False") -class VariantValidatorClass(Resource): +class VariantFormatterClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) def get(self, genome_build, variant_description, transcript_model, select_transcripts, checkonly): diff --git a/rest_VariantValidator/endpoints/vijs_endpoints.py b/rest_VariantValidator/endpoints/vijs_endpoints.py new file mode 100644 index 0000000..37a0512 --- /dev/null +++ b/rest_VariantValidator/endpoints/vijs_endpoints.py @@ -0,0 +1,282 @@ +# Import modules +from flask_restplus import Namespace, Resource +from . import request_parser +from . import representations + +# Import variantFormatter +import VariantFormatter +import VariantFormatter.simpleVariantFormatter +import VariantValidator +vval = VariantValidator.Validator() + +""" +Create a list containing common warnings which are not errors +""" +my_warnings = [ + "A more recent version of the selected reference sequence", + "No transcripts found that fully overlap the described variation", + "is pending therefore changes may be made to the LRG reference sequence", + "This coding sequence variant description spans at least one intron", + "RefSeqGene record not available", + "automapped to equivalent RefSeq record", + "Protein level variant descriptions are not fully supported", + "is HGVS compliant and contains a valid reference amino acid description" +] + + +""" +Create a parser object locally +""" +parser = request_parser.parser + +api = Namespace('VariantsInJournals', description='Endpoints to ensure variants submitted to journals are validated') + + +@api.route("/transcript_descriptions//") +@api.param("variant_description", "***HGVS***\n" + "> - NM_000088.3:c.589G>T\n" + "> - LRG_1t1:c.589G>T\n" + "> - *Recommended maximum is 60 variants per submission*\n") +@api.param("genome_build", "***Accepted:***\n" + "> - GRCh37\n" + "> - GRCh38\n" + "> - hg19\n" + "> - hg38") +class VariantValidatorClass(Resource): + # Add documentation about the parser + @api.expect(parser, validate=True) + def get(self, genome_build, variant_description): + + # List the submitted descriptions and the allowed reference sequence types + description_list = variant_description.split() + allowed_references = ["NM_", + "NR_", + "ENST", + "NP_", + "ENSP", + "t", + "p"] + + # Check correct variant type (Currently transcript but also accepts Protein + # even though we do not advertise this + variant_description_outs = [] + for description in description_list: + for reference in allowed_references: + if reference in description: + variant_description_outs.append(description) + variant_description = "|".join(variant_description_outs) + + # Refresh the content + refreshed_content = {} + if variant_description is "": + refreshed_content = {"error": "Unsupported variant type"} + else: + # Validate using the VariantValidator Python Library + validate = vval.validate(variant_description, genome_build, select_transcripts='all') + content = validate.format_as_dict(with_meta=True) + + # Collect Arguments + args = parser.parse_args() + for k, v in content.items(): + if k is "metadata": + refreshed_content[k] = v + elif k is "flag": + continue + else: + refreshed_content[v["submitted_variant"]] = {} + refreshed_content[v["submitted_variant"]]['pass'] = False + refreshed_content[v["submitted_variant"]]['errors'] = [] + refreshed_content[v["submitted_variant"]]['correction'] = None + + # Handle transcript variant inputs + if "p." not in v["submitted_variant"]: + # Filter the errors/warnings i.e. removing any warnings that are not actual errors + # Warnings which are not errors are contained in the my_warnings list + error_found = [] + if v["validation_warnings"] is not []: + for warning in v["validation_warnings"]: + safe_found = False + for safe in my_warnings: + if safe in warning: + safe_found = True + break + if safe_found is False: + error_found.append(warning) + refreshed_content[v["submitted_variant"]]['errors'] = error_found + + # Is the input == to the output? + if "LRG" not in v["submitted_variant"]: + if v["submitted_variant"] == v["hgvs_transcript_variant"]: + refreshed_content[v["submitted_variant"]]['pass'] = True + else: + refreshed_content[v["submitted_variant"]]['correction'] = v["hgvs_transcript_variant"] + else: + if v["submitted_variant"] == v["hgvs_lrg_transcript_variant"]: + refreshed_content[v["submitted_variant"]]['pass'] = True + else: + refreshed_content[v["submitted_variant"]]['correction'] = v["hgvs_lrg_transcript" + "_variant"] + + else: + # Filter the errors/warnings i.e. removing any warnings that are not actual errors + # Warnings which are not errors are contained in the my_warnings list + error_found = [] + if v["validation_warnings"] is not []: + for warning in v["validation_warnings"]: + safe_found = False + for safe in my_warnings: + if safe in warning: + safe_found = True + break + if safe_found is False: + error_found.append(warning) + refreshed_content[v["submitted_variant"]]['errors'] = error_found + + # Is the input == to the output? + if "LRG" not in v["submitted_variant"]: + if v["submitted_variant"] == v["hgvs_predicted_protein_consequence"]["tlr"]: + refreshed_content[v["submitted_variant"]]['pass'] = True + elif v["submitted_variant"] == v["hgvs_predicted_protein_consequence"]["slr"]: + refreshed_content[v["submitted_variant"]]['pass'] = True + else: + if v["hgvs_predicted_protein_consequence"]["tlr"] != "": + refreshed_content[v["submitted_variant"]]['correction'] = v[ + "hgvs_predicted_protein_consequence"]["tlr"] + else: + refreshed_content[v["submitted_variant"]]['correction'] = None + else: + if v["submitted_variant"] == v["hgvs_predicted_protein_consequence"]["lrg_tlr"]: + refreshed_content[v["submitted_variant"]]['pass'] = True + elif v["submitted_variant"] == v["hgvs_predicted_protein_consequence"]["lrg_slr"]: + refreshed_content[v["submitted_variant"]]['pass'] = True + else: + if v["hgvs_predicted_protein_consequence"]["lrg_tlr"] != "": + refreshed_content[v["submitted_variant"]]['correction'] = v[ + "hgvs_predicted_protein_consequence"]["lrg_tlr"] + else: + refreshed_content[v["submitted_variant"]]['correction'] = None + + # Overrides the default response route so that the standard HTML URL can return any specified format + if args['content-type'] == 'application/json': + # example: http://127.0.0.1:5000.....bob?content-type=application/json + return representations.application_json(refreshed_content, 200, None) + # example: http://127.0.0.1:5000.....?content-type=application/xml + elif args['content-type'] == 'application/xml': + return representations.xml(refreshed_content, 200, None) + else: + # Return the api default output + return refreshed_content + + +@api.route("/genomic_descriptions///") +@api.param("variant_description", "***Genomic HGVS***\n" + "> - NC_000017.10:g.48275363C>A\n" + "> - *Recommended maximum is 60 variants per submission*\n") +@api.param("transcript_model", "***Accepted:***\n" + "> - refseq (return data for RefSeq transcript models)\n" + "> - all (currently refseq only)") +@api.param("genome_build", "***Accepted:***\n" + "> - GRCh37\n" + "> - GRCh38\n" + "> - hg19\n" + "> - hg38\n") +class LOVDClass(Resource): + # Add documentation about the parser + @api.expect(parser, validate=True) + def get(self, genome_build, variant_description, transcript_model): + if transcript_model == 'None' or transcript_model == 'none': + transcript_model = None + select_transcripts = None + checkonly = True + liftover = False + + # List the submitted descriptions and the allowed reference sequence types + description_list = variant_description.split() + allowed_references = ["NC_", + "NG_"] + + # Check correct variant type (Currently transcript but also accepts Protein + # even though we do not advertise this + variant_description_outs = [] + for description in description_list: + for reference in allowed_references: + if reference in description: + variant_description_outs.append(description) + variant_description = "|".join(variant_description_outs) + + # Refresh the content + refreshed_content = {} + if variant_description is "": + refreshed_content = {"error": "Unsupported variant type"} + else: + # Validate using the VariantValidator Python Library + content = VariantFormatter.simpleVariantFormatter.format(variant_description, + genome_build, + transcript_model, + select_transcripts, + checkonly, + liftover) + + # Collect Arguments + args = parser.parse_args() + for k, v in content.items(): + if k is "metadata": + refreshed_content[k] = v + else: + for k2, v2 in v.items(): + if k2 is "flag" or k2 is "errors": + continue + + # else + refreshed_content[k2] = {} + refreshed_content[k2]['pass'] = False + refreshed_content[k2]['errors'] = [] + refreshed_content[k2]['correction'] = None + + # Filter the errors/warnings i.e. removing any warnings that are not actual errors + # Warnings which are not errors are contained in the my_warnings list + error_found = [] + if v2["genomic_variant_error"] is not None: + safe_found = False + for safe in my_warnings: + if safe in v2["genomic_variant_error"]: + safe_found = True + break + if safe_found is False: + error_found.append(v2["genomic_variant_error"]) + refreshed_content[k2]['errors'] = error_found + + # Populate the rest of the output + if k2 == v2["g_hgvs"]: + refreshed_content[k2]["pass"] = True + else: + refreshed_content[k2]["correction"] = v2["g_hgvs"] + + # Overrides the default response route so that the standard HTML URL can return any specified format + if args['content-type'] == 'application/json': + # example: http://127.0.0.1:5000.....bob?content-type=application/json + return representations.application_json(refreshed_content, 200, None) + # example: http://127.0.0.1:5000.....?content-type=application/xml + elif args['content-type'] == 'application/xml': + return representations.xml(refreshed_content, 200, None) + else: + # Return the api default output + return refreshed_content + + +# +# Copyright (C) 2016-2021 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# From 9ef580fbe193b70a9a6c7d789d8001710ca6d91e Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 13 Jan 2022 13:36:57 +0000 Subject: [PATCH 073/218] update c date range and close issue #330 --- bin/batch_validator.py | 2 +- bin/update_vdb.py | 2 +- bin/variant_validator.py | 2 +- bin/vv_configure.py | 2 +- configuration/docker.ini | 2 +- rest_VariantValidator/__init__.py | 2 +- rest_VariantValidator/app.py | 2 +- rest_VariantValidator/endpoints/__init__.py | 2 +- rest_VariantValidator/endpoints/exceptions.py | 2 +- rest_VariantValidator/endpoints/hello.py | 2 +- .../endpoints/lovd_endpoints.py | 2 +- .../endpoints/representations.py | 2 +- .../endpoints/request_parser.py | 2 +- .../endpoints/variantformatter_endpoints.py | 2 +- .../endpoints/vijs_endpoints.py | 24 +++++++++---------- setup.py | 2 +- 16 files changed, 26 insertions(+), 28 deletions(-) diff --git a/bin/batch_validator.py b/bin/batch_validator.py index 8ed1d1f..761e26d 100644 --- a/bin/batch_validator.py +++ b/bin/batch_validator.py @@ -84,7 +84,7 @@ outfile.close() # -# Copyright (C) 2016-2021 VariantValidator Contributors +# Copyright (C) 2016-2022 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/bin/update_vdb.py b/bin/update_vdb.py index f6a3543..88c60ff 100644 --- a/bin/update_vdb.py +++ b/bin/update_vdb.py @@ -16,7 +16,7 @@ update_vv_db.update() # -# Copyright (C) 2016-2021 VariantValidator Contributors +# Copyright (C) 2016-2022 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/bin/variant_validator.py b/bin/variant_validator.py index 29bf472..70589f7 100644 --- a/bin/variant_validator.py +++ b/bin/variant_validator.py @@ -54,7 +54,7 @@ def output_results(valoutput, outformat, with_meta): args.output.write(output_results(output, args.output_format, args.meta) + '\n') # -# Copyright (C) 2016-2021 VariantValidator Contributors +# Copyright (C) 2016-2022 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/bin/vv_configure.py b/bin/vv_configure.py index 3c739d2..7e62eb5 100644 --- a/bin/vv_configure.py +++ b/bin/vv_configure.py @@ -59,7 +59,7 @@ def read_settings(): config.write(fh) # -# Copyright (C) 2016-2021 VariantValidator Contributors +# Copyright (C) 2016-2022 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/configuration/docker.ini b/configuration/docker.ini index 048ad4f..d6568ed 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -30,7 +30,7 @@ api_key = None # -# Copyright (C) 2016-2021 VariantValidator Contributors +# Copyright (C) 2016-2022 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/__init__.py b/rest_VariantValidator/__init__.py index 7ef5469..d0d4c13 100644 --- a/rest_VariantValidator/__init__.py +++ b/rest_VariantValidator/__init__.py @@ -13,7 +13,7 @@ __version__ = None # -# Copyright (C) 2016-2021 VariantValidator Contributors +# Copyright (C) 2016-2022 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index c6fe3b3..4d8edcd 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -164,7 +164,7 @@ def default_error_handler(e): # -# Copyright (C) 2016-2021 VariantValidator Contributors +# Copyright (C) 2016-2022 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/__init__.py b/rest_VariantValidator/endpoints/__init__.py index 187f31d..16b5c35 100644 --- a/rest_VariantValidator/endpoints/__init__.py +++ b/rest_VariantValidator/endpoints/__init__.py @@ -56,7 +56,7 @@ def specs_url(self): # -# Copyright (C) 2016-2021 VariantValidator Contributors +# Copyright (C) 2016-2022 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/exceptions.py b/rest_VariantValidator/endpoints/exceptions.py index 5028051..f71b073 100644 --- a/rest_VariantValidator/endpoints/exceptions.py +++ b/rest_VariantValidator/endpoints/exceptions.py @@ -2,7 +2,7 @@ class RemoteConnectionError(Exception): code = 504 # -# Copyright (C) 2016-2021 VariantValidator Contributors +# Copyright (C) 2016-2022 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py index f010ee8..12a8a1c 100644 --- a/rest_VariantValidator/endpoints/hello.py +++ b/rest_VariantValidator/endpoints/hello.py @@ -60,7 +60,7 @@ def get(self): # -# Copyright (C) 2016-2021 VariantValidator Contributors +# Copyright (C) 2016-2022 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index 99fbf45..9e2c403 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -85,7 +85,7 @@ def get(self, genome_build, variant_description, transcript_model, select_transc # -# Copyright (C) 2016-2021 VariantValidator Contributors +# Copyright (C) 2016-2022 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/representations.py b/rest_VariantValidator/endpoints/representations.py index c9d202d..a580e45 100644 --- a/rest_VariantValidator/endpoints/representations.py +++ b/rest_VariantValidator/endpoints/representations.py @@ -26,7 +26,7 @@ def application_json(data, code, headers): return resp # -# Copyright (C) 2016-2021 VariantValidator Contributors +# Copyright (C) 2016-2022 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/request_parser.py b/rest_VariantValidator/endpoints/request_parser.py index 2dcc23f..052dd3a 100644 --- a/rest_VariantValidator/endpoints/request_parser.py +++ b/rest_VariantValidator/endpoints/request_parser.py @@ -11,7 +11,7 @@ # -# Copyright (C) 2016-2021 VariantValidator Contributors +# Copyright (C) 2016-2022 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index 3cb3907..50a2589 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -72,7 +72,7 @@ def get(self, genome_build, variant_description, transcript_model, select_transc # -# Copyright (C) 2016-2021 VariantValidator Contributors +# Copyright (C) 2016-2022 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/vijs_endpoints.py b/rest_VariantValidator/endpoints/vijs_endpoints.py index 37a0512..35b5304 100644 --- a/rest_VariantValidator/endpoints/vijs_endpoints.py +++ b/rest_VariantValidator/endpoints/vijs_endpoints.py @@ -72,11 +72,9 @@ def get(self, genome_build, variant_description): refreshed_content = {"error": "Unsupported variant type"} else: # Validate using the VariantValidator Python Library - validate = vval.validate(variant_description, genome_build, select_transcripts='all') + validate = vval.validate(variant_description, genome_build, select_transcripts='all', liftover_level=None) content = validate.format_as_dict(with_meta=True) - # Collect Arguments - args = parser.parse_args() for k, v in content.items(): if k is "metadata": refreshed_content[k] = v @@ -156,6 +154,9 @@ def get(self, genome_build, variant_description): else: refreshed_content[v["submitted_variant"]]['correction'] = None + # Collect Arguments + args = parser.parse_args() + # Overrides the default response route so that the standard HTML URL can return any specified format if args['content-type'] == 'application/json': # example: http://127.0.0.1:5000.....bob?content-type=application/json @@ -168,13 +169,10 @@ def get(self, genome_build, variant_description): return refreshed_content -@api.route("/genomic_descriptions///") +@api.route("/genomic_descriptions///") @api.param("variant_description", "***Genomic HGVS***\n" "> - NC_000017.10:g.48275363C>A\n" "> - *Recommended maximum is 60 variants per submission*\n") -@api.param("transcript_model", "***Accepted:***\n" - "> - refseq (return data for RefSeq transcript models)\n" - "> - all (currently refseq only)") @api.param("genome_build", "***Accepted:***\n" "> - GRCh37\n" "> - GRCh38\n" @@ -183,9 +181,8 @@ def get(self, genome_build, variant_description): class LOVDClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) - def get(self, genome_build, variant_description, transcript_model): - if transcript_model == 'None' or transcript_model == 'none': - transcript_model = None + def get(self, genome_build, variant_description): + transcript_model = 'all' select_transcripts = None checkonly = True liftover = False @@ -217,8 +214,6 @@ def get(self, genome_build, variant_description, transcript_model): checkonly, liftover) - # Collect Arguments - args = parser.parse_args() for k, v in content.items(): if k is "metadata": refreshed_content[k] = v @@ -252,6 +247,9 @@ def get(self, genome_build, variant_description, transcript_model): else: refreshed_content[k2]["correction"] = v2["g_hgvs"] + # Collect Arguments + args = parser.parse_args() + # Overrides the default response route so that the standard HTML URL can return any specified format if args['content-type'] == 'application/json': # example: http://127.0.0.1:5000.....bob?content-type=application/json @@ -265,7 +263,7 @@ def get(self, genome_build, variant_description, transcript_model): # -# Copyright (C) 2016-2021 VariantValidator Contributors +# Copyright (C) 2016-2022 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/setup.py b/setup.py index c2166cf..6146864 100644 --- a/setup.py +++ b/setup.py @@ -68,7 +68,7 @@ ) # -# Copyright (C) 2016-2021 VariantValidator Contributors +# Copyright (C) 2016-2022 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as From c335eccd8a9bbb6a00035fd676de23fd36bf7ced Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Fri, 14 Jan 2022 14:01:54 +0000 Subject: [PATCH 074/218] Plug in issue https://github.com/openvar/variantValidator/issues/325 --- .../endpoints/variantvalidator_endpoints.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 086e897..a044cba 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -103,6 +103,44 @@ def get(self, gene_query): return content +@api.route("/tools/gene2transcripts_v2//") +@api.param("gene_query", "***HGNC gene symbol, HGNC ID or transcript ID***\n" + "\nCurrent supported transcript IDs" + "\n- RefSeq") +@api.param("limit_transcripts", "***Return all possible transcripts***\n" + "> False\n" + "\n***Single***\n" + "> NM_000093.4\n" + "\n***Multiple***\n" + "> NM_000093.4|NM_001278074.1|NM_000093.3") +class Gene2transcriptsV2Class(Resource): + # Add documentation about the parser + @api.expect(parser, validate=True) + def get(self, gene_query, limit_transcripts): + + if "False" in limit_transcripts or "false" in limit_transcripts or limit_transcripts is False: + limit_transcripts = None + try: + content = vval.gene2transcripts(gene_query, select_transcripts=limit_transcripts) + except ConnectionError: + message = "Cannot connect to rest.genenames.org, please try again later" + raise exceptions.RemoteConnectionError(message) + + # Collect Arguments + args = parser.parse_args() + + # Overrides the default response route so that the standard HTML URL can return any specified format + if args['content-type'] == 'application/json': + # example: http://127.0.0.1:5000.....bob?content-type=application/json + return representations.application_json(content, 200, None) + # example: http://127.0.0.1:5000.....?content-type=application/xml + elif args['content-type'] == 'application/xml': + return representations.xml(content, 200, None) + else: + # Return the api default output + return content + + @api.route("/tools/hgvs2reference/") @api.param("hgvs_description", "***hgvs_description***\n" "\nSequence variation description in the HGVS format\n" From c9cc08551ea5eaa1e79ea18a89b96a16f31984dd Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Thu, 20 Jan 2022 16:51:05 +0000 Subject: [PATCH 075/218] initial commits for issue https://github.com/openvar/variantValidator/issues/217 --- Dockerfile | 3 +++ configuration/docker.ini | 1 + docker-compose.yml | 9 ++++++++- docs/DOCKER.md | 5 +++-- rest_VariantValidator/app.py | 5 +++++ rest_VariantValidator/endpoints/vijs_endpoints.py | 10 +++++----- 6 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0ae2f0c..16d9937 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,9 @@ WORKDIR /app # Copy the current directory contents into the container's /app directory COPY . /app +# Create logging directory +RUN mkdir /usr/local/share/logs + # Update apt-get RUN apt-get update diff --git a/configuration/docker.ini b/configuration/docker.ini index d6568ed..fcf0806 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -20,6 +20,7 @@ password = uta_admin [logging] #Levels control verbosity and can be set to "CRITICAL" "ERROR" "WARNING" "INFO" or "DEBUG". +log_to = /usr/local/share/logs log = True console = WARNING file = ERROR diff --git a/docker-compose.yml b/docker-compose.yml index d174299..c008bcc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,6 +29,7 @@ services: - vdb - vvta volumes: + - logs:/usr/local/share/logs - seqdata:/usr/local/share/seqrepo - share:/usr/local/share ports: @@ -50,4 +51,10 @@ volumes: driver_opts: type: 'none' o: 'bind' - device: '${HOME}/variantvalidator_data/share/seqrepo' \ No newline at end of file + device: '${HOME}/variantvalidator_data/share/seqrepo' + logs: + driver: local + driver_opts: + type: 'none' + o: 'bind' + device: '${HOME}/variantvalidator_data/share/logs' \ No newline at end of file diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 0a31c54..b6dd2d4 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -76,8 +76,9 @@ $ docker-compose build --no-cache $ docker-compose up # When you see the following message the containers have been created. -# "vvta_1 | 2021-07-23 16:29:17.590 UTC [1] LOG: database system is ready to accept connections" -# Initial shut down prior to re-launch and working with VarinatValidator in Docker +"vvta_1 | 2021-07-23 16:29:17.590 UTC [1] LOG: database system is ready to accept connections" + +# Then perforn an initial shut down prior to re-launch and working with VarinatValidator in Docker ctrl + c ``` diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 4d8edcd..7fa8095 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -36,6 +36,11 @@ # Log with a rotating file-handler # This sets the maximum size of the log to 0.5Mb and allows two additional logs # The logs are then deleted and replaced in rotation + try: + parent = config['logging']['log_to'] + except KeyError: + pass + logHandler = handlers.RotatingFileHandler(str(parent) + '/rest_VariantValidator.log', maxBytes=500000, backupCount=2) diff --git a/rest_VariantValidator/endpoints/vijs_endpoints.py b/rest_VariantValidator/endpoints/vijs_endpoints.py index 35b5304..c11e620 100644 --- a/rest_VariantValidator/endpoints/vijs_endpoints.py +++ b/rest_VariantValidator/endpoints/vijs_endpoints.py @@ -68,7 +68,7 @@ def get(self, genome_build, variant_description): # Refresh the content refreshed_content = {} - if variant_description is "": + if variant_description == "": refreshed_content = {"error": "Unsupported variant type"} else: # Validate using the VariantValidator Python Library @@ -76,9 +76,9 @@ def get(self, genome_build, variant_description): content = validate.format_as_dict(with_meta=True) for k, v in content.items(): - if k is "metadata": + if k == "metadata": refreshed_content[k] = v - elif k is "flag": + elif k == "flag": continue else: refreshed_content[v["submitted_variant"]] = {} @@ -215,11 +215,11 @@ def get(self, genome_build, variant_description): liftover) for k, v in content.items(): - if k is "metadata": + if k == "metadata": refreshed_content[k] = v else: for k2, v2 in v.items(): - if k2 is "flag" or k2 is "errors": + if k2 == "flag" or k2 == "errors": continue # else From a343e2f543b7ce322dfe81675b959d2d81ced772 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 19 Apr 2022 18:23:52 +0100 Subject: [PATCH 076/218] In response to https://github.com/openvar/rest_variantValidator/issues/75, some requirements have been assigned a fix build in requirements.txt. --- REQUIREMENTS.txt | 11 +++++++---- docs/DOCKER.md | 2 +- rest_VariantValidator/endpoints/vijs_endpoints.py | 2 +- setup.py | 4 +++- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index 70314c5..90f244a 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -2,8 +2,6 @@ git+https://github.com/openvar/vv_hgvs@master#egg=vvhgvs git+https://github.com/openvar/variantValidator@master#egg=VariantValidator git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter biopython -flask==1.1.1 -flask-restplus gunicorn biocommons.seqrepo>=0.5.1 httplib2>=0.9.0 @@ -16,7 +14,12 @@ dicttoxml notebook jupyter_contrib_nbextensions jupyter_nbextensions_configurator -Werkzeug==0.16.1 pytest>=3.6 pytest-cov -codecov \ No newline at end of file +codecov + +# flask restplus is out of date, requiring these +flask-restplus +flask==1.1.1 +Werkzeug==0.16.1 +itsdangerous==2.0.1 diff --git a/docs/DOCKER.md b/docs/DOCKER.md index b6dd2d4..621a610 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -54,7 +54,7 @@ $ docker-compose pull $ mkdir ~/variantvalidator_data $ mkdir ~/variantvalidator_data/share ``` -i.e. a directory called share in your home directory +*i.e.* a directory called `variantvalidator_data/share` in your `home` directory - Build diff --git a/rest_VariantValidator/endpoints/vijs_endpoints.py b/rest_VariantValidator/endpoints/vijs_endpoints.py index c11e620..27ddb63 100644 --- a/rest_VariantValidator/endpoints/vijs_endpoints.py +++ b/rest_VariantValidator/endpoints/vijs_endpoints.py @@ -203,7 +203,7 @@ def get(self, genome_build, variant_description): # Refresh the content refreshed_content = {} - if variant_description is "": + if variant_description == "": refreshed_content = {"error": "Unsupported variant type"} else: # Validate using the VariantValidator Python Library diff --git a/setup.py b/setup.py index 6146864..c4b08b0 100644 --- a/setup.py +++ b/setup.py @@ -46,7 +46,6 @@ # List run-time dependencies here. These will be installed by pip when the project is installed. install_requires=[ "werkzeug==0.16.1", - "flask", "flask-restplus", "gunicorn", "httplib2>=0.9.0", @@ -61,6 +60,9 @@ "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@master#egg=VariantFormatter", "biocommons.seqrepo>=0.5.1", "biopython", + "flask==1.1.1", + "Werkzeug==0.16.1", + "itsdangerous==2.0.1", ], setup_requires=[ "setuptools_scm", From 0c73e8e448e8ad80db99823d1cf24cd88204cb23 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Fri, 29 Apr 2022 11:08:44 +0100 Subject: [PATCH 077/218] periodic update to add new 2002_02 database info --- configuration/docker.ini | 6 +++--- vdb_docker.df | 2 +- vvsr_docker.df | 6 +++--- vvta_docker.df | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/configuration/docker.ini b/configuration/docker.ini index fcf0806..755df47 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -4,17 +4,17 @@ port = 3306 database = validator user = vvadmin password = var1ant -version = vvdb_2021_4 +version = vvdb_2022_04 [seqrepo] -version = VV_SR_2021_2/master +version = VV_SR_2022_02/master location = /usr/local/share/seqrepo [postgres] host = vvta port = 5432 database = vvta -version = vvta_2021_2 +version = vvta_2022_02 user = uta_admin password = uta_admin diff --git a/vdb_docker.df b/vdb_docker.df index ab57143..b39b70b 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -10,5 +10,5 @@ ENV MYSQL_PASSWORD var1ant RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2021-07-21.sql.gz -O /docker-entrypoint-initdb.d/validator_2021-07-21.sql.gz +RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2022_04.sql.gz -O /docker-entrypoint-initdb.d/validator_2022_04.sql.gz diff --git a/vvsr_docker.df b/vvsr_docker.df index 4bef0f7..e680777 100644 --- a/vvsr_docker.df +++ b/vvsr_docker.df @@ -6,8 +6,8 @@ RUN apt-get install -y wget RUN mkdir -p /usr/local/share/seqrepo -RUN wget --output-document=/usr/local/share/seqrepo/VV_SR_2021_2.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2021_2.tar +RUN wget --output-document=/usr/local/share/seqrepo/VV_SR_2022_02.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2022_02.tar -RUN tar -xvf /usr/local/share/seqrepo/VV_SR_2021_2.tar --directory /usr/local/share/seqrepo +RUN tar -xvf /usr/local/share/seqrepo/VV_SR_2022_02.tar --directory /usr/local/share/seqrepo -RUN rm /usr/local/share/seqrepo/VV_SR_2021_2.tar \ No newline at end of file +RUN rm /usr/local/share/seqrepo/VV_SR_2022_02.tar \ No newline at end of file diff --git a/vvta_docker.df b/vvta_docker.df index 4a56269..5524fe5 100644 --- a/vvta_docker.df +++ b/vvta_docker.df @@ -8,4 +8,4 @@ ENV POSTGRES_PASSWORD=uta_admin RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget --output-document=VTA_2021_2_noseq.sql.gz https://www528.lamp.le.ac.uk/vvdata/vvta/VVTA_2021_2_noseq.psql.gz -O /docker-entrypoint-initdb.d/VVTA_2021_2_noseq.sql.gz +RUN wget --output-document=VVTA_2022_02.noseq.psql.gz https://www528.lamp.le.ac.uk/vvdata/vvta/VVTA_2022_02.noseq.psql.gz -O /docker-entrypoint-initdb.d/VVTA_2022_02.noseq.psql.gz From 28a5bf5bef51a8df7061bb0c31f606c29c050e70 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Tue, 17 May 2022 16:42:42 +0100 Subject: [PATCH 078/218] Fix up docker install for new version 2.1.0 --- REQUIREMENTS.txt | 2 ++ docs/DOCKER.md | 43 +++++++++++++++++++++++++++++-------------- setup.py | 21 --------------------- vdb_docker.df | 2 +- vvta_docker.df | 4 ++-- 5 files changed, 34 insertions(+), 38 deletions(-) diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index 90f244a..894817e 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -23,3 +23,5 @@ flask-restplus flask==1.1.1 Werkzeug==0.16.1 itsdangerous==2.0.1 +jinja2<3.1.0 + diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 621a610..2144111 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -64,22 +64,34 @@ $ docker-compose build --no-cache - Complete build - The first time you do this, it will complete the build process, for example, populating the required the databases - - When this is completed you will need to shutdown the services and re-start (see below) - The build takes a while because the vv databases are large. However, this is a significant improvement on previou - s versions. Install time is approximately 30 minutes (depending on the speed of you computer and internet connection) - - The build has completed when you see the message ***"Successfully built "*** - - example: "Successfully built fc9b83c8d21fa8bdebd52e0e87b9fde967933a043dace1a31916f8106110c8d8 -" - - Then complete the following steps + s versions. Build time is ~30 minutes (depending on the speed of you computer and internet connection) + - The build has completed when you see the message ***"naming to docker.io/library/rest_variantvalidator_restvv"*** + ```bash -# Create the containers (This only takes a coule of minutes) -$ docker-compose up +# If you have previously installed this software you will need to remove old SeqRepo databases +$ rm -r -f ~/variantvalidator_data/share/seqrepo/ + +# Create the vvta container (This takes ~10 minutes to complete) +$ docker-compose up vvta -# When you see the following message the containers have been created. -"vvta_1 | 2021-07-23 16:29:17.590 UTC [1] LOG: database system is ready to accept connections" +# When you see the following message the container has been created. +"database system is ready to accept connections" -# Then perforn an initial shut down prior to re-launch and working with VarinatValidator in Docker +# Then perforn shut down ctrl + c + +# Create the vdb container (This takes a few of minutes) and needs to be created first +$ docker-compose up vdb + +# When you see the following message the container has been created. +"database system is ready to accept connections" + +# Then perforn shut down +ctrl + c + +# Launch the full application +$ docker-compose up ``` ### Build errors you may encounter @@ -306,12 +318,15 @@ Update requires that the restvv container is deleted from your system. This is n If you are only running rest_variantValidator in docker, we recommend deleting and re-building all containers ```bash -# Delete all containers -$ docker-compose down +# Remove the specific containers +$ docker-compose rm + +# OR Delete all containers on your system +$ docker-compose rm $ docker system prune -a --volumes ``` -***Once you have deleted the containers, got to Install and Build*** +***Once you have deleted the containers, go to Install and Build*** Alternatively, you may wish to try and force the containers to re-build without deleting diff --git a/setup.py b/setup.py index c4b08b0..b523bd6 100644 --- a/setup.py +++ b/setup.py @@ -43,27 +43,6 @@ "sequencevariants", ], - # List run-time dependencies here. These will be installed by pip when the project is installed. - install_requires=[ - "werkzeug==0.16.1", - "flask-restplus", - "gunicorn", - "httplib2>=0.9.0", - "configparser>=3.5.0", - "pyliftover>=0.3", - "biotools>=0.3.0", - "mysql-connector-python", - "requests", - "dicttoxml", - "vvhgvs @ git+https://github.com/openvar/vv_hgvs.git@master#egg=vvhgvs", - "VariantValidator @ git+https://github.com/openvar/variantValidator.git@master#egg=VariantValidator", - "VariantFormatter @ git+https://github.com/openvar/variantFormatter.git@master#egg=VariantFormatter", - "biocommons.seqrepo>=0.5.1", - "biopython", - "flask==1.1.1", - "Werkzeug==0.16.1", - "itsdangerous==2.0.1", - ], setup_requires=[ "setuptools_scm", ] diff --git a/vdb_docker.df b/vdb_docker.df index b39b70b..173c1a1 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -10,5 +10,5 @@ ENV MYSQL_PASSWORD var1ant RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2022_04.sql.gz -O /docker-entrypoint-initdb.d/validator_2022_04.sql.gz +RUN wget --output-document=validator_2022_04.sql.gz https://www528.lamp.le.ac.uk/vvdata/validator/validator_2022_04.sql.gz -O /docker-entrypoint-initdb.d/validator_2022_04.sql.gz diff --git a/vvta_docker.df b/vvta_docker.df index 5524fe5..fdbab6d 100644 --- a/vvta_docker.df +++ b/vvta_docker.df @@ -1,4 +1,4 @@ -FROM postgres:12.6 +FROM postgres:11.15 ENV POSTGRES_DB=vvta @@ -8,4 +8,4 @@ ENV POSTGRES_PASSWORD=uta_admin RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget --output-document=VVTA_2022_02.noseq.psql.gz https://www528.lamp.le.ac.uk/vvdata/vvta/VVTA_2022_02.noseq.psql.gz -O /docker-entrypoint-initdb.d/VVTA_2022_02.noseq.psql.gz +RUN wget --output-document=VVTA_2022_02_noseq.sql.gz https://www528.lamp.le.ac.uk/vvdata/vvta/VVTA_2022_02_noseq.sql.gz -O /docker-entrypoint-initdb.d/VVTA_2022_02_noseq.sql.gz \ No newline at end of file From da0ac9bcb6c1a1f3372dd0c10ed0486363015030 Mon Sep 17 00:00:00 2001 From: Peter Causey-Freeman Date: Fri, 14 Oct 2022 16:48:03 +0100 Subject: [PATCH 079/218] see if this fixes https://github.com/openvar/variantValidator/issues/430 because this is really weird. For some reason the live server thinks a SeqObject is passed in the dict not string --- .../endpoints/lovd_endpoints.py | 17 ++++++++++++++++- wsgi.py | 5 +++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index 9e2c403..cfc4f83 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -1,4 +1,6 @@ # Import modules +import json +import ast from flask_restplus import Namespace, Resource from . import request_parser from . import representations @@ -9,6 +11,14 @@ import VariantValidator vval = VariantValidator.Validator() + +def ordereddict_to_dict(value): + for k, v in value.items(): + if isinstance(v, dict): + value[k] = ordereddict_to_dict(v) + return dict(value) + + """ Create a parser object locally """ @@ -69,6 +79,11 @@ def get(self, genome_build, variant_description, transcript_model, select_transc content = VariantFormatter.simpleVariantFormatter.format(variant_description, genome_build, transcript_model, select_transcripts, checkonly, liftover) + to_dict = ordereddict_to_dict(content) + content = str(to_dict) + content = content.replace("'", '"') + content = ast.literal_eval(content) + # Collect Arguments args = parser.parse_args() @@ -78,7 +93,7 @@ def get(self, genome_build, variant_description, transcript_model, select_transc return representations.application_json(content, 200, None) # example: http://127.0.0.1:5000.....?content-type=application/xml elif args['content-type'] == 'application/xml': - return representations.xml(content, 200, None) + return representations.xml(str(content), 200, None) else: # Return the api default output return content diff --git a/wsgi.py b/wsgi.py index dac45b1..359a1f8 100644 --- a/wsgi.py +++ b/wsgi.py @@ -1,4 +1,5 @@ -"""mod_wsgi gateway wsgi file +""" +mod_wsgi gateway wsgi file """ from rest_VariantValidator.app import application as application @@ -13,4 +14,4 @@ else: application.debug = False application.config['PROPAGATE_EXCEPTIONS'] = False - application.run(host="127.0.0.1", port=5000) + application.run(host="127.0.0.1", port=8080) From b2e26a59ebdbc61b1fb2e4b29b5d56a2b423ac0b Mon Sep 17 00:00:00 2001 From: e05374pc Date: Fri, 25 Nov 2022 11:09:03 +0000 Subject: [PATCH 080/218] Fix setup.py --- setup.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b523bd6..b4299f3 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,11 @@ use_scm_version=True, zip_safe=True, author="VariantValidator Contributors", - author_email = 'admin@variantvalidator.org', + author_email='admin@variantvalidator.org', + packages=['rest_VariantValidator', + 'rest_VariantValidator.endpoints', + 'rest_VariantValidator.utils', + ], description='Rest API for VariantValidator', long_description=open('README.md').read(), url='https://github.com/openvar/variantFormatter', From 479b98b89decc0c3becff33909d9bf89d34b31c2 Mon Sep 17 00:00:00 2001 From: e05374pc Date: Fri, 25 Nov 2022 12:14:18 +0000 Subject: [PATCH 081/218] Update to flask_restx and update REQUIREMENTS.txt --- REQUIREMENTS.txt | 18 ++++++------------ rest_VariantValidator/endpoints/__init__.py | 2 +- rest_VariantValidator/endpoints/hello.py | 2 +- .../endpoints/lovd_endpoints.py | 2 +- .../endpoints/request_parser.py | 2 +- .../endpoints/variantformatter_endpoints.py | 2 +- .../endpoints/variantvalidator_endpoints.py | 2 +- .../endpoints/vijs_endpoints.py | 2 +- 8 files changed, 13 insertions(+), 19 deletions(-) diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index 894817e..1279758 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -8,20 +8,14 @@ httplib2>=0.9.0 configparser>=3.5.0 pyliftover>=0.3 biotools>=0.3.0 -mysql-connector-python requests dicttoxml -notebook -jupyter_contrib_nbextensions -jupyter_nbextensions_configurator pytest>=3.6 pytest-cov codecov - -# flask restplus is out of date, requiring these -flask-restplus -flask==1.1.1 -Werkzeug==0.16.1 -itsdangerous==2.0.1 -jinja2<3.1.0 - +flask-restx +flask-socketio +flask == 1.1.4 +jinja2==2.11.3 +Werkzeug==1.0.1 +markupsafe==2.0.1 diff --git a/rest_VariantValidator/endpoints/__init__.py b/rest_VariantValidator/endpoints/__init__.py index 16b5c35..b9ecc9b 100644 --- a/rest_VariantValidator/endpoints/__init__.py +++ b/rest_VariantValidator/endpoints/__init__.py @@ -1,7 +1,7 @@ import rest_VariantValidator import VariantValidator import VariantFormatter -from flask_restplus import Api +from flask_restx import Api from flask import url_for from .variantvalidator_endpoints import api as ns_vv from .variantformatter_endpoints import api as ns_vf diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py index 12a8a1c..ca4283b 100644 --- a/rest_VariantValidator/endpoints/hello.py +++ b/rest_VariantValidator/endpoints/hello.py @@ -1,4 +1,4 @@ -from flask_restplus import Namespace, Resource +from flask_restx import Namespace, Resource from . import request_parser from . import representations diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index 9e2c403..72942f0 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -1,5 +1,5 @@ # Import modules -from flask_restplus import Namespace, Resource +from flask_restx import Namespace, Resource from . import request_parser from . import representations diff --git a/rest_VariantValidator/endpoints/request_parser.py b/rest_VariantValidator/endpoints/request_parser.py index 052dd3a..b14bb0c 100644 --- a/rest_VariantValidator/endpoints/request_parser.py +++ b/rest_VariantValidator/endpoints/request_parser.py @@ -1,4 +1,4 @@ -from flask_restplus import reqparse +from flask_restx import reqparse # Create a RequestParser object to identify specific content-type requests in HTTP URLs diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index 50a2589..a9c91a5 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -1,5 +1,5 @@ # Import modules -from flask_restplus import Namespace, Resource +from flask_restx import Namespace, Resource from . import request_parser from . import representations diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index a044cba..f2ac1ce 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -1,5 +1,5 @@ # Import modules -from flask_restplus import Namespace, Resource +from flask_restx import Namespace, Resource from . import request_parser from . import representations from . import exceptions diff --git a/rest_VariantValidator/endpoints/vijs_endpoints.py b/rest_VariantValidator/endpoints/vijs_endpoints.py index 27ddb63..124976e 100644 --- a/rest_VariantValidator/endpoints/vijs_endpoints.py +++ b/rest_VariantValidator/endpoints/vijs_endpoints.py @@ -1,5 +1,5 @@ # Import modules -from flask_restplus import Namespace, Resource +from flask_restx import Namespace, Resource from . import request_parser from . import representations From bea1472ad68e776c7f017fd0d607ecd1debb13c6 Mon Sep 17 00:00:00 2001 From: e05374pc Date: Fri, 25 Nov 2022 13:40:46 +0000 Subject: [PATCH 082/218] Update db installations and docker.md for new version. Also switch to flask-restx and Python 10 --- Dockerfile | 3 +-- configuration/docker.ini | 14 +++++++------- docker-compose.yml | 8 ++++---- docs/DOCKER.md | 4 +++- vdb_docker.df | 9 ++++++--- vvsr_docker.df | 6 +++--- vvta_docker.df | 5 +++-- 7 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Dockerfile b/Dockerfile index 16d9937..2e9ded7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # Use an official Python runtime as a parent image -FROM python:3.8 +FROM python:3.10 # Set the working directory to /app WORKDIR /app @@ -30,5 +30,4 @@ RUN pip install -e . COPY configuration/docker.ini /root/.variantvalidator # Start the application with gunicorn -# CMD gunicorn -b 0.0.0.0:8000 app --workers=3 --threads=5 --worker-class=gthread --chdir ./rest_VariantValidator/ CMD gunicorn -b 0.0.0.0:8000 app --threads=5 --chdir ./rest_VariantValidator/ \ No newline at end of file diff --git a/configuration/docker.ini b/configuration/docker.ini index 755df47..22ed6ec 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -4,30 +4,30 @@ port = 3306 database = validator user = vvadmin password = var1ant -version = vvdb_2022_04 +version = vvdb_2022_11 [seqrepo] -version = VV_SR_2022_02/master +version = VV_SR_2022_11/master location = /usr/local/share/seqrepo +require_threading = True [postgres] host = vvta port = 5432 database = vvta -version = vvta_2022_02 +version = vvta_2022_11 user = uta_admin password = uta_admin [logging] -#Levels control verbosity and can be set to "CRITICAL" "ERROR" "WARNING" "INFO" or "DEBUG". -log_to = /usr/local/share/logs +# Levels control verbosity and can be set to "CRITICAL" "ERROR" "WARNING" "INFO" or "DEBUG". log = True console = WARNING file = ERROR [Entrez] -email = None -api_key = None +email = OPTIONAL +api_key = OPTIONAL # diff --git a/docker-compose.yml b/docker-compose.yml index c008bcc..6a8831b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,17 +6,17 @@ services: context: . dockerfile: vdb_docker.df ports: - - "33060:3306" + - "33061:3306" expose: - - "33060" + - "33061" vvta: build: context: . dockerfile: vvta_docker.df ports: - - "54320:5432" + - "54321:5432" expose: - - "54320" + - "54321" seqrepo: build: context: . diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 2144111..e73e939 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -53,6 +53,7 @@ $ docker-compose pull ```bash $ mkdir ~/variantvalidator_data $ mkdir ~/variantvalidator_data/share +$ mkdir ~/variantvalidator_data/share/logs ``` *i.e.* a directory called `variantvalidator_data/share` in your `home` directory @@ -66,7 +67,7 @@ $ docker-compose build --no-cache - The first time you do this, it will complete the build process, for example, populating the required the databases - The build takes a while because the vv databases are large. However, this is a significant improvement on previou s versions. Build time is ~30 minutes (depending on the speed of you computer and internet connection) - - The build has completed when you see the message ***"naming to docker.io/library/rest_variantvalidator_restvv"*** + - The build has completed when you see the message ***"Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them"*** ```bash # If you have previously installed this software you will need to remove old SeqRepo databases @@ -216,6 +217,7 @@ $ exit - Note: Under the terms and conditions of our [license](https://github.com/openvar/rest_variantValidator/blob/master/LICENSE.txt) changes to the code and improvements must be made available to the community so that we can integrate them for the good of all our users - See instructions on VariantValidator development in Docker +3. For other commands that you can run using bash, see the [VariantValidator DOCKER.md](https://github.com/openvar/variantValidator/blob/master/docs/DOCKER.md) ## Developing VariantValidator in Docker The container has been configured with git installed. This means that you can clone Repos directly into the container diff --git a/vdb_docker.df b/vdb_docker.df index 173c1a1..29a1bc3 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -1,4 +1,8 @@ -FROM mysql:latest +# For Arm chips e.g. Apple M1 +FROM biarms/mysql:5.7 + +# For Intel chips +# FROM mysql:5.7 ENV MYSQL_RANDOM_ROOT_PASSWORD yes @@ -10,5 +14,4 @@ ENV MYSQL_PASSWORD var1ant RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget --output-document=validator_2022_04.sql.gz https://www528.lamp.le.ac.uk/vvdata/validator/validator_2022_04.sql.gz -O /docker-entrypoint-initdb.d/validator_2022_04.sql.gz - +RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2022_11.sql.gz -O /docker-entrypoint-initdb.d/validator_2022_11.sql.gz diff --git a/vvsr_docker.df b/vvsr_docker.df index e680777..4546e4c 100644 --- a/vvsr_docker.df +++ b/vvsr_docker.df @@ -6,8 +6,8 @@ RUN apt-get install -y wget RUN mkdir -p /usr/local/share/seqrepo -RUN wget --output-document=/usr/local/share/seqrepo/VV_SR_2022_02.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2022_02.tar +RUN wget --output-document=/usr/local/share/seqrepo/VV_SR_2022_11.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2022_11.tar -RUN tar -xvf /usr/local/share/seqrepo/VV_SR_2022_02.tar --directory /usr/local/share/seqrepo +RUN tar -xvf /usr/local/share/seqrepo/VV_SR_2022_11.tar --directory /usr/local/share/seqrepo -RUN rm /usr/local/share/seqrepo/VV_SR_2022_02.tar \ No newline at end of file +RUN rm /usr/local/share/seqrepo/VV_SR_2022_11.tar \ No newline at end of file diff --git a/vvta_docker.df b/vvta_docker.df index fdbab6d..40ce652 100644 --- a/vvta_docker.df +++ b/vvta_docker.df @@ -1,4 +1,4 @@ -FROM postgres:11.15 +FROM postgres:12.6 ENV POSTGRES_DB=vvta @@ -8,4 +8,5 @@ ENV POSTGRES_PASSWORD=uta_admin RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget --output-document=VVTA_2022_02_noseq.sql.gz https://www528.lamp.le.ac.uk/vvdata/vvta/VVTA_2022_02_noseq.sql.gz -O /docker-entrypoint-initdb.d/VVTA_2022_02_noseq.sql.gz \ No newline at end of file +RUN wget --output-document=vvta_2022_11_noseq.sql.gz https://www528.lamp.le.ac.uk/vvdata/vvta/vvta_2022_11_no_seq.sql.gz -O /docker-entrypoint-initdb.d/vvta_2022_11_noseq.sql.gz + From 0532f5c8d945cfee80f8179ac2f24408119974fd Mon Sep 17 00:00:00 2001 From: e05374pc Date: Fri, 25 Nov 2022 13:41:22 +0000 Subject: [PATCH 083/218] Switch default MySQL chipset --- vdb_docker.df | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vdb_docker.df b/vdb_docker.df index 29a1bc3..20c1756 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -1,8 +1,8 @@ # For Arm chips e.g. Apple M1 -FROM biarms/mysql:5.7 +# FROM biarms/mysql:5.7 # For Intel chips -# FROM mysql:5.7 +FROM mysql:5.7 ENV MYSQL_RANDOM_ROOT_PASSWORD yes From b853e9ad017b253cfd05ea38dcf9a84c7d0a6bd4 Mon Sep 17 00:00:00 2001 From: e05374pc Date: Fri, 25 Nov 2022 13:46:47 +0000 Subject: [PATCH 084/218] Update docker.md with arm/intel instructions --- docs/DOCKER.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index e73e939..7c1625d 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -37,6 +37,17 @@ do not update your container for more than 12 months; else leave as `None`. See Note: Reconfiguration can be achieved by accessing the docker container through bash. See below for entry and the VariantValidator [manual](https://github.com/openvar/variantValidator/blob/master/docs/MANUAL.md) for details +- Edit the `vdb_docker.df` file + +You need to select your chip set e.g. Arm or Intel and remove the relevant hash. Default is intel + +``` +# For Arm chips e.g. Apple M1 +# FROM biarms/mysql:5.7 + +# For Intel chips +FROM mysql:5.7 +``` ## Build the container From cbeeb9022ea326095934092c1d420be69f1f415a Mon Sep 17 00:00:00 2001 From: e05374pc Date: Wed, 4 Jan 2023 16:32:10 +0000 Subject: [PATCH 085/218] update copyright statement for 2023 --- bin/batch_validator.py | 2 +- bin/update_vdb.py | 2 +- bin/variant_validator.py | 2 +- bin/vv_configure.py | 2 +- configuration/docker.ini | 2 +- rest_VariantValidator/__init__.py | 2 +- rest_VariantValidator/app.py | 2 +- rest_VariantValidator/endpoints/__init__.py | 2 +- rest_VariantValidator/endpoints/exceptions.py | 2 +- rest_VariantValidator/endpoints/hello.py | 2 +- rest_VariantValidator/endpoints/lovd_endpoints.py | 2 +- rest_VariantValidator/endpoints/representations.py | 2 +- rest_VariantValidator/endpoints/request_parser.py | 2 +- rest_VariantValidator/endpoints/variantformatter_endpoints.py | 2 +- rest_VariantValidator/endpoints/vijs_endpoints.py | 2 +- setup.py | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/bin/batch_validator.py b/bin/batch_validator.py index 761e26d..26bf25f 100644 --- a/bin/batch_validator.py +++ b/bin/batch_validator.py @@ -84,7 +84,7 @@ outfile.close() # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2023 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/bin/update_vdb.py b/bin/update_vdb.py index 88c60ff..6abd1c2 100644 --- a/bin/update_vdb.py +++ b/bin/update_vdb.py @@ -16,7 +16,7 @@ update_vv_db.update() # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2023 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/bin/variant_validator.py b/bin/variant_validator.py index 70589f7..cb831f9 100644 --- a/bin/variant_validator.py +++ b/bin/variant_validator.py @@ -54,7 +54,7 @@ def output_results(valoutput, outformat, with_meta): args.output.write(output_results(output, args.output_format, args.meta) + '\n') # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2023 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/bin/vv_configure.py b/bin/vv_configure.py index 7e62eb5..941d59c 100644 --- a/bin/vv_configure.py +++ b/bin/vv_configure.py @@ -59,7 +59,7 @@ def read_settings(): config.write(fh) # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2023 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/configuration/docker.ini b/configuration/docker.ini index 22ed6ec..cf1e8d9 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -31,7 +31,7 @@ api_key = OPTIONAL # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2023 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/__init__.py b/rest_VariantValidator/__init__.py index d0d4c13..cbf88ab 100644 --- a/rest_VariantValidator/__init__.py +++ b/rest_VariantValidator/__init__.py @@ -13,7 +13,7 @@ __version__ = None # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2023 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 7fa8095..053212a 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -169,7 +169,7 @@ def default_error_handler(e): # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2023 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/__init__.py b/rest_VariantValidator/endpoints/__init__.py index b9ecc9b..a8b7cd5 100644 --- a/rest_VariantValidator/endpoints/__init__.py +++ b/rest_VariantValidator/endpoints/__init__.py @@ -56,7 +56,7 @@ def specs_url(self): # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2023 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/exceptions.py b/rest_VariantValidator/endpoints/exceptions.py index f71b073..07c5179 100644 --- a/rest_VariantValidator/endpoints/exceptions.py +++ b/rest_VariantValidator/endpoints/exceptions.py @@ -2,7 +2,7 @@ class RemoteConnectionError(Exception): code = 504 # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2023 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py index ca4283b..4153cfa 100644 --- a/rest_VariantValidator/endpoints/hello.py +++ b/rest_VariantValidator/endpoints/hello.py @@ -60,7 +60,7 @@ def get(self): # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2023 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index 3fcbce8..2ae2f3d 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -100,7 +100,7 @@ def get(self, genome_build, variant_description, transcript_model, select_transc # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2023 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/representations.py b/rest_VariantValidator/endpoints/representations.py index a580e45..578e03f 100644 --- a/rest_VariantValidator/endpoints/representations.py +++ b/rest_VariantValidator/endpoints/representations.py @@ -26,7 +26,7 @@ def application_json(data, code, headers): return resp # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2023 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/request_parser.py b/rest_VariantValidator/endpoints/request_parser.py index b14bb0c..feaf728 100644 --- a/rest_VariantValidator/endpoints/request_parser.py +++ b/rest_VariantValidator/endpoints/request_parser.py @@ -11,7 +11,7 @@ # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2023 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index a9c91a5..ccd62f3 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -72,7 +72,7 @@ def get(self, genome_build, variant_description, transcript_model, select_transc # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2023 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/vijs_endpoints.py b/rest_VariantValidator/endpoints/vijs_endpoints.py index 124976e..7d09626 100644 --- a/rest_VariantValidator/endpoints/vijs_endpoints.py +++ b/rest_VariantValidator/endpoints/vijs_endpoints.py @@ -263,7 +263,7 @@ def get(self, genome_build, variant_description): # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2023 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/setup.py b/setup.py index b4299f3..a2f2291 100644 --- a/setup.py +++ b/setup.py @@ -53,7 +53,7 @@ ) # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2023 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as From 205c0ba301539c59db877de617213cbc0396c1f6 Mon Sep 17 00:00:00 2001 From: e05374pc Date: Tue, 18 Apr 2023 22:42:39 +0100 Subject: [PATCH 086/218] Update to use select_transcripts = raw field --- rest_VariantValidator/endpoints/lovd_endpoints.py | 9 +++++++-- .../endpoints/variantformatter_endpoints.py | 9 +++++++-- .../endpoints/variantvalidator_endpoints.py | 4 +++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index 2ae2f3d..dede1fd 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -38,9 +38,14 @@ def ordereddict_to_dict(value): "> - *Recommended maximum is 10 variants per submission*") @api.param("transcript_model", "***Accepted:***\n" "> - refseq (return data for RefSeq transcript models)\n" - "> - all (currently refseq only)") + "> - ensembl (return data for ensembl transcript models)\n" + "> - all") @api.param("select_transcripts", "***Return all possible transcripts***\n" - "> None or all\n" + "> None or all (all transcripts at the latest versions)\n" + "> raw (all transcripts all version)\n" + "> select (select transcripts)\n" + "> mane (MANE select transcripts)\n" + "> mane_select (MANE select and MANE Plus Clinical transcripts)\n" "\n***Single***\n" "> NM_000093.4\n" "\n***Multiple***\n" diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index ccd62f3..ff3b02e 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -26,9 +26,14 @@ "> - *Recommended maximum is 10 variants per submission*") @api.param("transcript_model", "***Accepted:***\n" "> - refseq (return data for RefSeq transcript models)\n" - "> - all (currently refseq only)") + "> - ensembl (return data for ensembl transcript models)\n" + "> - all") @api.param("select_transcripts", "***Return all possible transcripts***\n" - "> None or all\n" + "> None or all (all transcripts at the latest versions)\n" + "> raw (all transcripts all version)\n" + "> select (select transcripts)\n" + "> mane (MANE select transcripts)\n" + "> mane_select (MANE select and MANE Plus Clinical transcripts)\n" "\n***Single***\n" "> NM_000093.4\n" "\n***Multiple***\n" diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index f2ac1ce..48b8aa9 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -18,10 +18,12 @@ @api.route("/variantvalidator///") @api.param("select_transcripts", "***Return all possible transcripts***\n" - "> all\n" + "> all (at latest version for each transcript)\n" + "> raw (all versions of each transcript)\n" "\n***Return only 'select' transcripts***\n" "> select\n" "> mane_select\n" + "> mane (MANE and MANE Plus Clinical)\n" "> refseq_select\n" "\n***Single***\n" "> NM_000093.4\n" From 61dbe72ea2314ea9363863a69898e5e787f9c53f Mon Sep 17 00:00:00 2001 From: e05374pc Date: Wed, 19 Apr 2023 09:08:18 +0100 Subject: [PATCH 087/218] Update vdb docker to FROM mysql:5.7-debian --- vdb_docker.df | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdb_docker.df b/vdb_docker.df index 20c1756..bbb08cd 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -2,7 +2,7 @@ # FROM biarms/mysql:5.7 # For Intel chips -FROM mysql:5.7 +FROM mysql:5.7-debian ENV MYSQL_RANDOM_ROOT_PASSWORD yes From fc34d463ae8549b1e0b96e27d0a7a7e535d338ba Mon Sep 17 00:00:00 2001 From: e05374pc Date: Wed, 19 Apr 2023 12:03:02 +0100 Subject: [PATCH 088/218] Update docker install instructions --- docs/DOCKER.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 7c1625d..d78e273 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -64,6 +64,7 @@ $ docker-compose pull ```bash $ mkdir ~/variantvalidator_data $ mkdir ~/variantvalidator_data/share +$ mkdir ~/variantvalidator_data/share/seqrepo/ $ mkdir ~/variantvalidator_data/share/logs ``` *i.e.* a directory called `variantvalidator_data/share` in your `home` directory @@ -87,13 +88,13 @@ $ rm -r -f ~/variantvalidator_data/share/seqrepo/ # Create the vvta container (This takes ~10 minutes to complete) $ docker-compose up vvta -# When you see the following message the container has been created. +# When you see the following message the container has been created (Can take ~30 min or so). "database system is ready to accept connections" # Then perforn shut down ctrl + c -# Create the vdb container (This takes a few of minutes) and needs to be created first +# Create the vdb container (This takes a few of minutes) $ docker-compose up vdb # When you see the following message the container has been created. From 74886038855f221fb1201bc4dca30305d6fcc4e5 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 28 Apr 2023 15:39:32 +0100 Subject: [PATCH 089/218] Add in code changes to make the genes2transcripts v2 work --- docker-compose.yml | 2 +- .../endpoints/variantvalidator_endpoints.py | 19 +++++++++++++------ vdb_docker.df | 4 ++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 6a8831b..ae6231d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -57,4 +57,4 @@ volumes: driver_opts: type: 'none' o: 'bind' - device: '${HOME}/variantvalidator_data/share/logs' \ No newline at end of file + device: '${HOME}/variantvalidator_data/share/logs' diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 48b8aa9..c8d5ba8 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -105,25 +105,32 @@ def get(self, gene_query): return content -@api.route("/tools/gene2transcripts_v2//") +@api.route("/tools/gene2transcripts_v2///") @api.param("gene_query", "***HGNC gene symbol, HGNC ID or transcript ID***\n" "\nCurrent supported transcript IDs" - "\n- RefSeq") + "\n- RefSeq or Ensembl") @api.param("limit_transcripts", "***Return all possible transcripts***\n" "> False\n" "\n***Single***\n" - "> NM_000093.4\n" + "> NM_000088.4\n" "\n***Multiple***\n" - "> NM_000093.4|NM_001278074.1|NM_000093.3") + "> NM_000088.4|NM_000088.3\n" + "\n***Limit to select transcripts***\n" + "> mane_select = MANE Select transcript only\n" + "> mane = Mane Select and MANE Plus Clinical\n" + "> select = All transcripts that have been classified as canonical") +@api.param("transcript_set", "***RefSeq or Ensembl***\n" + "\nall = all transcripts, refseq = RefSeq only, ensembl = Ensembl only") class Gene2transcriptsV2Class(Resource): # Add documentation about the parser @api.expect(parser, validate=True) - def get(self, gene_query, limit_transcripts): + def get(self, gene_query, limit_transcripts, transcript_set): if "False" in limit_transcripts or "false" in limit_transcripts or limit_transcripts is False: limit_transcripts = None try: - content = vval.gene2transcripts(gene_query, select_transcripts=limit_transcripts) + content = vval.gene2transcripts(gene_query, select_transcripts=limit_transcripts, + transcript_set=transcript_set) except ConnectionError: message = "Cannot connect to rest.genenames.org, please try again later" raise exceptions.RemoteConnectionError(message) diff --git a/vdb_docker.df b/vdb_docker.df index bbb08cd..b0acadf 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -1,8 +1,8 @@ # For Arm chips e.g. Apple M1 -# FROM biarms/mysql:5.7 +FROM biarms/mysql:5.7 # For Intel chips -FROM mysql:5.7-debian +# FROM mysql:5.7-debian ENV MYSQL_RANDOM_ROOT_PASSWORD yes From 5cf6a95b4196dbceaa5f8f635dfeebd958cad300 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 3 May 2023 18:52:44 +0100 Subject: [PATCH 090/218] Updated start up method and now should build on both ARM and other --- Dockerfile | 12 ++++++++---- docker-compose.yml | 4 ++++ docs/DOCKER.md | 39 +++++++++++++++++++++------------------ vdb_docker.df | 17 +++++++---------- vvsr_docker.df | 2 +- vvta_docker.df | 6 ++++-- 6 files changed, 45 insertions(+), 35 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2e9ded7..d31b645 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,3 @@ - -# Use an official Python runtime as a parent image FROM python:3.10 # Set the working directory to /app @@ -29,5 +27,11 @@ RUN pip install -e . # Copy the config file into the container home diorectory COPY configuration/docker.ini /root/.variantvalidator -# Start the application with gunicorn -CMD gunicorn -b 0.0.0.0:8000 app --threads=5 --chdir ./rest_VariantValidator/ \ No newline at end of file +# Expose the port +EXPOSE 8000 + +# Define the entrypoint as an empty command +ENTRYPOINT [] + +# Start the container with CMD +CMD ["gunicorn", "-b", "0.0.0.0:8000", "app", "--threads=5", "--chdir", "./rest_VariantValidator/"] diff --git a/docker-compose.yml b/docker-compose.yml index ae6231d..0ceefbc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,9 +25,13 @@ services: - seqdata:/usr/local/share/seqrepo restvv: build: . + entrypoint: /bin/bash + command: ["-c", "sleep infinity"] + restart: always depends_on: - vdb - vvta + - seqrepo volumes: - logs:/usr/local/share/logs - seqdata:/usr/local/share/seqrepo diff --git a/docs/DOCKER.md b/docs/DOCKER.md index d78e273..ea098a0 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -41,14 +41,6 @@ VariantValidator [manual](https://github.com/openvar/variantValidator/blob/maste You need to select your chip set e.g. Arm or Intel and remove the relevant hash. Default is intel -``` -# For Arm chips e.g. Apple M1 -# FROM biarms/mysql:5.7 - -# For Intel chips -FROM mysql:5.7 -``` - ## Build the container *Note: some of these steps take >>1hr to complete depending on the speed of your internet connection, particularly @@ -74,13 +66,14 @@ $ mkdir ~/variantvalidator_data/share/logs ```bash $ docker-compose build --no-cache ``` - + - Complete build - The first time you do this, it will complete the build process, for example, populating the required the databases - The build takes a while because the vv databases are large. However, this is a significant improvement on previou s versions. Build time is ~30 minutes (depending on the speed of you computer and internet connection) - The build has completed when you see the message ***"Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them"*** +- Test each container and completes builds if necessary ```bash # If you have previously installed this software you will need to remove old SeqRepo databases $ rm -r -f ~/variantvalidator_data/share/seqrepo/ @@ -98,11 +91,17 @@ ctrl + c $ docker-compose up vdb # When you see the following message the container has been created. -"database system is ready to accept connections" +" /usr/sbin/mysqld: ready for connections" # Then perforn shut down ctrl + c +# Create the SeqRepo comtainer +docker-compose up seqrepo + +# This will auto exit once complete and you will see +"exited with code 0" + # Launch the full application $ docker-compose up ``` @@ -181,10 +180,14 @@ $ docker-compose down $ docker-compose up --force-recreate ``` -## Accessing and using rest_variantValidator -Start the container +## Start the container ```bash -$ docker-compose up +$ docker-compose up -d +``` + +## Start the server +```bash +$ docker exec -it rest_variantvalidator-restvv-1 gunicorn -b 0.0.0.0:8000 app --threads=5 --chdir ./rest_VariantValidator/ ``` In a web browser navigate to @@ -207,10 +210,10 @@ It is possible to access both the UTA and Validator databases outside of docker ## Accessing VariantValidator directly through bash and reconfiguring a container post build The container hosts a full install of VariantValidator. -To start this version you use the command +To start this version you start the container in detached mode and access it using ```bash -$ docker-compose run restvv bash +$ docker-compose exec restvv bash ``` When you are finished exit the container @@ -236,10 +239,10 @@ The container has been configured with git installed. This means that you can cl To develop VariantValidator in the container -Start the container +Start the container in detached mode ```bash -$ docker-compose run restvv bash +$ docker-compose exec restvv bash ``` ON YOUR COMPUTER change into the share directory @@ -316,7 +319,7 @@ $ git checkout name_of_branch Navigating to the Repo is identical ```bash -$ docker-compose run restvv bash +$ docker-compose exec restvv bash $ cd /usr/local/share/DevelopmentRepos/rest_variantValidator ``` diff --git a/vdb_docker.df b/vdb_docker.df index b0acadf..efe6c2d 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -1,17 +1,14 @@ -# For Arm chips e.g. Apple M1 -FROM biarms/mysql:5.7 - -# For Intel chips -# FROM mysql:5.7-debian +# Should run on all processors +FROM ubuntu/mysql:8.0-20.04_beta ENV MYSQL_RANDOM_ROOT_PASSWORD yes - ENV MYSQL_DATABASE validator - ENV MYSQL_USER vvadmin - ENV MYSQL_PASSWORD var1ant -RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* +RUN apt-get update && apt-get install -y \ + wget + +RUN rm -rf /var/lib/apt/lists/* -RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2022_11.sql.gz -O /docker-entrypoint-initdb.d/validator_2022_11.sql.gz +RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2022_11.sql.gz -O /docker-entrypoint-initdb.d/validator_2022_11.sql.gz \ No newline at end of file diff --git a/vvsr_docker.df b/vvsr_docker.df index 4546e4c..8e4bf9d 100644 --- a/vvsr_docker.df +++ b/vvsr_docker.df @@ -1,4 +1,4 @@ -FROM ubuntu:latest +FROM ubuntu:22.04 RUN apt-get update diff --git a/vvta_docker.df b/vvta_docker.df index 40ce652..0157e42 100644 --- a/vvta_docker.df +++ b/vvta_docker.df @@ -1,4 +1,4 @@ -FROM postgres:12.6 +FROM postgres:13.10 ENV POSTGRES_DB=vvta @@ -8,5 +8,7 @@ ENV POSTGRES_PASSWORD=uta_admin RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* -RUN wget --output-document=vvta_2022_11_noseq.sql.gz https://www528.lamp.le.ac.uk/vvdata/vvta/vvta_2022_11_no_seq.sql.gz -O /docker-entrypoint-initdb.d/vvta_2022_11_noseq.sql.gz +# Prepare to migrate to postgres 14 +# RUN wget --output-document=https://www528.lamp.le.ac.uk/vvdata/vvta/vvta_2022_11_no_seq.sql.gz | gzip -dq | sed 's/anyarray/anycompatiblearray/g' | gzip > docker-entrypoint-initdb.d/vvta_2022_11_noseq.sql.gz +RUN wget --output-document=vvta_2022_11_noseq.sql.gz https://www528.lamp.le.ac.uk/vvdata/vvta/vvta_2022_11_no_seq.sql.gz -O /docker-entrypoint-initdb.d/vvta_2022_11_noseq.sql.gz From b34560781cab59308bf41bab20d69b3a22558ffc Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 3 May 2023 19:01:40 +0100 Subject: [PATCH 091/218] a couple of instruction changes to docker --- docs/DOCKER.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index ea098a0..60b927e 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -102,8 +102,13 @@ docker-compose up seqrepo # This will auto exit once complete and you will see "exited with code 0" -# Launch the full application -$ docker-compose up +# Start the container +$ docker-compose up -d + +# Start the server +$ docker exec -it rest_variantvalidator-restvv-1 gunicorn -b 0.0.0.0:8000 app --threads=5 --chdir ./rest_VariantValidator/ + +# Now go to Running the app (below) if you have no build errors ``` ### Build errors you may encounter @@ -180,13 +185,13 @@ $ docker-compose down $ docker-compose up --force-recreate ``` -## Start the container +## Running the app + ```bash +# Start the container $ docker-compose up -d -``` -## Start the server -```bash +# Start the server $ docker exec -it rest_variantvalidator-restvv-1 gunicorn -b 0.0.0.0:8000 app --threads=5 --chdir ./rest_VariantValidator/ ``` From 78f914b89533df143f41940464c30aeb9651bbf7 Mon Sep 17 00:00:00 2001 From: leicray Date: Wed, 10 May 2023 14:30:25 +0100 Subject: [PATCH 092/218] Update DOCKER.md --- docs/DOCKER.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index d78e273..9cca939 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -7,7 +7,7 @@ See their [documentation](https://docs.docker.com/compose/install/) for informat ## Clone the rest_VariantValidator Repository -Create a directory collate your cloned repositories. Move into the directory then, clone the repository. +Create a directory to collate your cloned repositories. Move into the directory then, clone the repository. ```bash $ git clone https://github.com/openvar/rest_variantValidator @@ -39,10 +39,10 @@ VariantValidator [manual](https://github.com/openvar/variantValidator/blob/maste - Edit the `vdb_docker.df` file -You need to select your chip set e.g. Arm or Intel and remove the relevant hash. Default is intel +You need to select your chip set e.g. ARM or Intel and remove the relevant hash. Default is Intel ``` -# For Arm chips e.g. Apple M1 +# For ARM chips e.g. Apple M1 or Raspberry Pi # FROM biarms/mysql:5.7 # For Intel chips @@ -352,4 +352,4 @@ $ docker-compose up --force-recreate ***If you choose this option, make sure you see the container restvv being re-created and all Python packages being reinstalled in the printed logs, otherwise the container may not actually be rebuilt and the contained modules may not - update*** \ No newline at end of file + update*** From 0db6f7acb8e59640aba87b085269698a57f97b52 Mon Sep 17 00:00:00 2001 From: leicray Date: Fri, 12 May 2023 11:12:50 +0100 Subject: [PATCH 093/218] Update DOCKER.md --- docs/DOCKER.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 9cca939..d455983 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -7,7 +7,7 @@ See their [documentation](https://docs.docker.com/compose/install/) for informat ## Clone the rest_VariantValidator Repository -Create a directory to collate your cloned repositories. Move into the directory then, clone the repository. +Create a directory to collate your cloned repositories. Move into the directory, then clone the repository. ```bash $ git clone https://github.com/openvar/rest_variantValidator @@ -16,7 +16,7 @@ $ git clone https://github.com/openvar/rest_variantValidator Once the repository has been cloned, cd into the rest_variantValidator directory that the clone creates. ```bash -$ cd rest_variantValidator/ +$ cd rest_variantValidator ``` If you have cloned the repository previously, update it prior to installing/re-installing using Docker From c2f43ddda12e77355113e12553025c4a8a34702d Mon Sep 17 00:00:00 2001 From: leicray Date: Fri, 12 May 2023 14:19:47 +0100 Subject: [PATCH 094/218] Update DOCKER.md --- docs/DOCKER.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 60b927e..4e5f73f 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -87,7 +87,7 @@ $ docker-compose up vvta # Then perforn shut down ctrl + c -# Create the vdb container (This takes a few of minutes) +# Create the vdb container (This takes a few minutes) $ docker-compose up vdb # When you see the following message the container has been created. @@ -360,4 +360,4 @@ $ docker-compose up --force-recreate ***If you choose this option, make sure you see the container restvv being re-created and all Python packages being reinstalled in the printed logs, otherwise the container may not actually be rebuilt and the contained modules may not - update*** \ No newline at end of file + update*** From eeeaae10df0bdc558451e584eb02ca785ed8fec3 Mon Sep 17 00:00:00 2001 From: leicray Date: Fri, 12 May 2023 14:25:15 +0100 Subject: [PATCH 095/218] Update DOCKER.md --- docs/DOCKER.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 4e5f73f..a0818af 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -2,12 +2,12 @@ ## Prerequisites -To install rest_variantValidator via Docker, first ensure you have both docker and docker-compose installed. +To install rest_variantValidator via Docker, first ensure that you have both docker and docker-compose installed. See their [documentation](https://docs.docker.com/compose/install/) for information. ## Clone the rest_VariantValidator Repository -Create a directory collate your cloned repositories. Move into the directory then, clone the repository. +Create a directory to collate your cloned repositories. Move into the directory, then clone the repository. ```bash $ git clone https://github.com/openvar/rest_variantValidator @@ -39,7 +39,7 @@ VariantValidator [manual](https://github.com/openvar/variantValidator/blob/maste - Edit the `vdb_docker.df` file -You need to select your chip set e.g. Arm or Intel and remove the relevant hash. Default is intel +You need to select your chip set e.g. ARM or Intel and remove the relevant hash. Default is Intel. ## Build the container From 5fd2600163cc4772ac23c7aed95283f847562c56 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Tue, 16 May 2023 10:51:00 +0100 Subject: [PATCH 096/218] Update ports for more guniorn options --- docker-compose.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 0ceefbc..ef5059b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,10 +38,14 @@ services: - share:/usr/local/share ports: - "5000:5000" + - "5050:5050" - "8000:8000" + - "8080:8080" expose: - "5000" + - "5050" - "8000" + - "8080" volumes: share: From 7efe25796265c1eda95ca7d4e9030e71e3490a8a Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 17 May 2023 11:31:15 +0100 Subject: [PATCH 097/218] set postgres to 12 for use on Pi --- docs/DOCKER.md | 4 ---- vvta_docker.df | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index a0818af..ae2a9bf 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -37,10 +37,6 @@ do not update your container for more than 12 months; else leave as `None`. See Note: Reconfiguration can be achieved by accessing the docker container through bash. See below for entry and the VariantValidator [manual](https://github.com/openvar/variantValidator/blob/master/docs/MANUAL.md) for details -- Edit the `vdb_docker.df` file - -You need to select your chip set e.g. ARM or Intel and remove the relevant hash. Default is Intel. - ## Build the container *Note: some of these steps take >>1hr to complete depending on the speed of your internet connection, particularly diff --git a/vvta_docker.df b/vvta_docker.df index 0157e42..2a17a9c 100644 --- a/vvta_docker.df +++ b/vvta_docker.df @@ -1,4 +1,4 @@ -FROM postgres:13.10 +FROM postgres:12.12 ENV POSTGRES_DB=vvta From c16eca0806db0a675b4197ef0a9bbf12e6f04d5b Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 24 May 2023 13:59:03 +0100 Subject: [PATCH 098/218] Migrate to postgres 14 --- docker-compose.yml | 1 + docs/DOCKER.md | 25 ++++++++++++------------- vvta_docker.df | 30 +++++++++++++++++++++++------- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ef5059b..3a9efdf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,6 +17,7 @@ services: - "54321:5432" expose: - "54321" + shm_size: 2g seqrepo: build: context: . diff --git a/docs/DOCKER.md b/docs/DOCKER.md index ae2a9bf..ddb133f 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -330,28 +330,27 @@ However, instead of running `pip install -e`, we can test the install using the python rest_variantValidator/app.py ``` -## Updating rest_variantValidator using docker-compose +## Updating rest_variantValidator +To update a container, use + +```bash +$ docker-compose down +$ docker-compose build --no-cache +$ docker-compose up +``` + +## Deleting rest_variantValidator Update requires that the restvv container is deleted from your system. This is not achieved by removing the container If you are only running rest_variantValidator in docker, we recommend deleting and re-building all containers ```bash # Remove the specific containers -$ docker-compose rm +$ docker-compose rm # OR Delete all containers on your system -$ docker-compose rm -$ docker system prune -a --volumes -``` - -***Once you have deleted the containers, go to Install and Build*** - -Alternatively, you may wish to try and force the containers to re-build without deleting - -```bash -# Force re-build $ docker-compose down -$ docker-compose up --force-recreate +$ docker system prune -a --volumes ``` ***If you choose this option, make sure you see the container restvv being re-created and all Python packages being diff --git a/vvta_docker.df b/vvta_docker.df index 2a17a9c..c092a03 100644 --- a/vvta_docker.df +++ b/vvta_docker.df @@ -1,14 +1,30 @@ -FROM postgres:12.12 +FROM postgres:14.7 ENV POSTGRES_DB=vvta - ENV POSTGRES_USER=uta_admin - ENV POSTGRES_PASSWORD=uta_admin -RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/* +# Install necessary dependencies +RUN apt-get update && \ + apt-get install -y \ + wget \ + && rm -rf /var/lib/apt/lists/* + +# Auto-create and set PostgreSQL configuration +RUN echo "shared_buffers = 2GB" > /docker-entrypoint-initdb.d/postgresql.conf + +# Step 1: Download the file +RUN wget https://www528.lamp.le.ac.uk/vvdata/vvta/vvta_2022_11_no_seq.sql.gz -O input_file.sql.gz + +# Step 2: Extract the gzipped file +RUN gzip -dq input_file.sql.gz + +# Step 3: Use sed to replace text +RUN sed 's/anyarray/anycompatiblearray/g' input_file.sql > modified_file.sql -# Prepare to migrate to postgres 14 -# RUN wget --output-document=https://www528.lamp.le.ac.uk/vvdata/vvta/vvta_2022_11_no_seq.sql.gz | gzip -dq | sed 's/anyarray/anycompatiblearray/g' | gzip > docker-entrypoint-initdb.d/vvta_2022_11_noseq.sql.gz +# Step 4: Compress the modified file +RUN rm input_file.sql +RUN gzip modified_file.sql -RUN wget --output-document=vvta_2022_11_noseq.sql.gz https://www528.lamp.le.ac.uk/vvdata/vvta/vvta_2022_11_no_seq.sql.gz -O /docker-entrypoint-initdb.d/vvta_2022_11_noseq.sql.gz +# Step 5: Move the modified and compressed file to the desired location +RUN mv modified_file.sql.gz /docker-entrypoint-initdb.d/vvta_2022_11_noseq.sql.gz From b0d032ac5d40e6eb868ac6f280413c76a9fb55da Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Tue, 6 Jun 2023 12:36:05 +0100 Subject: [PATCH 099/218] A few docker tweaks that make logging work correctly --- Dockerfile | 3 --- configuration/docker.ini | 1 + docker-compose.yml | 23 +++++++++++++---------- rest_VariantValidator/app.py | 3 +-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index d31b645..d256256 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,9 +27,6 @@ RUN pip install -e . # Copy the config file into the container home diorectory COPY configuration/docker.ini /root/.variantvalidator -# Expose the port -EXPOSE 8000 - # Define the entrypoint as an empty command ENTRYPOINT [] diff --git a/configuration/docker.ini b/configuration/docker.ini index cf1e8d9..c4e2efa 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -24,6 +24,7 @@ password = uta_admin log = True console = WARNING file = ERROR +file_path = /usr/local/share/logs [Entrez] email = OPTIONAL diff --git a/docker-compose.yml b/docker-compose.yml index 3a9efdf..e41ce01 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,8 @@ services: ports: - "33061:3306" expose: - - "33061" + - "33061" # Expose port for external access + vvta: build: context: . @@ -16,14 +17,16 @@ services: ports: - "54321:5432" expose: - - "54321" + - "54321" # Expose port for external access shm_size: 2g + seqrepo: build: context: . dockerfile: vvsr_docker.df volumes: - - seqdata:/usr/local/share/seqrepo + - seqdata:/usr/local/share/seqrepo # Mount volume for sequence data + restvv: build: . entrypoint: /bin/bash @@ -34,16 +37,16 @@ services: - vvta - seqrepo volumes: - - logs:/usr/local/share/logs - - seqdata:/usr/local/share/seqrepo - - share:/usr/local/share + - logs:/usr/local/share/logs # Mount volume for logs + - seqdata:/usr/local/share/seqrepo # Mount volume for sequence data + - share:/usr/local/share # Mount volume for shared data ports: - "5000:5000" - "5050:5050" - "8000:8000" - "8080:8080" expose: - - "5000" + - "5000" # Expose ports for external access - "5050" - "8000" - "8080" @@ -54,16 +57,16 @@ volumes: driver_opts: type: 'none' o: 'bind' - device: '${HOME}/variantvalidator_data/share' + device: '${HOME}/variantvalidator_data/share' # Mount host directory for shared data seqdata: driver: local driver_opts: type: 'none' o: 'bind' - device: '${HOME}/variantvalidator_data/share/seqrepo' + device: '${HOME}/variantvalidator_data/share/seqrepo' # Mount host directory for sequence data logs: driver: local driver_opts: type: 'none' o: 'bind' - device: '${HOME}/variantvalidator_data/share/logs' + device: '${HOME}/variantvalidator_data/share/logs' # Mount host directory for logs diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 053212a..be23036 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -37,10 +37,9 @@ # This sets the maximum size of the log to 0.5Mb and allows two additional logs # The logs are then deleted and replaced in rotation try: - parent = config['logging']['log_to'] + parent = config['logging']['file_path'] except KeyError: pass - logHandler = handlers.RotatingFileHandler(str(parent) + '/rest_VariantValidator.log', maxBytes=500000, backupCount=2) From 3aa2f3aa68ef3027e6e22722171a2dec001e60f1 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 14 Jun 2023 18:32:33 +0100 Subject: [PATCH 100/218] Freeze requirements --- REQUIREMENTS.txt | 120 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 99 insertions(+), 21 deletions(-) diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index 1279758..0b3c072 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -1,21 +1,99 @@ -git+https://github.com/openvar/vv_hgvs@master#egg=vvhgvs -git+https://github.com/openvar/variantValidator@master#egg=VariantValidator -git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter -biopython -gunicorn -biocommons.seqrepo>=0.5.1 -httplib2>=0.9.0 -configparser>=3.5.0 -pyliftover>=0.3 -biotools>=0.3.0 -requests -dicttoxml -pytest>=3.6 -pytest-cov -codecov -flask-restx -flask-socketio -flask == 1.1.4 -jinja2==2.11.3 -Werkzeug==1.0.1 -markupsafe==2.0.1 +aniso8601==9.0.1 +appdirs==1.4.4 +appnope==0.1.3 +attrs==22.1.0 +beautifulsoup4==4.11.1 +bidict==0.22.0 +biocommons.seqrepo==0.6.5 +biopython==1.79 +biotools==1.2.12 +bioutils==0.5.7 +blinker==1.6.2 +brotlipy @ file:///Users/runner/miniforge3/conda-bld/brotlipy_1666764759924/work +bs4==0.0.1 +certifi==2022.9.24 +cffi @ file:///Users/runner/miniforge3/conda-bld/cffi_1666754817555/work +charset-normalizer @ file:///home/conda/feedstock_root/build_artifacts/charset-normalizer_1661170624537/work +click==8.1.3 +codecov==2.1.12 +coloredlogs==15.0.1 +configparser @ file:///home/conda/feedstock_root/build_artifacts/configparser_1660952164778/work +coverage==6.5.0 +cryptography @ file:///Users/runner/miniforge3/conda-bld/cryptography_1667424024887/work +cssselect==1.2.0 +decorator==5.1.1 +dicttoxml==1.7.4 +dnspython @ file:///home/conda/feedstock_root/build_artifacts/dnspython_1646621171513/work +enum34==1.1.10 +exceptiongroup==1.0.1 +fake-useragent==0.1.14 +Flask==2.3.2 +Flask-HTTPAuth==4.7.0 +flask-restx==1.0.3 +Flask-SocketIO==5.3.1 +Flask-SQLAlchemy==2.5.1 +gunicorn==20.1.0 +httplib2 @ file:///home/conda/feedstock_root/build_artifacts/httplib2_1667305570420/work +humanfriendly==10.0 +idna @ file:///home/conda/feedstock_root/build_artifacts/idna_1663625384323/work +importlib-metadata==5.0.0 +iniconfig==1.1.1 +ipython==5.10.0 +itsdangerous==2.1.2 +Jinja2==3.1.2 +jsonschema==4.17.0 +lxml==4.9.1 +MarkupSafe==2.1.3 +mysql-connector-python==8.0.31 +numpy @ file:///Users/runner/miniforge3/conda-bld/numpy_1666788277198/work +packaging==21.3 +parse==1.19.0 +Parsley==1.3 +passlib==1.7.4 +pexpect==4.8.0 +pickleshare==0.7.5 +pluggy==1.0.0 +prompt-toolkit==1.0.18 +protobuf==3.20.1 +psycopg2==2.9.5 +psycopg2-binary==2.9.5 +ptyprocess==0.7.0 +pycparser @ file:///home/conda/feedstock_root/build_artifacts/pycparser_1636257122734/work +pyee==8.2.2 +Pygments==2.5.2 +pyliftover==0.4 +PyMySQL==1.0.2 +pyOpenSSL @ file:///home/conda/feedstock_root/build_artifacts/pyopenssl_1665350324128/work +pyparsing @ file:///home/conda/feedstock_root/build_artifacts/pyparsing_1652235407899/work +pyppeteer==1.0.2 +pyquery==1.4.3 +pyrsistent==0.19.2 +pysam==0.20.0 +PySocks @ file:///home/conda/feedstock_root/build_artifacts/pysocks_1661604839144/work +pytest==7.2.0 +pytest-cov==4.0.0 +python-engineio==4.3.4 +python-socketio==5.7.2 +pytz==2022.6 +requests @ file:///home/conda/feedstock_root/build_artifacts/requests_1661872987712/work +requests-html==0.10.0 +-e git+https://github.com/openvar/VariantsInJournals.git@e8922a98dd4b583c0a3857cf5b5fd023499e3552#egg=rest_VariantValidator +simplegeneric==0.8.1 +six==1.16.0 +soupsieve==2.3.2.post1 +SQLAlchemy==1.4.44 +sqlparse==0.4.3 +tabulate==0.9.0 +tomli==2.0.1 +tqdm==4.64.1 +traitlets==5.5.0 +urllib3 @ file:///home/conda/feedstock_root/build_artifacts/urllib3_1658789158161/work +-e git+https://github.com/openvar/variantFormatter.git@5f6251aa656ec7bc18199d6587a746ef88f990d6#egg=VariantFormatter +-e git+https://github.com/openvar/variantValidator.git@aa87b9746ec2790f96ff4b27182c998453730753#egg=VariantValidator +-e git+https://github.com/openvar/vv_hgvs.git@69b1a7c107f3bba1bb43ef1de19c22ba62ff4c80#egg=vvhgvs +w3lib==2.0.1 +wcwidth==0.2.5 +websockets==10.4 +Werkzeug==2.3.6 +yoyo-migrations==8.1.0 +zipp==3.10.0 From f76928ef8b8f77ae83d70c955a9f07583419b7da Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 15 Jun 2023 09:06:41 +0100 Subject: [PATCH 101/218] Clean up installation manual and REQUIREMENTS Should now be a new enviroment vvrest --- REQUIREMENTS.txt | 116 ++++++-------------------------- configuration/.variantvalidator | 29 ++++++++ docs/INSTALLATION.md | 33 +++++---- environment.yml | 14 ++++ 4 files changed, 83 insertions(+), 109 deletions(-) create mode 100644 configuration/.variantvalidator create mode 100644 environment.yml diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index 0b3c072..26eea04 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -1,99 +1,23 @@ -aniso8601==9.0.1 -appdirs==1.4.4 -appnope==0.1.3 -attrs==22.1.0 -beautifulsoup4==4.11.1 -bidict==0.22.0 -biocommons.seqrepo==0.6.5 -biopython==1.79 -biotools==1.2.12 -bioutils==0.5.7 -blinker==1.6.2 -brotlipy @ file:///Users/runner/miniforge3/conda-bld/brotlipy_1666764759924/work -bs4==0.0.1 -certifi==2022.9.24 -cffi @ file:///Users/runner/miniforge3/conda-bld/cffi_1666754817555/work -charset-normalizer @ file:///home/conda/feedstock_root/build_artifacts/charset-normalizer_1661170624537/work -click==8.1.3 -codecov==2.1.12 -coloredlogs==15.0.1 -configparser @ file:///home/conda/feedstock_root/build_artifacts/configparser_1660952164778/work -coverage==6.5.0 -cryptography @ file:///Users/runner/miniforge3/conda-bld/cryptography_1667424024887/work -cssselect==1.2.0 -decorator==5.1.1 -dicttoxml==1.7.4 -dnspython @ file:///home/conda/feedstock_root/build_artifacts/dnspython_1646621171513/work -enum34==1.1.10 -exceptiongroup==1.0.1 -fake-useragent==0.1.14 -Flask==2.3.2 -Flask-HTTPAuth==4.7.0 +git+https://github.com/openvar/vv_hgvs@master#egg=vvhgvs +git+https://github.com/openvar/variantValidator@master#egg=VariantValidator +git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter +biopython +gunicorn +biocommons.seqrepo>=0.5.1 +httplib2>=0.9.0 +configparser>=3.5.0 +pyliftover>=0.3 +biotools>=0.3.0 +requests +dicttoxml +pytest>=3.6 +pytest-cov +codecov +dicttoxml + +# Core web components flask-restx==1.0.3 -Flask-SocketIO==5.3.1 -Flask-SQLAlchemy==2.5.1 -gunicorn==20.1.0 -httplib2 @ file:///home/conda/feedstock_root/build_artifacts/httplib2_1667305570420/work -humanfriendly==10.0 -idna @ file:///home/conda/feedstock_root/build_artifacts/idna_1663625384323/work -importlib-metadata==5.0.0 -iniconfig==1.1.1 -ipython==5.10.0 -itsdangerous==2.1.2 +Flask==2.3.2 Jinja2==3.1.2 -jsonschema==4.17.0 -lxml==4.9.1 -MarkupSafe==2.1.3 -mysql-connector-python==8.0.31 -numpy @ file:///Users/runner/miniforge3/conda-bld/numpy_1666788277198/work -packaging==21.3 -parse==1.19.0 -Parsley==1.3 -passlib==1.7.4 -pexpect==4.8.0 -pickleshare==0.7.5 -pluggy==1.0.0 -prompt-toolkit==1.0.18 -protobuf==3.20.1 -psycopg2==2.9.5 -psycopg2-binary==2.9.5 -ptyprocess==0.7.0 -pycparser @ file:///home/conda/feedstock_root/build_artifacts/pycparser_1636257122734/work -pyee==8.2.2 -Pygments==2.5.2 -pyliftover==0.4 -PyMySQL==1.0.2 -pyOpenSSL @ file:///home/conda/feedstock_root/build_artifacts/pyopenssl_1665350324128/work -pyparsing @ file:///home/conda/feedstock_root/build_artifacts/pyparsing_1652235407899/work -pyppeteer==1.0.2 -pyquery==1.4.3 -pyrsistent==0.19.2 -pysam==0.20.0 -PySocks @ file:///home/conda/feedstock_root/build_artifacts/pysocks_1661604839144/work -pytest==7.2.0 -pytest-cov==4.0.0 -python-engineio==4.3.4 -python-socketio==5.7.2 -pytz==2022.6 -requests @ file:///home/conda/feedstock_root/build_artifacts/requests_1661872987712/work -requests-html==0.10.0 --e git+https://github.com/openvar/VariantsInJournals.git@e8922a98dd4b583c0a3857cf5b5fd023499e3552#egg=rest_VariantValidator -simplegeneric==0.8.1 -six==1.16.0 -soupsieve==2.3.2.post1 -SQLAlchemy==1.4.44 -sqlparse==0.4.3 -tabulate==0.9.0 -tomli==2.0.1 -tqdm==4.64.1 -traitlets==5.5.0 -urllib3 @ file:///home/conda/feedstock_root/build_artifacts/urllib3_1658789158161/work --e git+https://github.com/openvar/variantFormatter.git@5f6251aa656ec7bc18199d6587a746ef88f990d6#egg=VariantFormatter --e git+https://github.com/openvar/variantValidator.git@aa87b9746ec2790f96ff4b27182c998453730753#egg=VariantValidator --e git+https://github.com/openvar/vv_hgvs.git@69b1a7c107f3bba1bb43ef1de19c22ba62ff4c80#egg=vvhgvs -w3lib==2.0.1 -wcwidth==0.2.5 -websockets==10.4 Werkzeug==2.3.6 -yoyo-migrations==8.1.0 -zipp==3.10.0 +MarkupSafe==2.1.3 diff --git a/configuration/.variantvalidator b/configuration/.variantvalidator new file mode 100644 index 0000000..f6028c2 --- /dev/null +++ b/configuration/.variantvalidator @@ -0,0 +1,29 @@ +[mysql] +host = 127.0.0.1 +port = 3306 +database = validator +user = USER +password = PASSWORD + +[seqrepo] +version = PATH TO SEQREPO VERSION +location = PATH TO SEQREPO +require_threading = True + +[postgres] +host = 127.0.0.1 +database = vvta +port = 5432 +version = VVTA VERSION +user = USERNAME +password = PASSWORD + +[logging] +log = True +console = INFO +file = WARNING + +[Entrez] +email = None +api_key = None + diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index 83e78d4..5a17ec8 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -6,24 +6,31 @@ For any other systems, or if you cannot install the databases, we recommend inst ## Pre-requisites Installation requires [VariantValidator](https://github.com/openvar/variantValidator) and [VariantFormatter](https://github.com/openvar/variantFormatter) -## Installing VariantValidator +## Installing -When installing we have found that some of the VariantValidator dependencies do not load well using pip. -Instead install VariantValidator first using anaconda and the pre-configured environment.yml -See the [VariantValidator](https://github.com/openvar/variantValidator) installation documentation - -## Download the source code - -To download the source code simply clone the master branch. - -``` +Download the git repo +```bash $ git clone https://github.com/openvar/rest_variantValidator $ cd rest_variantValidator ``` -## Installing rest_variantValidator -To install rest_variantValidator within your virtual environment, activate the environment and run: + +When installing we have found that some of the VariantValidator dependencies do not load well using pip. +Instead, install using anaconda and the pre-configured environment.yml + +```bash +$ conda env create -f environment.yml +$ conda activate vvrest +$ pip install -r REQUIREMENTS.txt ``` -$ pip install -e . + +Only if you have never installed VariantValidator perform the following command otherwise your previous config will be +over-written + +```bash +cp ./configuration ~/.variantvalidator ``` + +See the [VariantValidator](https://github.com/openvar/variantValidator) installation documentation to install the +databases and set up configurations. Contact admin if you are having any difficulties diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..94a141d --- /dev/null +++ b/environment.yml @@ -0,0 +1,14 @@ +name: vvrest +channels: + - conda-forge + - bioconda +dependencies: + - python==3.10 + - pip + - protobuf + - mysql-connector-python + - pyliftover>=0.3 + - numpy + - httplib2>=0.9.0 + - configparser>=3.5.0 + - requests From fa266d7ab49ebfbe01de76f0ade543329e93215d Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 3 Aug 2023 14:35:05 +0100 Subject: [PATCH 102/218] Roll back mysql connector python to 8.0.31 to try fix error mysql.connector.errors.InterfaceError: 2003: Can't connect to MySQL server on '%-.100s:%u' (%s) (Warning: %u format: a real number is required, not str) --- REQUIREMENTS.txt | 1 - environment.yml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index 26eea04..69261f5 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -13,7 +13,6 @@ dicttoxml pytest>=3.6 pytest-cov codecov -dicttoxml # Core web components flask-restx==1.0.3 diff --git a/environment.yml b/environment.yml index 94a141d..d0b3938 100644 --- a/environment.yml +++ b/environment.yml @@ -6,7 +6,7 @@ dependencies: - python==3.10 - pip - protobuf - - mysql-connector-python + - mysql-connector-python=8.0.31 - pyliftover>=0.3 - numpy - httplib2>=0.9.0 From 60a704b1bb102b20f1a5b7cbb0f6b7e89aeb15d2 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 3 Aug 2023 16:51:34 +0100 Subject: [PATCH 103/218] update vdb to latest version --- docs/DOCKER.md | 2 +- environment.yml | 2 +- vdb_docker.df | 7 ++++--- vvta_docker.df | 1 + 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 004102b..02d806a 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -89,7 +89,7 @@ $ docker-compose up vdb # When you see the following message the container has been created. " /usr/sbin/mysqld: ready for connections" -# Then perforn shut down +# Then perform shut down ctrl + c # Create the SeqRepo comtainer diff --git a/environment.yml b/environment.yml index d0b3938..94a141d 100644 --- a/environment.yml +++ b/environment.yml @@ -6,7 +6,7 @@ dependencies: - python==3.10 - pip - protobuf - - mysql-connector-python=8.0.31 + - mysql-connector-python - pyliftover>=0.3 - numpy - httplib2>=0.9.0 diff --git a/vdb_docker.df b/vdb_docker.df index efe6c2d..d73a094 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -1,14 +1,15 @@ # Should run on all processors -FROM ubuntu/mysql:8.0-20.04_beta +FROM ubuntu/mysql:8.0-22.04_beta ENV MYSQL_RANDOM_ROOT_PASSWORD yes ENV MYSQL_DATABASE validator ENV MYSQL_USER vvadmin ENV MYSQL_PASSWORD var1ant -RUN apt-get update && apt-get install -y \ +RUN apt-get update && apt-get install -y \ wget RUN rm -rf /var/lib/apt/lists/* -RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2022_11.sql.gz -O /docker-entrypoint-initdb.d/validator_2022_11.sql.gz \ No newline at end of file +RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2022_11.sql.gz -O /docker-entrypoint-initdb.d/validator_2022_11.sql.gz + diff --git a/vvta_docker.df b/vvta_docker.df index c092a03..a8f8874 100644 --- a/vvta_docker.df +++ b/vvta_docker.df @@ -28,3 +28,4 @@ RUN gzip modified_file.sql # Step 5: Move the modified and compressed file to the desired location RUN mv modified_file.sql.gz /docker-entrypoint-initdb.d/vvta_2022_11_noseq.sql.gz + From 5910cb763eb73e99e096b64ade3bb37656b96a5a Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 7 Aug 2023 11:06:55 +0100 Subject: [PATCH 104/218] Add timeout setting for gunicorn --- Dockerfile | 6 +++--- docs/DOCKER.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index d256256..95abfc5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,11 +24,11 @@ RUN pip install -r REQUIREMENTS.txt # Install the tool RUN pip install -e . -# Copy the config file into the container home diorectory +# Copy the config file into the container home directory COPY configuration/docker.ini /root/.variantvalidator # Define the entrypoint as an empty command ENTRYPOINT [] -# Start the container with CMD -CMD ["gunicorn", "-b", "0.0.0.0:8000", "app", "--threads=5", "--chdir", "./rest_VariantValidator/"] +# Start the container with CMD and set Gunicorn timeout to 600 seconds +CMD ["gunicorn", "-b", "0.0.0.0:8000", "--timeout", "600", "app", "--threads=5", "--chdir", "./rest_VariantValidator/"] diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 02d806a..e362144 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -102,7 +102,7 @@ docker-compose up seqrepo $ docker-compose up -d # Start the server -$ docker exec -it rest_variantvalidator-restvv-1 gunicorn -b 0.0.0.0:8000 app --threads=5 --chdir ./rest_VariantValidator/ +$ docker exec -it rest_variantvalidator-restvv-1 gunicorn -b 0.0.0.0:8000 --timeout 600 app --threads=5 --chdir ./rest_VariantValidator/ # Now go to Running the app (below) if you have no build errors ``` @@ -188,7 +188,7 @@ $ docker-compose up --force-recreate $ docker-compose up -d # Start the server -$ docker exec -it rest_variantvalidator-restvv-1 gunicorn -b 0.0.0.0:8000 app --threads=5 --chdir ./rest_VariantValidator/ +$ docker exec -it rest_variantvalidator-restvv-1 gunicorn -b 0.0.0.0:8000 --timeout 600 app --threads=5 --chdir ./rest_VariantValidator/ ``` In a web browser navigate to From 2b3328aa75c6760115d77bc51d2fa9afdee06ee6 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 11 Aug 2023 14:58:50 +0100 Subject: [PATCH 105/218] Create batch genes2transcripts for v2 endpoint --- .../endpoints/variantvalidator_endpoints.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index c8d5ba8..8184c1f 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -105,10 +105,15 @@ def get(self, gene_query): return content -@api.route("/tools/gene2transcripts_v2///") +@api.route("/tools/gene2transcripts_v2////" + "") @api.param("gene_query", "***HGNC gene symbol, HGNC ID or transcript ID***\n" "\nCurrent supported transcript IDs" - "\n- RefSeq or Ensembl") + "\n- RefSeq or Ensembl""\n" + "\n***Single***\n" + "> COL1A1\n" + "\n***Multiple***\n" + "> COL1A1|COL1A2|COL5A1\n") @api.param("limit_transcripts", "***Return all possible transcripts***\n" "> False\n" "\n***Single***\n" @@ -121,16 +126,20 @@ def get(self, gene_query): "> select = All transcripts that have been classified as canonical") @api.param("transcript_set", "***RefSeq or Ensembl***\n" "\nall = all transcripts, refseq = RefSeq only, ensembl = Ensembl only") +@api.param("genome_build", "***GRCh37 or GRCh38***\n" + "\nall = all builds, GRCh37 = GRCh37 only, GRCh38 = GRCh38 only") class Gene2transcriptsV2Class(Resource): # Add documentation about the parser @api.expect(parser, validate=True) - def get(self, gene_query, limit_transcripts, transcript_set): - + def get(self, gene_query, limit_transcripts, transcript_set, genome_build): + if genome_build not in ["GRCh37", "GRCh38"]: + genome_build = None if "False" in limit_transcripts or "false" in limit_transcripts or limit_transcripts is False: limit_transcripts = None try: content = vval.gene2transcripts(gene_query, select_transcripts=limit_transcripts, - transcript_set=transcript_set) + transcript_set=transcript_set, genome_build=genome_build, + batch_output=True) except ConnectionError: message = "Cannot connect to rest.genenames.org, please try again later" raise exceptions.RemoteConnectionError(message) From 29df9fbff3c5029f2c5ad9346505b0335891d4d2 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Tue, 15 Aug 2023 19:03:28 +0100 Subject: [PATCH 106/218] Restructure and tweak for Aug 2023 release --- REQUIREMENTS.txt | 1 + rest_VariantValidator/app.py | 13 ++++++------ rest_VariantValidator/endpoints/hello.py | 7 +++---- .../endpoints/lovd_endpoints.py | 8 +++----- .../endpoints/variantformatter_endpoints.py | 7 +++---- .../endpoints/variantvalidator_endpoints.py | 20 +++++++++---------- .../endpoints/vijs_endpoints.py | 11 +++++----- .../{endpoints => utils}/exceptions.py | 0 .../{endpoints => utils}/representations.py | 2 +- .../{endpoints => utils}/request_parser.py | 2 +- vvsr_docker.df | 6 +++--- vvta_docker.df | 4 ++-- 12 files changed, 38 insertions(+), 43 deletions(-) rename rest_VariantValidator/{endpoints => utils}/exceptions.py (100%) rename rest_VariantValidator/{endpoints => utils}/representations.py (96%) rename rest_VariantValidator/{endpoints => utils}/request_parser.py (94%) diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt index 69261f5..436a936 100644 --- a/REQUIREMENTS.txt +++ b/REQUIREMENTS.txt @@ -1,3 +1,4 @@ +setuptools==58.2.0 git+https://github.com/openvar/vv_hgvs@master#egg=vvhgvs git+https://github.com/openvar/variantValidator@master#egg=VariantValidator git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index be23036..ec7f498 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -4,7 +4,8 @@ # Import modules from flask import Flask, request -from rest_VariantValidator.endpoints import api, representations, exceptions, request_parser +from rest_VariantValidator.endpoints import api +from rest_VariantValidator.utils import exceptions, request_parser, representations from logging import handlers import time import os @@ -80,10 +81,10 @@ # Requires the module make_response from flask and dict-to-xml -@api.representation('application/xml') +@api.representation('text/xml') def application_xml(data, code, headers): resp = representations.xml(data, code, headers) - resp.headers['Content-Type'] = 'application/xml' + resp.headers['Content-Type'] = 'text/xml' return resp @@ -117,7 +118,7 @@ def remote_connection_error_handler(e): # Collect Arguments args = parser.parse_args() - if args['content-type'] != 'application/xml': + if args['content-type'] != 'text/xml': return application_json({'message': str(e)}, 504, None) @@ -134,7 +135,7 @@ def not_found_error_handler(e): # Collect Arguments args = parser.parse_args() - if args['content-type'] != 'application/xml': + if args['content-type'] != 'text/xml': return application_json({'message': 'Requested Endpoint not found'}, 404, None) @@ -151,7 +152,7 @@ def default_error_handler(e): # Collect Arguments args = parser.parse_args() - if args['content-type'] != 'application/xml': + if args['content-type'] != 'text/xml': return application_json({'message': 'unhandled error: contact https://variantvalidator.org/contact_admin/'}, 500, None) diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py index 4153cfa..4e39c5f 100644 --- a/rest_VariantValidator/endpoints/hello.py +++ b/rest_VariantValidator/endpoints/hello.py @@ -1,6 +1,5 @@ from flask_restx import Namespace, Resource -from . import request_parser -from . import representations +from ..utils import request_parser, representations # Import VariantValidator code import VariantValidator @@ -44,8 +43,8 @@ def get(self): "metadata": config_dict }, 200, None) - # example: http://127.0.0.1:5000/name/name/bob?content-type=application/xml - elif args['content-type'] == 'application/xml': + # example: http://127.0.0.1:5000/name/name/bob?content-type=text/xml + elif args['content-type'] == 'text/xml': return representations.xml({ "status": "hello_world", "metadata": config_dict diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index dede1fd..aa3c2cb 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -1,9 +1,7 @@ # Import modules -import json import ast from flask_restx import Namespace, Resource -from . import request_parser -from . import representations +from ..utils import request_parser, representations # Import variantFormatter import VariantFormatter @@ -96,8 +94,8 @@ def get(self, genome_build, variant_description, transcript_model, select_transc if args['content-type'] == 'application/json': # example: http://127.0.0.1:5000.....bob?content-type=application/json return representations.application_json(content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=application/xml - elif args['content-type'] == 'application/xml': + # example: http://127.0.0.1:5000.....?content-type=text/xml + elif args['content-type'] == 'text/xml': return representations.xml(str(content), 200, None) else: # Return the api default output diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index ff3b02e..5480f53 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -1,7 +1,6 @@ # Import modules from flask_restx import Namespace, Resource -from . import request_parser -from . import representations +from ..utils import request_parser, representations # Import variantFormatter import VariantFormatter @@ -68,8 +67,8 @@ def get(self, genome_build, variant_description, transcript_model, select_transc if args['content-type'] == 'application/json': # example: http://127.0.0.1:5000.....bob?content-type=application/json return representations.application_json(content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=application/xml - elif args['content-type'] == 'application/xml': + # example: http://127.0.0.1:5000.....?content-type=text/xml + elif args['content-type'] == 'text/xml': return representations.xml(content, 200, None) else: # Return the api default output diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 8184c1f..a86f218 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -1,8 +1,6 @@ # Import modules from flask_restx import Namespace, Resource -from . import request_parser -from . import representations -from . import exceptions +from ..utils import exceptions, request_parser, representations # Import VariantValidator code import VariantValidator @@ -68,8 +66,8 @@ def get(self, genome_build, variant_description, select_transcripts): if args['content-type'] == 'application/json': # example: http://127.0.0.1:5000.....bob?content-type=application/json return representations.application_json(content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=application/xml - elif args['content-type'] == 'application/xml': + # example: http://127.0.0.1:5000.....?content-type=text/xml + elif args['content-type'] == 'text/xml': return representations.xml(content, 200, None) else: # Return the api default output @@ -97,8 +95,8 @@ def get(self, gene_query): if args['content-type'] == 'application/json': # example: http://127.0.0.1:5000.....bob?content-type=application/json return representations.application_json(content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=application/xml - elif args['content-type'] == 'application/xml': + # example: http://127.0.0.1:5000.....?content-type=text/xml + elif args['content-type'] == 'text/xml': return representations.xml(content, 200, None) else: # Return the api default output @@ -151,8 +149,8 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build): if args['content-type'] == 'application/json': # example: http://127.0.0.1:5000.....bob?content-type=application/json return representations.application_json(content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=application/xml - elif args['content-type'] == 'application/xml': + # example: http://127.0.0.1:5000.....?content-type=text/xml + elif args['content-type'] == 'text/xml': return representations.xml(content, 200, None) else: # Return the api default output @@ -178,8 +176,8 @@ def get(self, hgvs_description): if args['content-type'] == 'application/json': # example: http://127.0.0.1:5000.....bob?content-type=application/json return representations.application_json(content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=application/xml - elif args['content-type'] == 'application/xml': + # example: http://127.0.0.1:5000.....?content-type=text/xml + elif args['content-type'] == 'text/xml': return representations.xml(content, 200, None) else: # Return the api default output diff --git a/rest_VariantValidator/endpoints/vijs_endpoints.py b/rest_VariantValidator/endpoints/vijs_endpoints.py index 7d09626..82c11c0 100644 --- a/rest_VariantValidator/endpoints/vijs_endpoints.py +++ b/rest_VariantValidator/endpoints/vijs_endpoints.py @@ -1,7 +1,6 @@ # Import modules from flask_restx import Namespace, Resource -from . import request_parser -from . import representations +from ..utils import request_parser, representations # Import variantFormatter import VariantFormatter @@ -161,8 +160,8 @@ def get(self, genome_build, variant_description): if args['content-type'] == 'application/json': # example: http://127.0.0.1:5000.....bob?content-type=application/json return representations.application_json(refreshed_content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=application/xml - elif args['content-type'] == 'application/xml': + # example: http://127.0.0.1:5000.....?content-type=text/xml + elif args['content-type'] == 'text/xml': return representations.xml(refreshed_content, 200, None) else: # Return the api default output @@ -254,8 +253,8 @@ def get(self, genome_build, variant_description): if args['content-type'] == 'application/json': # example: http://127.0.0.1:5000.....bob?content-type=application/json return representations.application_json(refreshed_content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=application/xml - elif args['content-type'] == 'application/xml': + # example: http://127.0.0.1:5000.....?content-type=text/xml + elif args['content-type'] == 'text/xml': return representations.xml(refreshed_content, 200, None) else: # Return the api default output diff --git a/rest_VariantValidator/endpoints/exceptions.py b/rest_VariantValidator/utils/exceptions.py similarity index 100% rename from rest_VariantValidator/endpoints/exceptions.py rename to rest_VariantValidator/utils/exceptions.py diff --git a/rest_VariantValidator/endpoints/representations.py b/rest_VariantValidator/utils/representations.py similarity index 96% rename from rest_VariantValidator/endpoints/representations.py rename to rest_VariantValidator/utils/representations.py index 578e03f..2915948 100644 --- a/rest_VariantValidator/endpoints/representations.py +++ b/rest_VariantValidator/utils/representations.py @@ -16,7 +16,7 @@ def xml(data, code, headers): data = dicttoxml(data) resp = make_response(data, code) - resp.headers['Content-Type'] = 'application/xml' + resp.headers['Content-Type'] = 'text/xml' return resp diff --git a/rest_VariantValidator/endpoints/request_parser.py b/rest_VariantValidator/utils/request_parser.py similarity index 94% rename from rest_VariantValidator/endpoints/request_parser.py rename to rest_VariantValidator/utils/request_parser.py index feaf728..2b7ad94 100644 --- a/rest_VariantValidator/endpoints/request_parser.py +++ b/rest_VariantValidator/utils/request_parser.py @@ -7,7 +7,7 @@ parser.add_argument('content-type', type=str, help='***Select the response format***', - choices=['application/json', 'application/xml']) + choices=['application/json', 'text/xml']) # diff --git a/vvsr_docker.df b/vvsr_docker.df index 8e4bf9d..8646711 100644 --- a/vvsr_docker.df +++ b/vvsr_docker.df @@ -6,8 +6,8 @@ RUN apt-get install -y wget RUN mkdir -p /usr/local/share/seqrepo -RUN wget --output-document=/usr/local/share/seqrepo/VV_SR_2022_11.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2022_11.tar +RUN wget --output-document=/usr/local/share/seqrepo/VV_SR_2023_05.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2023_05.tar -RUN tar -xvf /usr/local/share/seqrepo/VV_SR_2022_11.tar --directory /usr/local/share/seqrepo +RUN tar -xvf /usr/local/share/seqrepo/VV_SR_2023_05.tar --directory /usr/local/share/seqrepo -RUN rm /usr/local/share/seqrepo/VV_SR_2022_11.tar \ No newline at end of file +RUN rm /usr/local/share/seqrepo/VV_SR_2023_05.tar \ No newline at end of file diff --git a/vvta_docker.df b/vvta_docker.df index a8f8874..402a81e 100644 --- a/vvta_docker.df +++ b/vvta_docker.df @@ -14,7 +14,7 @@ RUN apt-get update && \ RUN echo "shared_buffers = 2GB" > /docker-entrypoint-initdb.d/postgresql.conf # Step 1: Download the file -RUN wget https://www528.lamp.le.ac.uk/vvdata/vvta/vvta_2022_11_no_seq.sql.gz -O input_file.sql.gz +RUN wget https://www528.lamp.le.ac.uk/vvdata/vvta/vvta_2023_05_no_seq.sql.gz -O input_file.sql.gz # Step 2: Extract the gzipped file RUN gzip -dq input_file.sql.gz @@ -27,5 +27,5 @@ RUN rm input_file.sql RUN gzip modified_file.sql # Step 5: Move the modified and compressed file to the desired location -RUN mv modified_file.sql.gz /docker-entrypoint-initdb.d/vvta_2022_11_noseq.sql.gz +RUN mv modified_file.sql.gz /docker-entrypoint-initdb.d/vvta_2023_05_noseq.sql.gz From f00f3bdefa205e82e83ffd4344ecdf51371565a2 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 17 Aug 2023 16:39:21 +0100 Subject: [PATCH 107/218] Pre push --- REQUIREMENTS.txt | 23 ----- pyproject.toml | 83 +++++++++++++++++++ rest_VariantValidator/endpoints/hello.py | 2 +- .../endpoints/lovd_endpoints.py | 2 +- .../endpoints/variantformatter_endpoints.py | 2 +- .../endpoints/variantvalidator_endpoints.py | 2 +- .../endpoints/vijs_endpoints.py | 2 +- setup.py | 70 ---------------- 8 files changed, 88 insertions(+), 98 deletions(-) delete mode 100644 REQUIREMENTS.txt create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt deleted file mode 100644 index 436a936..0000000 --- a/REQUIREMENTS.txt +++ /dev/null @@ -1,23 +0,0 @@ -setuptools==58.2.0 -git+https://github.com/openvar/vv_hgvs@master#egg=vvhgvs -git+https://github.com/openvar/variantValidator@master#egg=VariantValidator -git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter -biopython -gunicorn -biocommons.seqrepo>=0.5.1 -httplib2>=0.9.0 -configparser>=3.5.0 -pyliftover>=0.3 -biotools>=0.3.0 -requests -dicttoxml -pytest>=3.6 -pytest-cov -codecov - -# Core web components -flask-restx==1.0.3 -Flask==2.3.2 -Jinja2==3.1.2 -Werkzeug==2.3.6 -MarkupSafe==2.1.3 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..7174516 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,83 @@ +[project] +name = "rest_VariantValidator" +dynamic = ["version"] +description = "REST API interface for VariantValidator" +license = {file = "LICENSE.txt"} +authors = [{name = "VariantValidator Contributors", email = "admin@variantvalidator.org"}] +readme = "README.md" +keywords = ["bioinformatics", + "computational biology", + "genome variants", + "genome variation", + "genomic variants", + "genomic variation", + "genomics", + "hgvs", + "HGVS", + "sequencevariants" +] + +requires-python = ">=3.9" + +dependencies = ["httplib2>=0.9.0", + "configparser>=3.5.0", + "dicttoxml", + "gunicorn", + "flask-restx==1.0.3", + "Flask==2.3.2", + "Jinja2==3.1.2", + "Werkzeug==2.3.6", + "MarkupSafe==2.1.3", + "VariantValidator@git+https://github.com/openvar/variantValidator@master#egg=VariantValidator" +] + +classifiers = [ + # How mature is this project? Common values are + # 3 - Alpha + # 4 - Beta + # 5 - Production/Stable + "Development Status :: 5 - Production/Stable", + + # Indicate who your project is intended for + "Intended Audience :: Developers", + "Topic :: Software Development :: Build Tools", + + # Pick your license as you wish + "License :: OSI Approved :: AGPL-3.0", + + # Specify the Python versions you support here. In particular, ensure + # that you indicate you support Python 3. These classifiers are *not* + # checked by "pip install". See instead "python_requires" below. + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", +] + +[project.urls] +"Homepage" = "https://variantvalidator.org/" +"Source" = "https://github.com/openvar/rest_variantValidator" +"Bug Reports" = "https://github.com/openvar/variantValidator/issues" +"Say Thanks!" = "https://www.buymeacoffee.com/VariantValidatr" + +[scripts] +update_vdb = "bin/update_vdb:main" +variant_validator = "bin/variant_validator:main" +vv_configure = "bin/vv_configure:main" +batch_validator = "bin/batch_validator:main" + +data = [ + { include = "configuration", glob = "configuration/empty_vv_db.sql" } +] + +[tool.setuptools] +py-modules = ["rest_VariantValidator"] + +[tool.setuptools_scm] + +[tool.poetry.packages] +exclude = ["batch", "locust"] + +[build-system] +requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"] +build-backend = "setuptools.build_meta" + diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py index 4e39c5f..60da27d 100644 --- a/rest_VariantValidator/endpoints/hello.py +++ b/rest_VariantValidator/endpoints/hello.py @@ -1,5 +1,5 @@ from flask_restx import Namespace, Resource -from ..utils import request_parser, representations +from rest_VariantValidator.utils import request_parser, representations # Import VariantValidator code import VariantValidator diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index aa3c2cb..cd20a97 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -1,7 +1,7 @@ # Import modules import ast from flask_restx import Namespace, Resource -from ..utils import request_parser, representations +from rest_VariantValidator.utils import request_parser, representations # Import variantFormatter import VariantFormatter diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index 5480f53..c9bacb7 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -1,6 +1,6 @@ # Import modules from flask_restx import Namespace, Resource -from ..utils import request_parser, representations +from rest_VariantValidator.utils import request_parser, representations # Import variantFormatter import VariantFormatter diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index a86f218..9e49ba5 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -1,6 +1,6 @@ # Import modules from flask_restx import Namespace, Resource -from ..utils import exceptions, request_parser, representations +from rest_VariantValidator.utils import exceptions, request_parser, representations # Import VariantValidator code import VariantValidator diff --git a/rest_VariantValidator/endpoints/vijs_endpoints.py b/rest_VariantValidator/endpoints/vijs_endpoints.py index 82c11c0..20aad9e 100644 --- a/rest_VariantValidator/endpoints/vijs_endpoints.py +++ b/rest_VariantValidator/endpoints/vijs_endpoints.py @@ -1,6 +1,6 @@ # Import modules from flask_restx import Namespace, Resource -from ..utils import request_parser, representations +from rest_VariantValidator.utils import request_parser, representations # Import variantFormatter import VariantFormatter diff --git a/setup.py b/setup.py deleted file mode 100644 index a2f2291..0000000 --- a/setup.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python - -# Prefer setuptools over distutils -from setuptools import setup - -setup( - name='rest_VariantValidator', - use_scm_version=True, - zip_safe=True, - author="VariantValidator Contributors", - author_email='admin@variantvalidator.org', - packages=['rest_VariantValidator', - 'rest_VariantValidator.endpoints', - 'rest_VariantValidator.utils', - ], - description='Rest API for VariantValidator', - long_description=open('README.md').read(), - url='https://github.com/openvar/variantFormatter', - license="GNU AFFERO GENERAL PUBLIC LICENSE, Version 3 (https://www.gnu.org/licenses/agpl-3.0.en.html)", - # See https://pypi.python.org/pypi?%3Aaction=list_classifiers - classifiers=[ - # How mature is this project? Common values are - # 3 - Alpha - # 4 - Beta - # 5 - Production/Stable - 'Development Status :: 3 - Alpha', - - # Audience - 'Intended Audience :: Developers', - 'Topic :: Software Development :: Build Tools', - - # Specify the Python versions - 'Programming Language :: Python', - ], - - # What does your project relate to? - keywords=[ - "bioinformatics", - "computational biology", - "genome variants", - "genome variation", - "genomic variants", - "genomic variation", - "genomics", - "hgvs", - "HGVS", - "sequencevariants", - ], - - setup_requires=[ - "setuptools_scm", - ] -) - -# -# Copyright (C) 2016-2023 VariantValidator Contributors -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program 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 Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# From 0335ae5669d3b9f0153853166b22e7a2459c9c35 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 18 Aug 2023 17:18:52 +0100 Subject: [PATCH 108/218] Add testing --- docs/INSTALLATION.md | 17 +- environment.yml | 10 +- pyproject.toml | 104 ++++--- rest_VariantValidator/endpoints/__init__.py | 2 - rest_VariantValidator/endpoints/hello.py | 23 +- .../endpoints/vijs_endpoints.py | 279 ------------------ .../utils/representations.py | 4 +- tests/test_endpoints.py | 18 ++ tests/test_handlers.py | 35 +++ 9 files changed, 139 insertions(+), 353 deletions(-) delete mode 100644 rest_VariantValidator/endpoints/vijs_endpoints.py create mode 100644 tests/test_endpoints.py create mode 100644 tests/test_handlers.py diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index 5a17ec8..e215a16 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -14,23 +14,12 @@ $ git clone https://github.com/openvar/rest_variantValidator $ cd rest_variantValidator ``` - - -When installing we have found that some of the VariantValidator dependencies do not load well using pip. -Instead, install using anaconda and the pre-configured environment.yml - +Create a virtual environment - recommended ```bash $ conda env create -f environment.yml $ conda activate vvrest -$ pip install -r REQUIREMENTS.txt -``` - -Only if you have never installed VariantValidator perform the following command otherwise your previous config will be -over-written - -```bash -cp ./configuration ~/.variantvalidator ``` See the [VariantValidator](https://github.com/openvar/variantValidator) installation documentation to install the -databases and set up configurations. Contact admin if you are having any difficulties +databases and set up configurations. You will need to run the confuguration script if you have not installed this API +previously. Contact admin if you are having any difficulties diff --git a/environment.yml b/environment.yml index 94a141d..a04aa4e 100644 --- a/environment.yml +++ b/environment.yml @@ -3,12 +3,6 @@ channels: - conda-forge - bioconda dependencies: - - python==3.10 + - python==3.11 - pip - - protobuf - - mysql-connector-python - - pyliftover>=0.3 - - numpy - - httplib2>=0.9.0 - - configparser>=3.5.0 - - requests + diff --git a/pyproject.toml b/pyproject.toml index 7174516..a9b4661 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,83 +1,93 @@ +# Project metadata [project] name = "rest_VariantValidator" -dynamic = ["version"] +dynamic = ["version"] # Use dynamic version based on the setuptools_scm plugin description = "REST API interface for VariantValidator" -license = {file = "LICENSE.txt"} -authors = [{name = "VariantValidator Contributors", email = "admin@variantvalidator.org"}] -readme = "README.md" +license = {file = "LICENSE.txt"} # Specify the license file +authors = [{name = "VariantValidator Contributors", email = "admin@variantvalidator.org"}] # Author information +readme = "README.md" # Specify the README file keywords = ["bioinformatics", - "computational biology", - "genome variants", - "genome variation", - "genomic variants", - "genomic variation", - "genomics", - "hgvs", - "HGVS", - "sequencevariants" -] + "computational biology", + "genome variants", + "genome variation", + "genomic variants", + "genomic variation", + "genomics", + "hgvs", + "HGVS", + "sequencevariants"] # Keywords associated with the project + +requires-python = ">=3.9" # Minimum required Python version -requires-python = ">=3.9" +# List of project dependencies +dependencies = [ + # Dependencies that will be installed via PyPi + "httplib2>=0.9.0", + "configparser>=3.5.0", + "dicttoxml==1.7.16", + "gunicorn==21.2.0", + "flask-restx==1.1.0", + "Flask==2.3.2", + "Jinja2==3.1.2", + "Werkzeug==2.3.7", + "MarkupSafe==2.1.3", -dependencies = ["httplib2>=0.9.0", - "configparser>=3.5.0", - "dicttoxml", - "gunicorn", - "flask-restx==1.0.3", - "Flask==2.3.2", - "Jinja2==3.1.2", - "Werkzeug==2.3.6", - "MarkupSafe==2.1.3", - "VariantValidator@git+https://github.com/openvar/variantValidator@master#egg=VariantValidator" + # Dependencies from other repositories, specified with their repository URLs and package names + "vvhgvs@git+https://github.com/openvar/vv_hgvs@master#egg=vvhgvs", + "VariantFormatter@git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter", + "VariantValidator@git+https://github.com/openvar/variantValidator@master#egg=VariantValidator" ] +# Project classifiers classifiers = [ - # How mature is this project? Common values are - # 3 - Alpha - # 4 - Beta - # 5 - Production/Stable - "Development Status :: 5 - Production/Stable", - - # Indicate who your project is intended for - "Intended Audience :: Developers", - "Topic :: Software Development :: Build Tools", - - # Pick your license as you wish - "License :: OSI Approved :: AGPL-3.0", - - # Specify the Python versions you support here. In particular, ensure - # that you indicate you support Python 3. These classifiers are *not* - # checked by "pip install". See instead "python_requires" below. - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Topic :: Software Development :: Build Tools", + "License :: OSI Approved :: AGPL-3.0", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11" ] +# URLs associated with the project [project.urls] "Homepage" = "https://variantvalidator.org/" "Source" = "https://github.com/openvar/rest_variantValidator" "Bug Reports" = "https://github.com/openvar/variantValidator/issues" "Say Thanks!" = "https://www.buymeacoffee.com/VariantValidatr" +# Scripts and data files [scripts] +# Define scripts that will be available after installation, with their entry points +# After install, these scripts will be accessible for the user to run update_vdb = "bin/update_vdb:main" variant_validator = "bin/variant_validator:main" vv_configure = "bin/vv_configure:main" -batch_validator = "bin/batch_validator:main" +# Specify additional data files included in the distribution data = [ { include = "configuration", glob = "configuration/empty_vv_db.sql" } ] +# Setuptools configuration [tool.setuptools] +# Specify the main Python modules to be included in the distribution py-modules = ["rest_VariantValidator"] +# Setuptools SCM configuration [tool.setuptools_scm] -[tool.poetry.packages] +# Setuptools package discovery configuration +[tool.setuptools.packages.find] +# Define where to look for packages (root directory) +where = ["."] +# Specify patterns to include and exclude for package discovery +include = ["rest_VariantValidator*"] exclude = ["batch", "locust"] +# Build system configuration [build-system] +# Specify required dependencies for the build system requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"] +# Specify the build backend to use build-backend = "setuptools.build_meta" - diff --git a/rest_VariantValidator/endpoints/__init__.py b/rest_VariantValidator/endpoints/__init__.py index a8b7cd5..0e68684 100644 --- a/rest_VariantValidator/endpoints/__init__.py +++ b/rest_VariantValidator/endpoints/__init__.py @@ -6,7 +6,6 @@ from .variantvalidator_endpoints import api as ns_vv from .variantformatter_endpoints import api as ns_vf from .lovd_endpoints import api as ns_lovd -from .vijs_endpoints import api as ns_vijs from .hello import api as ns_hello # Obtain VariantValidator related metadata @@ -51,7 +50,6 @@ def specs_url(self): api.add_namespace(ns_vv) api.add_namespace(ns_vf) api.add_namespace(ns_lovd) -api.add_namespace(ns_vijs) api.add_namespace(ns_hello) diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py index 60da27d..b92e8a8 100644 --- a/rest_VariantValidator/endpoints/hello.py +++ b/rest_VariantValidator/endpoints/hello.py @@ -1,5 +1,6 @@ from flask_restx import Namespace, Resource -from rest_VariantValidator.utils import request_parser, representations +from rest_VariantValidator.utils import request_parser, representations, exceptions +from flask import abort # Import VariantValidator code import VariantValidator @@ -58,6 +59,26 @@ def get(self): } +@api.route('/trigger_error/') +class ExceptionClass(Resource): + @api.expect(parser, validate=True) + def get(self, error_code): + print("WUWUWU") + print(error_code) + if error_code == 400: + abort(400, "Bad Request") + elif error_code == 403: + abort(403, "Forbidden") + elif error_code == 404: + abort(404, "Not Found") + elif error_code == 500: + abort(500, "Internal Server Error") + elif error_code == 999: + print("HERE") + raise exceptions.RemoteConnectionError('https://rest.variantvalidator.org/variantvalidator currently ' + 'unavailable') + + # # Copyright (C) 2016-2023 VariantValidator Contributors # diff --git a/rest_VariantValidator/endpoints/vijs_endpoints.py b/rest_VariantValidator/endpoints/vijs_endpoints.py deleted file mode 100644 index 20aad9e..0000000 --- a/rest_VariantValidator/endpoints/vijs_endpoints.py +++ /dev/null @@ -1,279 +0,0 @@ -# Import modules -from flask_restx import Namespace, Resource -from rest_VariantValidator.utils import request_parser, representations - -# Import variantFormatter -import VariantFormatter -import VariantFormatter.simpleVariantFormatter -import VariantValidator -vval = VariantValidator.Validator() - -""" -Create a list containing common warnings which are not errors -""" -my_warnings = [ - "A more recent version of the selected reference sequence", - "No transcripts found that fully overlap the described variation", - "is pending therefore changes may be made to the LRG reference sequence", - "This coding sequence variant description spans at least one intron", - "RefSeqGene record not available", - "automapped to equivalent RefSeq record", - "Protein level variant descriptions are not fully supported", - "is HGVS compliant and contains a valid reference amino acid description" -] - - -""" -Create a parser object locally -""" -parser = request_parser.parser - -api = Namespace('VariantsInJournals', description='Endpoints to ensure variants submitted to journals are validated') - - -@api.route("/transcript_descriptions//") -@api.param("variant_description", "***HGVS***\n" - "> - NM_000088.3:c.589G>T\n" - "> - LRG_1t1:c.589G>T\n" - "> - *Recommended maximum is 60 variants per submission*\n") -@api.param("genome_build", "***Accepted:***\n" - "> - GRCh37\n" - "> - GRCh38\n" - "> - hg19\n" - "> - hg38") -class VariantValidatorClass(Resource): - # Add documentation about the parser - @api.expect(parser, validate=True) - def get(self, genome_build, variant_description): - - # List the submitted descriptions and the allowed reference sequence types - description_list = variant_description.split() - allowed_references = ["NM_", - "NR_", - "ENST", - "NP_", - "ENSP", - "t", - "p"] - - # Check correct variant type (Currently transcript but also accepts Protein - # even though we do not advertise this - variant_description_outs = [] - for description in description_list: - for reference in allowed_references: - if reference in description: - variant_description_outs.append(description) - variant_description = "|".join(variant_description_outs) - - # Refresh the content - refreshed_content = {} - if variant_description == "": - refreshed_content = {"error": "Unsupported variant type"} - else: - # Validate using the VariantValidator Python Library - validate = vval.validate(variant_description, genome_build, select_transcripts='all', liftover_level=None) - content = validate.format_as_dict(with_meta=True) - - for k, v in content.items(): - if k == "metadata": - refreshed_content[k] = v - elif k == "flag": - continue - else: - refreshed_content[v["submitted_variant"]] = {} - refreshed_content[v["submitted_variant"]]['pass'] = False - refreshed_content[v["submitted_variant"]]['errors'] = [] - refreshed_content[v["submitted_variant"]]['correction'] = None - - # Handle transcript variant inputs - if "p." not in v["submitted_variant"]: - # Filter the errors/warnings i.e. removing any warnings that are not actual errors - # Warnings which are not errors are contained in the my_warnings list - error_found = [] - if v["validation_warnings"] is not []: - for warning in v["validation_warnings"]: - safe_found = False - for safe in my_warnings: - if safe in warning: - safe_found = True - break - if safe_found is False: - error_found.append(warning) - refreshed_content[v["submitted_variant"]]['errors'] = error_found - - # Is the input == to the output? - if "LRG" not in v["submitted_variant"]: - if v["submitted_variant"] == v["hgvs_transcript_variant"]: - refreshed_content[v["submitted_variant"]]['pass'] = True - else: - refreshed_content[v["submitted_variant"]]['correction'] = v["hgvs_transcript_variant"] - else: - if v["submitted_variant"] == v["hgvs_lrg_transcript_variant"]: - refreshed_content[v["submitted_variant"]]['pass'] = True - else: - refreshed_content[v["submitted_variant"]]['correction'] = v["hgvs_lrg_transcript" - "_variant"] - - else: - # Filter the errors/warnings i.e. removing any warnings that are not actual errors - # Warnings which are not errors are contained in the my_warnings list - error_found = [] - if v["validation_warnings"] is not []: - for warning in v["validation_warnings"]: - safe_found = False - for safe in my_warnings: - if safe in warning: - safe_found = True - break - if safe_found is False: - error_found.append(warning) - refreshed_content[v["submitted_variant"]]['errors'] = error_found - - # Is the input == to the output? - if "LRG" not in v["submitted_variant"]: - if v["submitted_variant"] == v["hgvs_predicted_protein_consequence"]["tlr"]: - refreshed_content[v["submitted_variant"]]['pass'] = True - elif v["submitted_variant"] == v["hgvs_predicted_protein_consequence"]["slr"]: - refreshed_content[v["submitted_variant"]]['pass'] = True - else: - if v["hgvs_predicted_protein_consequence"]["tlr"] != "": - refreshed_content[v["submitted_variant"]]['correction'] = v[ - "hgvs_predicted_protein_consequence"]["tlr"] - else: - refreshed_content[v["submitted_variant"]]['correction'] = None - else: - if v["submitted_variant"] == v["hgvs_predicted_protein_consequence"]["lrg_tlr"]: - refreshed_content[v["submitted_variant"]]['pass'] = True - elif v["submitted_variant"] == v["hgvs_predicted_protein_consequence"]["lrg_slr"]: - refreshed_content[v["submitted_variant"]]['pass'] = True - else: - if v["hgvs_predicted_protein_consequence"]["lrg_tlr"] != "": - refreshed_content[v["submitted_variant"]]['correction'] = v[ - "hgvs_predicted_protein_consequence"]["lrg_tlr"] - else: - refreshed_content[v["submitted_variant"]]['correction'] = None - - # Collect Arguments - args = parser.parse_args() - - # Overrides the default response route so that the standard HTML URL can return any specified format - if args['content-type'] == 'application/json': - # example: http://127.0.0.1:5000.....bob?content-type=application/json - return representations.application_json(refreshed_content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=text/xml - elif args['content-type'] == 'text/xml': - return representations.xml(refreshed_content, 200, None) - else: - # Return the api default output - return refreshed_content - - -@api.route("/genomic_descriptions///") -@api.param("variant_description", "***Genomic HGVS***\n" - "> - NC_000017.10:g.48275363C>A\n" - "> - *Recommended maximum is 60 variants per submission*\n") -@api.param("genome_build", "***Accepted:***\n" - "> - GRCh37\n" - "> - GRCh38\n" - "> - hg19\n" - "> - hg38\n") -class LOVDClass(Resource): - # Add documentation about the parser - @api.expect(parser, validate=True) - def get(self, genome_build, variant_description): - transcript_model = 'all' - select_transcripts = None - checkonly = True - liftover = False - - # List the submitted descriptions and the allowed reference sequence types - description_list = variant_description.split() - allowed_references = ["NC_", - "NG_"] - - # Check correct variant type (Currently transcript but also accepts Protein - # even though we do not advertise this - variant_description_outs = [] - for description in description_list: - for reference in allowed_references: - if reference in description: - variant_description_outs.append(description) - variant_description = "|".join(variant_description_outs) - - # Refresh the content - refreshed_content = {} - if variant_description == "": - refreshed_content = {"error": "Unsupported variant type"} - else: - # Validate using the VariantValidator Python Library - content = VariantFormatter.simpleVariantFormatter.format(variant_description, - genome_build, - transcript_model, - select_transcripts, - checkonly, - liftover) - - for k, v in content.items(): - if k == "metadata": - refreshed_content[k] = v - else: - for k2, v2 in v.items(): - if k2 == "flag" or k2 == "errors": - continue - - # else - refreshed_content[k2] = {} - refreshed_content[k2]['pass'] = False - refreshed_content[k2]['errors'] = [] - refreshed_content[k2]['correction'] = None - - # Filter the errors/warnings i.e. removing any warnings that are not actual errors - # Warnings which are not errors are contained in the my_warnings list - error_found = [] - if v2["genomic_variant_error"] is not None: - safe_found = False - for safe in my_warnings: - if safe in v2["genomic_variant_error"]: - safe_found = True - break - if safe_found is False: - error_found.append(v2["genomic_variant_error"]) - refreshed_content[k2]['errors'] = error_found - - # Populate the rest of the output - if k2 == v2["g_hgvs"]: - refreshed_content[k2]["pass"] = True - else: - refreshed_content[k2]["correction"] = v2["g_hgvs"] - - # Collect Arguments - args = parser.parse_args() - - # Overrides the default response route so that the standard HTML URL can return any specified format - if args['content-type'] == 'application/json': - # example: http://127.0.0.1:5000.....bob?content-type=application/json - return representations.application_json(refreshed_content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=text/xml - elif args['content-type'] == 'text/xml': - return representations.xml(refreshed_content, 200, None) - else: - # Return the api default output - return refreshed_content - - -# -# Copyright (C) 2016-2023 VariantValidator Contributors -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program 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 Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# diff --git a/rest_VariantValidator/utils/representations.py b/rest_VariantValidator/utils/representations.py index 2915948..e1ebcc1 100644 --- a/rest_VariantValidator/utils/representations.py +++ b/rest_VariantValidator/utils/representations.py @@ -1,5 +1,5 @@ # Import modules -from flask import make_response +from flask import make_response, jsonify from dicttoxml import dicttoxml """ @@ -21,7 +21,7 @@ def xml(data, code, headers): def application_json(data, code, headers): - resp = make_response(data, code) + resp = make_response(jsonify(data), code) resp.headers['Content-Type'] = 'application/json' return resp diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py new file mode 100644 index 0000000..232d2af --- /dev/null +++ b/tests/test_endpoints.py @@ -0,0 +1,18 @@ +# Import necessary packages +import pytest +from rest_VariantValidator.app import application # Import your Flask app + + +# Fixture to set up the test client +@pytest.fixture(scope='module') +def client(): + application.testing = True + return application.test_client() # Create a test client to interact with the app + + +# Test function for the /hello/ endpoint +def test_hello_endpoint(client): + response = client.get('/hello/') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK + assert response.json["status"] == "hello_world" # Check the JSON response content + assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response diff --git a/tests/test_handlers.py b/tests/test_handlers.py new file mode 100644 index 0000000..af287d8 --- /dev/null +++ b/tests/test_handlers.py @@ -0,0 +1,35 @@ +# Import necessary packages +import pytest +from rest_VariantValidator.app import application # Import your Flask app + + +# Fixture to set up the test client +@pytest.fixture(scope='module') +def client(): + application.testing = True + return application.test_client() # Create a test client to interact with the app + + +# Test function for exception handlers +def test_bad_request_url(client): + response = client.get('/nonexistent/?content-type=application%2Fjson') # Send GET request to a nonexistent endpoint + assert response.status_code == 404 # Check if the response status code is 404 NOT FOUND + assert response.json["message"] == "Requested Endpoint not found" + + +def test_bad_request_code(client): + response = client.get('/trigger_error/404') # Send a GET request to a nonexistent endpoint + assert response.status_code == 404 # Check if the response status code is 404 NOT FOUND + assert response.json["message"] == "Requested Endpoint not found" + + +def test_error_code(client): + response = client.get('/hello/trigger_error/500') # Send a GET request to a nonexistent endpoint + assert response.status_code == 500 # Check if the response status code is 404 NOT FOUND + assert response.json["message"] == "Internal Server Error" + + +def test_connection_error(client): + response = client.get('/hello/trigger_error/999') # Send a GET request to a nonexistent endpoint + assert response.status_code == 504 # Check if the response status code is 404 NOT FOUND + assert response.json["message"] == "https://rest.variantvalidator.org/variantvalidator currently unavailable" From 45236cfa2acda19de712a1bbabbf19532fe4e821 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Tue, 29 Aug 2023 11:54:02 +0100 Subject: [PATCH 109/218] update docker for new version using toml --- Dockerfile | 3 --- configuration/docker.ini | 6 +++--- docs/DOCKER.md | 31 ++++++++++++++++++++++++++----- vdb_docker.df | 2 +- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/Dockerfile b/Dockerfile index 95abfc5..9df8764 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,9 +18,6 @@ RUN apt-get -y install git # Updrade pip RUN pip install --upgrade pip -# Install any needed packages specified in requirements.txt -RUN pip install -r REQUIREMENTS.txt - # Install the tool RUN pip install -e . diff --git a/configuration/docker.ini b/configuration/docker.ini index c4e2efa..215fc85 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -4,10 +4,10 @@ port = 3306 database = validator user = vvadmin password = var1ant -version = vvdb_2022_11 +version = vvdb_2023_8 [seqrepo] -version = VV_SR_2022_11/master +version = VV_SR_2023_05/master location = /usr/local/share/seqrepo require_threading = True @@ -15,7 +15,7 @@ require_threading = True host = vvta port = 5432 database = vvta -version = vvta_2022_11 +version = vvta_2023_05 user = uta_admin password = uta_admin diff --git a/docs/DOCKER.md b/docs/DOCKER.md index e362144..70d459d 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -93,18 +93,39 @@ $ docker-compose up vdb ctrl + c # Create the SeqRepo comtainer -docker-compose up seqrepo +$ docker-compose up seqrepo # This will auto exit once complete and you will see "exited with code 0" +``` -# Start the container -$ docker-compose up -d +### Test the build +```bash +# Start the container in bash mode +$ docker-compose exec restvv bash -# Start the server +# Run PyTest (all tests should pass) +$ pytest + +# Exit the container +$ exit +``` + +# Start the container and run the server +```bash +# Start the container in detached mode +$ docker-compose up -d + +# Run the server $ docker exec -it rest_variantvalidator-restvv-1 gunicorn -b 0.0.0.0:8000 --timeout 600 app --threads=5 --chdir ./rest_VariantValidator/ +``` -# Now go to Running the app (below) if you have no build errors +In a web browser navigate to +[http://0.0.0.0:8000](http://0.0.0.0:8000) + +When you are finished, stop the container +``` +ctrl + c ``` ### Build errors you may encounter diff --git a/vdb_docker.df b/vdb_docker.df index d73a094..148eeb7 100644 --- a/vdb_docker.df +++ b/vdb_docker.df @@ -11,5 +11,5 @@ RUN apt-get update && apt-get install -y \ RUN rm -rf /var/lib/apt/lists/* -RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2022_11.sql.gz -O /docker-entrypoint-initdb.d/validator_2022_11.sql.gz +RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2023_08.sql.gz -O /docker-entrypoint-initdb.d/validator_2023_08.sql.gz From 0e41f78cc7ae858b768e76fb9622bf0d38192079 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 31 Aug 2023 09:19:46 +0100 Subject: [PATCH 110/218] Set Python requirement to >=3.6 --- pyproject.toml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a9b4661..5a0a4c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ keywords = ["bioinformatics", "HGVS", "sequencevariants"] # Keywords associated with the project -requires-python = ">=3.9" # Minimum required Python version +requires-python = ">=3.6" # Minimum required Python version # List of project dependencies dependencies = [ @@ -33,9 +33,9 @@ dependencies = [ "MarkupSafe==2.1.3", # Dependencies from other repositories, specified with their repository URLs and package names - "vvhgvs@git+https://github.com/openvar/vv_hgvs@master#egg=vvhgvs", - "VariantFormatter@git+https://github.com/openvar/variantFormatter@master#egg=VariantFormatter", - "VariantValidator@git+https://github.com/openvar/variantValidator@master#egg=VariantValidator" + "vvhgvs@git+https://github.com/openvar/vv_hgvs@master", + "VariantFormatter@git+https://github.com/openvar/variantFormatter@master", + "VariantValidator@git+https://github.com/openvar/variantValidator@master" ] # Project classifiers @@ -44,6 +44,9 @@ classifiers = [ "Intended Audience :: Developers", "Topic :: Software Development :: Build Tools", "License :: OSI Approved :: AGPL-3.0", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11" From 7be9962e47d99d6a5aa0f3ad81d81393542a0c37 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Tue, 26 Sep 2023 20:09:04 +0100 Subject: [PATCH 111/218] Update Docker process --- Dockerfile | 10 +- configuration/docker.ini | 4 +- .../seqrepo/Dockerfile | 6 +- .../vdb/Dockerfile | 0 .../vvta/Dockerfile | 0 docker-compose.yml | 37 +++--- docs/DOCKER.md | 115 ++++-------------- 7 files changed, 55 insertions(+), 117 deletions(-) rename vvsr_docker.df => db_dockerfiles/seqrepo/Dockerfile (77%) rename vdb_docker.df => db_dockerfiles/vdb/Dockerfile (100%) rename vvta_docker.df => db_dockerfiles/vvta/Dockerfile (100%) diff --git a/Dockerfile b/Dockerfile index 9df8764..01fae9f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,16 +10,18 @@ COPY . /app RUN mkdir /usr/local/share/logs # Update apt-get -RUN apt-get update +RUN apt update -# Install git -RUN apt-get -y install git +# Install apt managed sofware +RUN apt -y install git \ + postgresql-client \ + sqlite3 # Updrade pip RUN pip install --upgrade pip # Install the tool -RUN pip install -e . +RUN pip install . # Copy the config file into the container home directory COPY configuration/docker.ini /root/.variantvalidator diff --git a/configuration/docker.ini b/configuration/docker.ini index 215fc85..b95f6e5 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -1,5 +1,5 @@ [mysql] -host = vdb +host = rv-vdb port = 3306 database = validator user = vvadmin @@ -12,7 +12,7 @@ location = /usr/local/share/seqrepo require_threading = True [postgres] -host = vvta +host = rv-vvta port = 5432 database = vvta version = vvta_2023_05 diff --git a/vvsr_docker.df b/db_dockerfiles/seqrepo/Dockerfile similarity index 77% rename from vvsr_docker.df rename to db_dockerfiles/seqrepo/Dockerfile index 8646711..4679253 100644 --- a/vvsr_docker.df +++ b/db_dockerfiles/seqrepo/Dockerfile @@ -10,4 +10,8 @@ RUN wget --output-document=/usr/local/share/seqrepo/VV_SR_2023_05.tar https://ww RUN tar -xvf /usr/local/share/seqrepo/VV_SR_2023_05.tar --directory /usr/local/share/seqrepo -RUN rm /usr/local/share/seqrepo/VV_SR_2023_05.tar \ No newline at end of file +RUN rm /usr/local/share/seqrepo/VV_SR_2023_05.tar + +ENTRYPOINT [] + +CMD ["tail", "-f", "/dev/null"] \ No newline at end of file diff --git a/vdb_docker.df b/db_dockerfiles/vdb/Dockerfile similarity index 100% rename from vdb_docker.df rename to db_dockerfiles/vdb/Dockerfile diff --git a/vvta_docker.df b/db_dockerfiles/vvta/Dockerfile similarity index 100% rename from vvta_docker.df rename to db_dockerfiles/vvta/Dockerfile diff --git a/docker-compose.yml b/docker-compose.yml index e41ce01..7592dce 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,50 +1,49 @@ version: '3' services: - vdb: + rv-vdb: build: context: . - dockerfile: vdb_docker.df + dockerfile: db_dockerfiles/vdb/Dockerfile ports: - "33061:3306" expose: - "33061" # Expose port for external access - vvta: + rv-vvta: build: context: . - dockerfile: vvta_docker.df + dockerfile: db_dockerfiles/vvta/Dockerfile ports: - "54321:5432" expose: - "54321" # Expose port for external access shm_size: 2g - seqrepo: + rv-seqrepo: build: context: . - dockerfile: vvsr_docker.df + dockerfile: db_dockerfiles/seqrepo/Dockerfile volumes: - seqdata:/usr/local/share/seqrepo # Mount volume for sequence data - restvv: + rest-variantvalidator: build: . entrypoint: /bin/bash command: ["-c", "sleep infinity"] restart: always depends_on: - - vdb - - vvta - - seqrepo + - rv-vdb + - rv-vvta + - rv-seqrepo volumes: - - logs:/usr/local/share/logs # Mount volume for logs + - vv-logs:/usr/local/share/logs # Mount volume for logs - seqdata:/usr/local/share/seqrepo # Mount volume for sequence data - - share:/usr/local/share # Mount volume for shared data ports: - "5000:5000" - "5050:5050" - "8000:8000" - - "8080:8080" + - "9000:9000" expose: - "5000" # Expose ports for external access - "5050" @@ -52,21 +51,15 @@ services: - "8080" volumes: - share: - driver: local - driver_opts: - type: 'none' - o: 'bind' - device: '${HOME}/variantvalidator_data/share' # Mount host directory for shared data seqdata: driver: local driver_opts: type: 'none' o: 'bind' - device: '${HOME}/variantvalidator_data/share/seqrepo' # Mount host directory for sequence data - logs: + device: '${HOME}/variantvalidator_data/seqdata' + vv-logs: driver: local driver_opts: type: 'none' o: 'bind' - device: '${HOME}/variantvalidator_data/share/logs' # Mount host directory for logs + device: '${HOME}/variantvalidator_data/logs' diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 70d459d..4ff6271 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -50,12 +50,10 @@ $ docker-compose pull - Create a directory for sharing resources between your computer and the container ```bash -$ mkdir ~/variantvalidator_data -$ mkdir ~/variantvalidator_data/share -$ mkdir ~/variantvalidator_data/share/seqrepo/ -$ mkdir ~/variantvalidator_data/share/logs +$ mkdir -p ~/variantvalidator_data/seqdata +$ mkdir -p ~/variantvalidator_data/logs ``` -*i.e.* a directory called `variantvalidator_data/share` in your `home` directory +*i.e.,* a directory called `variantvalidator_data/seqdata` in your `home` directory - Build @@ -71,53 +69,23 @@ $ docker-compose build --no-cache - Test each container and completes builds if necessary ```bash -# If you have previously installed this software you will need to remove old SeqRepo databases -$ rm -r -f ~/variantvalidator_data/share/seqrepo/ - -# Create the vvta container (This takes ~10 minutes to complete) -$ docker-compose up vvta - -# When you see the following message the container has been created (Can take ~30 min or so). -"database system is ready to accept connections" - -# Then perforn shut down -ctrl + c - -# Create the vdb container (This takes a few minutes) -$ docker-compose up vdb - -# When you see the following message the container has been created. -" /usr/sbin/mysqld: ready for connections" - -# Then perform shut down -ctrl + c - -# Create the SeqRepo comtainer -$ docker-compose up seqrepo - -# This will auto exit once complete and you will see -"exited with code 0" +# Start vvta containers (This takes ~10 minutes to complete) +$ docker-compose up -d rv-vvta +$ docker-compose up -d rv-vdb +$ docker-compose up -d rv-seqrepo +$ docker-compose up -d rest-variantvalidator ``` ### Test the build ```bash -# Start the container in bash mode -$ docker-compose exec restvv bash - # Run PyTest (all tests should pass) -$ pytest - -# Exit the container -$ exit +$ docker exec rest_variantvalidator-rest-variantvalidator-1 pytest ``` -# Start the container and run the server +# Run the server ```bash # Start the container in detached mode -$ docker-compose up -d - -# Run the server -$ docker exec -it rest_variantvalidator-restvv-1 gunicorn -b 0.0.0.0:8000 --timeout 600 app --threads=5 --chdir ./rest_VariantValidator/ +$ docker exec -it rest_variantvalidator-rest-variantvalidator-1 gunicorn -b 0.0.0.0:8000 --timeout 600 app --threads=5 --chdir ./rest_VariantValidator/ ``` In a web browser navigate to @@ -128,6 +96,8 @@ When you are finished, stop the container ctrl + c ``` +***Note: you may need to change :8080 to one of :5000 or :8000 depending on whether you altered the default port above*** + ### Build errors you may encounter ***If you have MySQL and or Postgres databases already running, you may encounter an error*** @@ -160,12 +130,8 @@ services: ``` - hash (`#`) the conflicting port and add the new ports as shown above -- force-recreate the container -```bash -$ docker-compose down -$ docker-compose up --force-recreate -``` +Once complete, re-build as above ***You may encounter a build error relating to other unavailable ports*** @@ -195,38 +161,13 @@ If you encounter these issues, stop the build by pressing `ctrl+c` - hash (`#`) the conflicting port and add the new ports as shown above - Change the command in Dockerfile to reflect the changes e.g. `CMD gunicorn -b 0.0.0.0:8080 app --threads=5 --chdir ./rest_VariantValidator/` -- force-recreate the container - -```bash -$ docker-compose down -$ docker-compose up --force-recreate -``` - -## Running the app - -```bash -# Start the container -$ docker-compose up -d - -# Start the server -$ docker exec -it rest_variantvalidator-restvv-1 gunicorn -b 0.0.0.0:8000 --timeout 600 app --threads=5 --chdir ./rest_VariantValidator/ -``` - -In a web browser navigate to -[http://0.0.0.0:8000](http://0.0.0.0:8000) - -When you are finished, stop the container -``` -ctrl + c -``` - -***Note: you may need to change :8080 to one of :5000 or :8000 depending on whether you altered the default port above*** +Once complete, re-build as above ## Accessing the VariantValidator databases externally It is possible to access both the UTA and Validator databases outside of docker as they expose the - default PostgreSQL and MySQL ports (5432 and 3306 respectively). In the current set-up it is not possible to - access the seqrepo database outside of docker. + default PostgreSQL and MySQL ports (5432 and 3306 respectively). You can also access the seqrepo database outside of +docker. Navigate to ~/variantvalidator_data/seqdata ## Accessing VariantValidator directly through bash and reconfiguring a container post build @@ -235,7 +176,7 @@ The container hosts a full install of VariantValidator. To start this version you start the container in detached mode and access it using ```bash -$ docker-compose exec restvv bash +$ docker-compose exec rest_variantvalidator-rest-variantvalidator-1 bash ``` When you are finished exit the container @@ -244,17 +185,15 @@ When you are finished exit the container $ exit ``` -#### What you can do in bash mode +#### Running the VariantValidator shell script -1. Run VariantValidator can be run on the commandline from within the container - - Instructions can be found in the VariantValidator [manual](https://github.com/openvar/variantValidator/blob/master/docs/MANUAL.md) under sections **Database updates** and **Operation** - -2. Start the REST services in development mode, bound to port 5000 - - For example, this is useful if you want to develop new methods and test them - - Note: Under the terms and conditions of our [license](https://github.com/openvar/rest_variantValidator/blob/master/LICENSE.txt) changes to the code and improvements must be made available to the community so that we can integrate them for the good of all our users - - See instructions on VariantValidator development in Docker +Once installed and running it is possible to run VariantValidator via a bash shell using the running the container -3. For other commands that you can run using bash, see the [VariantValidator DOCKER.md](https://github.com/openvar/variantValidator/blob/master/docs/DOCKER.md) +**Example** +```bash +# Note: The variant description must be contained in '' or "". See MANUAL.md for more examples +$ docker exec -it rest_variantvalidator-rest-variantvalidator-1 python bin/variant_validator.py -v 'NC_000017.11:g.50198002C>A' -g GRCh38 -t mane -s individual -f json -m +``` ## Developing VariantValidator in Docker The container has been configured with git installed. This means that you can clone Repos directly into the container @@ -264,7 +203,7 @@ To develop VariantValidator in the container Start the container in detached mode ```bash -$ docker-compose exec restvv bash +$ docker-compose exec rest_variantvalidator-rest-variantvalidator-1 bash ``` ON YOUR COMPUTER change into the share directory @@ -345,7 +284,7 @@ $ docker-compose exec restvv bash $ cd /usr/local/share/DevelopmentRepos/rest_variantValidator ``` -However, instead of running `pip install -e`, we can test the install using the Python development server +However, instead of running `pip install -e .`, we can test the install using the Python development server ```bash python rest_variantValidator/app.py From 0eed03d99848567e14b2412156b61708ec8f6625 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 26 Oct 2023 15:26:58 +0100 Subject: [PATCH 112/218] Update git buffer for docker install. R.e. Issues installing on Ubuntu VM --- Dockerfile | 3 +++ wsgi.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 01fae9f..2115e13 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,9 @@ RUN apt -y install git \ postgresql-client \ sqlite3 +# Manage git buffer +RUN git config http.postBuffer 500000000 + # Updrade pip RUN pip install --upgrade pip diff --git a/wsgi.py b/wsgi.py index 359a1f8..dae525d 100644 --- a/wsgi.py +++ b/wsgi.py @@ -14,4 +14,4 @@ else: application.debug = False application.config['PROPAGATE_EXCEPTIONS'] = False - application.run(host="127.0.0.1", port=8080) + application.run(host="127.0.0.1", port=8000) From 002b1322cb561c88c90360419e85f8a399b464e5 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 26 Oct 2023 16:49:44 +0100 Subject: [PATCH 113/218] point gunicorn docker at the wsgi.py --- docs/DOCKER.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 4ff6271..cd23c6e 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -65,27 +65,33 @@ $ docker-compose build --no-cache - The first time you do this, it will complete the build process, for example, populating the required the databases - The build takes a while because the vv databases are large. However, this is a significant improvement on previou s versions. Build time is ~30 minutes (depending on the speed of you computer and internet connection) - - The build has completed when you see the message ***"Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them"*** + - The build has completed when you see -- Test each container and completes builds if necessary +``` +Creating rest_variantvalidator_rv-vvta_1 ... done +Creating rest_variantvalidator_rv-vdb_1 ... done +Creating rest_variantvalidator_rv-seqrepo_1 ... done +Creating rest_variantvalidator_rest-variantvalidator_1 ... done +``` + +Use this command to complete the build and wait for the above messages ```bash -# Start vvta containers (This takes ~10 minutes to complete) -$ docker-compose up -d rv-vvta -$ docker-compose up -d rv-vdb -$ docker-compose up -d rv-seqrepo -$ docker-compose up -d rest-variantvalidator +$ docker-compose up -d rv-vvta && \ + docker-compose up -d rv-vdb && \ + docker-compose up -d rv-seqrepo && \ + docker-compose up -d rest-variantvalidator ``` ### Test the build ```bash # Run PyTest (all tests should pass) -$ docker exec rest_variantvalidator-rest-variantvalidator-1 pytest +$ docker exec rest_variantvalidator_rest-variantvalidator_1 pytest ``` # Run the server ```bash # Start the container in detached mode -$ docker exec -it rest_variantvalidator-rest-variantvalidator-1 gunicorn -b 0.0.0.0:8000 --timeout 600 app --threads=5 --chdir ./rest_VariantValidator/ +$ docker exec -it rest_variantvalidator_rest-variantvalidator_1 gunicorn -b 0.0.0.0:8000 --timeout 600 app.wsgi:application --threads=5 --chdir ./rest_VariantValidator/ ``` In a web browser navigate to From c6295f59ae7c253b85f1b9840984a4fef211cfc8 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 26 Oct 2023 16:53:44 +0100 Subject: [PATCH 114/218] revert --- docs/DOCKER.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index cd23c6e..46418f3 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -91,7 +91,7 @@ $ docker exec rest_variantvalidator_rest-variantvalidator_1 pytest # Run the server ```bash # Start the container in detached mode -$ docker exec -it rest_variantvalidator_rest-variantvalidator_1 gunicorn -b 0.0.0.0:8000 --timeout 600 app.wsgi:application --threads=5 --chdir ./rest_VariantValidator/ +$ docker exec -it rest_variantvalidator_rest-variantvalidator_1 gunicorn -b 0.0.0.0:8000 --timeout 600 app --threads=5 --chdir ./rest_VariantValidator/ ``` In a web browser navigate to From 9fda2a209a2bf2ed7de6cb1ea987a4288feb4d6f Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 27 Oct 2023 12:00:55 +0100 Subject: [PATCH 115/218] update docker mounts --- docker-compose.yml | 7 +++++++ docs/DOCKER.md | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7592dce..9ac8a28 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -39,6 +39,7 @@ services: volumes: - vv-logs:/usr/local/share/logs # Mount volume for logs - seqdata:/usr/local/share/seqrepo # Mount volume for sequence data + - rest-vv-app:/app/rest_VariantValidator # Mount the app directory to your system ports: - "5000:5000" - "5050:5050" @@ -63,3 +64,9 @@ volumes: type: 'none' o: 'bind' device: '${HOME}/variantvalidator_data/logs' + rest-vv-app: + driver: local + driver_opts: + type: 'none' + o: 'bind' + device: ${PWD}/rest_VariantValidator diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 46418f3..3dbde72 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -91,7 +91,12 @@ $ docker exec rest_variantvalidator_rest-variantvalidator_1 pytest # Run the server ```bash # Start the container in detached mode -$ docker exec -it rest_variantvalidator_rest-variantvalidator_1 gunicorn -b 0.0.0.0:8000 --timeout 600 app --threads=5 --chdir ./rest_VariantValidator/ +$ docker exec -it rest_variantvalidator_rest-variantvalidator_1 gunicorn -b 0.0.0.0:8000 --timeout 600 wsgi:app --threads=5 --chdir ./rest_VariantValidator/ +``` + +Optional: If your docker instance has multiple available cores, you can increase processing power by starting multiple workers e.g. +```bash +docker exec -it rest_variantvalidator_rest-variantvalidator_1 gunicorn -b 0.0.0.0:8000 --workers 3 --timeout 600 wsgi:app --threads=5 --chdir ./rest_VariantValidator/ ``` In a web browser navigate to From 7c957b6b936640f997c29e4d628d99ad8a48493f Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 27 Oct 2023 13:37:08 +0100 Subject: [PATCH 116/218] update docker mounts --- rest_VariantValidator/wsgi.py | 43 +++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 rest_VariantValidator/wsgi.py diff --git a/rest_VariantValidator/wsgi.py b/rest_VariantValidator/wsgi.py new file mode 100644 index 0000000..2caafcd --- /dev/null +++ b/rest_VariantValidator/wsgi.py @@ -0,0 +1,43 @@ +""" +Gunicorn wsgi gateway file +""" +from rest_VariantValidator.app import application + +if __name__ == '__main__': + from configparser import ConfigParser + from VariantValidator.settings import CONFIG_DIR + + config = ConfigParser() + config.read(CONFIG_DIR) + + if config["logging"]["log"] == "True": + application.debug = True + application.config['PROPAGATE_EXCEPTIONS'] = True + else: + application.debug = False + application.config['PROPAGATE_EXCEPTIONS'] = False + + # Define the Gunicorn entry point + from gunicorn.app.wsgiapp import WSGIApplication + + + class GunicornApp(WSGIApplication): + def init(self, parser, opts, args): + return { + 'bind': '0.0.0.0:8000', + 'workers': 3, + 'timeout': 600, + 'chdir': './rest_VariantValidator/' + } + + + if __name__ == '__main__': + options = { + 'bind': '0.0.0.0:8000', + 'workers': 3, + 'timeout': 600, + 'chdir': './rest_VariantValidator/' + } + GunicornApp().run() + + application.run(host="127.0.0.1", port=8000) From b8af63123edf1ff2cddc92816350ab773ee79f5f Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Tue, 7 Nov 2023 15:46:04 +0000 Subject: [PATCH 117/218] move vval object creation into the endpoints to try prevent race --- rest_VariantValidator/endpoints/hello.py | 6 ++---- rest_VariantValidator/endpoints/lovd_endpoints.py | 4 ++-- .../endpoints/variantvalidator_endpoints.py | 13 ++++++++++++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py index b92e8a8..348c4b7 100644 --- a/rest_VariantValidator/endpoints/hello.py +++ b/rest_VariantValidator/endpoints/hello.py @@ -4,7 +4,6 @@ # Import VariantValidator code import VariantValidator -vval = VariantValidator.Validator() """ Create a parser object locally @@ -31,6 +30,8 @@ class HelloClass(Resource): @api.expect(parser, validate=True) def get(self): + vval = VariantValidator.Validator() + # Collect Arguments args = parser.parse_args() config_dict = vval.my_config() @@ -63,8 +64,6 @@ def get(self): class ExceptionClass(Resource): @api.expect(parser, validate=True) def get(self, error_code): - print("WUWUWU") - print(error_code) if error_code == 400: abort(400, "Bad Request") elif error_code == 403: @@ -74,7 +73,6 @@ def get(self, error_code): elif error_code == 500: abort(500, "Internal Server Error") elif error_code == 999: - print("HERE") raise exceptions.RemoteConnectionError('https://rest.variantvalidator.org/variantvalidator currently ' 'unavailable') diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index cd20a97..d258728 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -6,8 +6,8 @@ # Import variantFormatter import VariantFormatter import VariantFormatter.simpleVariantFormatter -import VariantValidator -vval = VariantValidator.Validator() +# import VariantValidator +# vval = VariantValidator.Validator() def ordereddict_to_dict(value): diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 9e49ba5..cff5f75 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -4,7 +4,6 @@ # Import VariantValidator code import VariantValidator -vval = VariantValidator.Validator() """ Create a parser object locally @@ -55,8 +54,11 @@ class VariantValidatorClass(Resource): @api.expect(parser, validate=True) def get(self, genome_build, variant_description, select_transcripts): + vval = VariantValidator.Validator() + # Validate using the VariantValidator Python Library validate = vval.validate(variant_description, genome_build, select_transcripts) + content = validate.format_as_dict(with_meta=True) # Collect Arguments @@ -82,6 +84,9 @@ class Gene2transcriptsClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) def get(self, gene_query): + + vval = VariantValidator.Validator() + try: content = vval.gene2transcripts(gene_query) except ConnectionError: @@ -130,6 +135,9 @@ class Gene2transcriptsV2Class(Resource): # Add documentation about the parser @api.expect(parser, validate=True) def get(self, gene_query, limit_transcripts, transcript_set, genome_build): + + vval = VariantValidator.Validator() + if genome_build not in ["GRCh37", "GRCh38"]: genome_build = None if "False" in limit_transcripts or "false" in limit_transcripts or limit_transcripts is False: @@ -167,6 +175,9 @@ class Hgvs2referenceClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) def get(self, hgvs_description): + + vval = VariantValidator.Validator() + content = vval.hgvs2ref(hgvs_description) # Collect Arguments From 42e3fe53430084ee15195ec5fa85b06bdd680d46 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Tue, 7 Nov 2023 15:52:52 +0000 Subject: [PATCH 118/218] add flask-cors --- pyproject.toml | 1 + rest_VariantValidator/app.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 5a0a4c4..eeef8dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ dependencies = [ "Jinja2==3.1.2", "Werkzeug==2.3.7", "MarkupSafe==2.1.3", + "flask-cors==4.0.0", # Dependencies from other repositories, specified with their repository URLs and package names "vvhgvs@git+https://github.com/openvar/vv_hgvs@master", diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index ec7f498..5bb7425 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -5,6 +5,7 @@ # Import modules from flask import Flask, request from rest_VariantValidator.endpoints import api +from flask_cors import CORS from rest_VariantValidator.utils import exceptions, request_parser, representations from logging import handlers import time @@ -67,6 +68,9 @@ # By default, show all endpoints (collapsed) application.config.SWAGGER_UI_DOC_EXPANSION = 'list' +# enable CORS +CORS(application, resources={r'/*': {'origins': '*'}}) + """ Representations From 05c37a2e825526e6399348d858cbbd93618d8c0e Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 9 Nov 2023 13:46:00 +0000 Subject: [PATCH 119/218] Create connection pool to speed up response times without sharing objects --- .../endpoints/lovd_endpoints.py | 9 +-- .../endpoints/variantformatter_endpoints.py | 11 ++-- .../endpoints/variantvalidator_endpoints.py | 16 +++-- rest_VariantValidator/utils/object_pool.py | 64 +++++++++++++++++++ rest_cors_test.html | 39 +++++++++++ 5 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 rest_VariantValidator/utils/object_pool.py create mode 100644 rest_cors_test.html diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index d258728..7566226 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -2,12 +2,11 @@ import ast from flask_restx import Namespace, Resource from rest_VariantValidator.utils import request_parser, representations +from rest_VariantValidator.utils.object_pool import simple_variant_formatter_pool # Import variantFormatter import VariantFormatter import VariantFormatter.simpleVariantFormatter -# import VariantValidator -# vval = VariantValidator.Validator() def ordereddict_to_dict(value): @@ -79,8 +78,10 @@ def get(self, genome_build, variant_description, transcript_model, select_transc if liftover == 'False' or liftover == 'false': liftover = False - content = VariantFormatter.simpleVariantFormatter.format(variant_description, genome_build, transcript_model, - select_transcripts, checkonly, liftover) + simple_formatter = simple_variant_formatter_pool.get() + content = simple_formatter.format(variant_description, genome_build, transcript_model, + select_transcripts, checkonly, liftover) + simple_variant_formatter_pool.return_object(simple_formatter) to_dict = ordereddict_to_dict(content) content = str(to_dict) diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index c9bacb7..6defe0e 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -1,10 +1,7 @@ # Import modules from flask_restx import Namespace, Resource from rest_VariantValidator.utils import request_parser, representations - -# Import variantFormatter -import VariantFormatter -import VariantFormatter.simpleVariantFormatter +from rest_VariantValidator.utils.object_pool import simple_variant_formatter_pool """ Create a parser object locally @@ -12,6 +9,8 @@ parser = request_parser.parser api = Namespace('VariantFormatter', description='Variantformatter API Endpoints') + + @api.route("/variantformatter////" "/") @api.param("variant_description", "***Genomic HGVS***\n" @@ -57,8 +56,10 @@ def get(self, genome_build, variant_description, transcript_model, select_transc if checkonly == 'True' or checkonly == 'true': checkonly = True - content = VariantFormatter.simpleVariantFormatter.format(variant_description, genome_build, transcript_model, + simple_formatter = simple_variant_formatter_pool.get() + content = simple_formatter.simpleVariantFormatter.format(variant_description, genome_build, transcript_model, select_transcripts, checkonly) + simple_variant_formatter_pool.return_object(simple_formatter) # Collect Arguments args = parser.parse_args() diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index cff5f75..7a5f3f6 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -1,9 +1,7 @@ # Import modules from flask_restx import Namespace, Resource from rest_VariantValidator.utils import exceptions, request_parser, representations - -# Import VariantValidator code -import VariantValidator +from rest_VariantValidator.utils.object_pool import vval_object_pool """ Create a parser object locally @@ -54,10 +52,11 @@ class VariantValidatorClass(Resource): @api.expect(parser, validate=True) def get(self, genome_build, variant_description, select_transcripts): - vval = VariantValidator.Validator() + vval = vval_object_pool.get_object() # Validate using the VariantValidator Python Library validate = vval.validate(variant_description, genome_build, select_transcripts) + vval_object_pool.return_object(vval) content = validate.format_as_dict(with_meta=True) @@ -85,13 +84,14 @@ class Gene2transcriptsClass(Resource): @api.expect(parser, validate=True) def get(self, gene_query): - vval = VariantValidator.Validator() + vval = vval_object_pool.get_object() try: content = vval.gene2transcripts(gene_query) except ConnectionError: message = "Cannot connect to rest.genenames.org, please try again later" raise exceptions.RemoteConnectionError(message) + vval_object_pool.return_object(vval) # Collect Arguments args = parser.parse_args() @@ -136,7 +136,7 @@ class Gene2transcriptsV2Class(Resource): @api.expect(parser, validate=True) def get(self, gene_query, limit_transcripts, transcript_set, genome_build): - vval = VariantValidator.Validator() + vval = vval_object_pool.get_object() if genome_build not in ["GRCh37", "GRCh38"]: genome_build = None @@ -149,6 +149,7 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build): except ConnectionError: message = "Cannot connect to rest.genenames.org, please try again later" raise exceptions.RemoteConnectionError(message) + vval_object_pool.return_object(vval) # Collect Arguments args = parser.parse_args() @@ -176,9 +177,10 @@ class Hgvs2referenceClass(Resource): @api.expect(parser, validate=True) def get(self, hgvs_description): - vval = VariantValidator.Validator() + vval = vval_object_pool.get_object() content = vval.hgvs2ref(hgvs_description) + vval_object_pool.return_object(vval) # Collect Arguments args = parser.parse_args() diff --git a/rest_VariantValidator/utils/object_pool.py b/rest_VariantValidator/utils/object_pool.py new file mode 100644 index 0000000..2aae999 --- /dev/null +++ b/rest_VariantValidator/utils/object_pool.py @@ -0,0 +1,64 @@ +import threading +import VariantValidator +import VariantFormatter.simpleVariantFormatter + + +class ObjectPool: + def __init__(self, pool_size=10): + self.pool_size = pool_size + self.objects = [VariantValidator.Validator() for _ in range(pool_size)] + self.lock = threading.Lock() + + def get_object(self): + with self.lock: + while not self.objects: + # Wait until an object becomes available + self.lock.release() + self.lock.acquire() + return self.objects.pop() + + def return_object(self, obj): + with self.lock: + self.objects.append(obj) + + +class SimpleVariantFormatterPool: + def __init__(self, pool_size=10): + self.pool_size = pool_size + self.pool = [VariantFormatter.simpleVariantFormatter for _ in range(pool_size)] + self.lock = threading.Lock() + + def get(self): + with self.lock: + while not self.pool: + self.lock.release() + self.lock.acquire() + return self.pool.pop() + + def return_object(self, obj): + with self.lock: + self.pool.append(obj) + + +# Create a shared object pools +vval_object_pool = ObjectPool(pool_size=10) +simple_variant_formatter_pool = SimpleVariantFormatterPool(pool_size=10) + +# +# Copyright (C) 2016-2023 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + + diff --git a/rest_cors_test.html b/rest_cors_test.html new file mode 100644 index 0000000..5f50986 --- /dev/null +++ b/rest_cors_test.html @@ -0,0 +1,39 @@ + + + + Access VariantValidator API + + +

Test of VV API for access errors

+

View the output from this page in the console. The console is opened by typing Ctrl-Shift-j.

+ +
+ + + From 575e56089ac95ea2cc58f19cb265104d5ca37246 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 10 Nov 2023 09:07:15 +0000 Subject: [PATCH 120/218] Create connection pool to speed up response times without sharing objects --- rest_VariantValidator/endpoints/variantvalidator_endpoints.py | 3 ++- rest_VariantValidator/utils/object_pool.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 7a5f3f6..32b22cc 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -90,6 +90,7 @@ def get(self, gene_query): content = vval.gene2transcripts(gene_query) except ConnectionError: message = "Cannot connect to rest.genenames.org, please try again later" + vval_object_pool.return_object(vval) raise exceptions.RemoteConnectionError(message) vval_object_pool.return_object(vval) @@ -148,6 +149,7 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build): batch_output=True) except ConnectionError: message = "Cannot connect to rest.genenames.org, please try again later" + vval_object_pool.return_object(vval) raise exceptions.RemoteConnectionError(message) vval_object_pool.return_object(vval) @@ -178,7 +180,6 @@ class Hgvs2referenceClass(Resource): def get(self, hgvs_description): vval = vval_object_pool.get_object() - content = vval.hgvs2ref(hgvs_description) vval_object_pool.return_object(vval) diff --git a/rest_VariantValidator/utils/object_pool.py b/rest_VariantValidator/utils/object_pool.py index 2aae999..1f4bb14 100644 --- a/rest_VariantValidator/utils/object_pool.py +++ b/rest_VariantValidator/utils/object_pool.py @@ -41,8 +41,8 @@ def return_object(self, obj): # Create a shared object pools -vval_object_pool = ObjectPool(pool_size=10) -simple_variant_formatter_pool = SimpleVariantFormatterPool(pool_size=10) +vval_object_pool = ObjectPool(pool_size=2) +simple_variant_formatter_pool = SimpleVariantFormatterPool(pool_size=2) # # Copyright (C) 2016-2023 VariantValidator Contributors From 77eb86e8cbd116e4fa2598a673f73d8f567a4ed2 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 10 Nov 2023 09:51:01 +0000 Subject: [PATCH 121/218] create gene2trans object --- .../endpoints/variantvalidator_endpoints.py | 8 ++++---- rest_VariantValidator/utils/object_pool.py | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 32b22cc..77e71b8 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -1,7 +1,7 @@ # Import modules from flask_restx import Namespace, Resource from rest_VariantValidator.utils import exceptions, request_parser, representations -from rest_VariantValidator.utils.object_pool import vval_object_pool +from rest_VariantValidator.utils.object_pool import vval_object_pool, g2t_object_pool """ Create a parser object locally @@ -84,15 +84,15 @@ class Gene2transcriptsClass(Resource): @api.expect(parser, validate=True) def get(self, gene_query): - vval = vval_object_pool.get_object() + vval = g2t_object_pool_object_pool.get_object() try: content = vval.gene2transcripts(gene_query) except ConnectionError: message = "Cannot connect to rest.genenames.org, please try again later" - vval_object_pool.return_object(vval) + g2t_object_pool.return_object(vval) raise exceptions.RemoteConnectionError(message) - vval_object_pool.return_object(vval) + g2t_object_pool.return_object(vval) # Collect Arguments args = parser.parse_args() diff --git a/rest_VariantValidator/utils/object_pool.py b/rest_VariantValidator/utils/object_pool.py index 1f4bb14..928566a 100644 --- a/rest_VariantValidator/utils/object_pool.py +++ b/rest_VariantValidator/utils/object_pool.py @@ -41,8 +41,9 @@ def return_object(self, obj): # Create a shared object pools -vval_object_pool = ObjectPool(pool_size=2) -simple_variant_formatter_pool = SimpleVariantFormatterPool(pool_size=2) +vval_object_pool = ObjectPool(pool_size=1) +g2t_object_pool = ObjectPool(pool_size=1) +simple_variant_formatter_pool = SimpleVariantFormatterPool(pool_size=1) # # Copyright (C) 2016-2023 VariantValidator Contributors From 8fef048f93114903420b0546b1daaab166ad6868 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 10 Nov 2023 10:53:14 +0000 Subject: [PATCH 122/218] create gene2trans object --- .../endpoints/variantvalidator_endpoints.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 77e71b8..f72c6b1 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -84,7 +84,7 @@ class Gene2transcriptsClass(Resource): @api.expect(parser, validate=True) def get(self, gene_query): - vval = g2t_object_pool_object_pool.get_object() + vval = g2t_object_pool.get_object() try: content = vval.gene2transcripts(gene_query) @@ -137,7 +137,7 @@ class Gene2transcriptsV2Class(Resource): @api.expect(parser, validate=True) def get(self, gene_query, limit_transcripts, transcript_set, genome_build): - vval = vval_object_pool.get_object() + vval = g2t_object_pool.get_object() if genome_build not in ["GRCh37", "GRCh38"]: genome_build = None @@ -149,9 +149,9 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build): batch_output=True) except ConnectionError: message = "Cannot connect to rest.genenames.org, please try again later" - vval_object_pool.return_object(vval) + g2t_object_pool.return_object(vval) raise exceptions.RemoteConnectionError(message) - vval_object_pool.return_object(vval) + g2t_object_pool.return_object(vval) # Collect Arguments args = parser.parse_args() From 8cd65c596e200dd87c7a47bafcb73946a72fc559 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 10 Nov 2023 14:11:59 +0000 Subject: [PATCH 123/218] create gene2trans object --- rest_VariantValidator/utils/object_pool.py | 38 +++++----------------- 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/rest_VariantValidator/utils/object_pool.py b/rest_VariantValidator/utils/object_pool.py index 928566a..41ef265 100644 --- a/rest_VariantValidator/utils/object_pool.py +++ b/rest_VariantValidator/utils/object_pool.py @@ -1,12 +1,11 @@ import threading -import VariantValidator -import VariantFormatter.simpleVariantFormatter - +from VariantValidator import Validator +from VariantFormatter import simpleVariantFormatter class ObjectPool: - def __init__(self, pool_size=10): + def __init__(self, object_type, pool_size=10): self.pool_size = pool_size - self.objects = [VariantValidator.Validator() for _ in range(pool_size)] + self.objects = [object_type() for _ in range(pool_size)] self.lock = threading.Lock() def get_object(self): @@ -21,11 +20,10 @@ def return_object(self, obj): with self.lock: self.objects.append(obj) - class SimpleVariantFormatterPool: def __init__(self, pool_size=10): self.pool_size = pool_size - self.pool = [VariantFormatter.simpleVariantFormatter for _ in range(pool_size)] + self.pool = [simpleVariantFormatter for _ in range(pool_size)] self.lock = threading.Lock() def get(self): @@ -39,27 +37,7 @@ def return_object(self, obj): with self.lock: self.pool.append(obj) - -# Create a shared object pools -vval_object_pool = ObjectPool(pool_size=1) -g2t_object_pool = ObjectPool(pool_size=1) +# Create shared object pools +vval_object_pool = ObjectPool(Validator, pool_size=1) +g2t_object_pool = ObjectPool(Validator, pool_size=1) simple_variant_formatter_pool = SimpleVariantFormatterPool(pool_size=1) - -# -# Copyright (C) 2016-2023 VariantValidator Contributors -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program 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 Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# - - From 406562889b3583a4b4d1ddb7c7658987b81efbf7 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 10 Nov 2023 14:13:37 +0000 Subject: [PATCH 124/218] create gene2trans object --- rest_VariantValidator/utils/object_pool.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rest_VariantValidator/utils/object_pool.py b/rest_VariantValidator/utils/object_pool.py index 41ef265..c72992c 100644 --- a/rest_VariantValidator/utils/object_pool.py +++ b/rest_VariantValidator/utils/object_pool.py @@ -38,6 +38,6 @@ def return_object(self, obj): self.pool.append(obj) # Create shared object pools -vval_object_pool = ObjectPool(Validator, pool_size=1) -g2t_object_pool = ObjectPool(Validator, pool_size=1) -simple_variant_formatter_pool = SimpleVariantFormatterPool(pool_size=1) +vval_object_pool = ObjectPool(Validator, pool_size=2) +g2t_object_pool = ObjectPool(Validator, pool_size=2) +simple_variant_formatter_pool = SimpleVariantFormatterPool(pool_size=2) From 5c75fc214d380c314580c7979a6ba04d97284bf8 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 10 Nov 2023 14:27:32 +0000 Subject: [PATCH 125/218] update object pool --- rest_VariantValidator/utils/object_pool.py | 42 ++++++++++++++++------ 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/rest_VariantValidator/utils/object_pool.py b/rest_VariantValidator/utils/object_pool.py index c72992c..6e09193 100644 --- a/rest_VariantValidator/utils/object_pool.py +++ b/rest_VariantValidator/utils/object_pool.py @@ -7,37 +7,57 @@ def __init__(self, object_type, pool_size=10): self.pool_size = pool_size self.objects = [object_type() for _ in range(pool_size)] self.lock = threading.Lock() + self.condition = threading.Condition(self.lock) def get_object(self): - with self.lock: + with self.condition: while not self.objects: # Wait until an object becomes available - self.lock.release() - self.lock.acquire() + self.condition.wait() return self.objects.pop() def return_object(self, obj): - with self.lock: + with self.condition: self.objects.append(obj) + self.condition.notify() # Notify waiting threads that an object is available class SimpleVariantFormatterPool: def __init__(self, pool_size=10): self.pool_size = pool_size self.pool = [simpleVariantFormatter for _ in range(pool_size)] self.lock = threading.Lock() + self.condition = threading.Condition(self.lock) def get(self): - with self.lock: + with self.condition: while not self.pool: - self.lock.release() - self.lock.acquire() + # Wait until a formatter becomes available + self.condition.wait() return self.pool.pop() def return_object(self, obj): - with self.lock: + with self.condition: self.pool.append(obj) + self.condition.notify() # Notify waiting threads that a formatter is available # Create shared object pools -vval_object_pool = ObjectPool(Validator, pool_size=2) -g2t_object_pool = ObjectPool(Validator, pool_size=2) -simple_variant_formatter_pool = SimpleVariantFormatterPool(pool_size=2) +vval_object_pool = ObjectPool(Validator, pool_size=1) +g2t_object_pool = ObjectPool(Validator, pool_size=1) +simple_variant_formatter_pool = SimpleVariantFormatterPool(pool_size=1) + +# +# Copyright (C) 2016-2023 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# From 9d3fab395fce902282e469b5dfaa6c419bee170c Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 10 Nov 2023 14:46:50 +0000 Subject: [PATCH 126/218] update object pool --- .../endpoints/lovd_endpoints.py | 13 +++-- .../endpoints/variantformatter_endpoints.py | 12 +++-- .../endpoints/variantvalidator_endpoints.py | 51 ++++++++++--------- 3 files changed, 45 insertions(+), 31 deletions(-) diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index 7566226..d156b85 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -22,6 +22,8 @@ def ordereddict_to_dict(value): parser = request_parser.parser api = Namespace('LOVD', description='LOVD API Endpoints') + + @api.route("/lovd////" "//") @api.param("variant_description", "***Genomic HGVS***\n" @@ -79,9 +81,14 @@ def get(self, genome_build, variant_description, transcript_model, select_transc liftover = False simple_formatter = simple_variant_formatter_pool.get() - content = simple_formatter.format(variant_description, genome_build, transcript_model, - select_transcripts, checkonly, liftover) - simple_variant_formatter_pool.return_object(simple_formatter) + try: + content = simple_formatter.format(variant_description, genome_build, transcript_model, + select_transcripts, checkonly, liftover) + except Exception as e: + # Handle the exception and customize the error response + return {"error": str(e)}, 500 + finally: + simple_variant_formatter_pool.return_object(simple_formatter) to_dict = ordereddict_to_dict(content) content = str(to_dict) diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index 6defe0e..53617e2 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -57,9 +57,14 @@ def get(self, genome_build, variant_description, transcript_model, select_transc checkonly = True simple_formatter = simple_variant_formatter_pool.get() - content = simple_formatter.simpleVariantFormatter.format(variant_description, genome_build, transcript_model, - select_transcripts, checkonly) - simple_variant_formatter_pool.return_object(simple_formatter) + try: + content = simple_formatter.simpleVariantFormatter.format(variant_description, genome_build, transcript_model, + select_transcripts, checkonly) + except Exception as e: + # Handle the exception and customize the error response + return {"error": str(e)}, 500 + finally: + simple_variant_formatter_pool.return_object(simple_formatter) # Collect Arguments args = parser.parse_args() @@ -75,7 +80,6 @@ def get(self, genome_build, variant_description, transcript_model, select_transc # Return the api default output return content - # # Copyright (C) 2016-2023 VariantValidator Contributors # diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index f72c6b1..54d3ee0 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -13,7 +13,7 @@ @api.route("/variantvalidator///") @api.param("select_transcripts", "***Return all possible transcripts***\n" - "> all (at latest version for each transcript)\n" + "> all (at the latest version for each transcript)\n" "> raw (all versions of each transcript)\n" "\n***Return only 'select' transcripts***\n" "> select\n" @@ -51,14 +51,16 @@ class VariantValidatorClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) def get(self, genome_build, variant_description, select_transcripts): - vval = vval_object_pool.get_object() - - # Validate using the VariantValidator Python Library - validate = vval.validate(variant_description, genome_build, select_transcripts) - vval_object_pool.return_object(vval) - - content = validate.format_as_dict(with_meta=True) + try: + # Validate using the VariantValidator Python Library + validate = vval.validate(variant_description, genome_build, select_transcripts) + content = validate.format_as_dict(with_meta=True) + except Exception as e: + # Handle the exception and customize the error response + return {"error": str(e)}, 500 + finally: + vval_object_pool.return_object(vval) # Collect Arguments args = parser.parse_args() @@ -83,16 +85,15 @@ class Gene2transcriptsClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) def get(self, gene_query): - vval = g2t_object_pool.get_object() - try: content = vval.gene2transcripts(gene_query) except ConnectionError: message = "Cannot connect to rest.genenames.org, please try again later" g2t_object_pool.return_object(vval) raise exceptions.RemoteConnectionError(message) - g2t_object_pool.return_object(vval) + finally: + g2t_object_pool.return_object(vval) # Collect Arguments args = parser.parse_args() @@ -111,7 +112,7 @@ def get(self, gene_query): @api.route("/tools/gene2transcripts_v2////" "") -@api.param("gene_query", "***HGNC gene symbol, HGNC ID or transcript ID***\n" +@api.param("gene_query", "***HGNC gene symbol, HGNC ID, or transcript ID***\n" "\nCurrent supported transcript IDs" "\n- RefSeq or Ensembl""\n" "\n***Single***\n" @@ -136,14 +137,12 @@ class Gene2transcriptsV2Class(Resource): # Add documentation about the parser @api.expect(parser, validate=True) def get(self, gene_query, limit_transcripts, transcript_set, genome_build): - vval = g2t_object_pool.get_object() - - if genome_build not in ["GRCh37", "GRCh38"]: - genome_build = None - if "False" in limit_transcripts or "false" in limit_transcripts or limit_transcripts is False: - limit_transcripts = None try: + if genome_build not in ["GRCh37", "GRCh38"]: + genome_build = None + if "False" in limit_transcripts or "false" in limit_transcripts or limit_transcripts is False: + limit_transcripts = None content = vval.gene2transcripts(gene_query, select_transcripts=limit_transcripts, transcript_set=transcript_set, genome_build=genome_build, batch_output=True) @@ -151,7 +150,8 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build): message = "Cannot connect to rest.genenames.org, please try again later" g2t_object_pool.return_object(vval) raise exceptions.RemoteConnectionError(message) - g2t_object_pool.return_object(vval) + finally: + g2t_object_pool.return_object(vval) # Collect Arguments args = parser.parse_args() @@ -171,17 +171,20 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build): @api.route("/tools/hgvs2reference/") @api.param("hgvs_description", "***hgvs_description***\n" "\nSequence variation description in the HGVS format\n" - "\n *Intronic descriptions in the context of transcript reference sequences are currentl" - "y " + "\n *Intronic descriptions in the context of transcript reference sequences are currently " "unsupported*") class Hgvs2referenceClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) def get(self, hgvs_description): - vval = vval_object_pool.get_object() - content = vval.hgvs2ref(hgvs_description) - vval_object_pool.return_object(vval) + try: + content = vval.hgvs2ref(hgvs_description) + except Exception as e: + # Handle the exception and customize the error response + return {"error": str(e)}, 500 + finally: + vval_object_pool.return_object(vval) # Collect Arguments args = parser.parse_args() From be9f71676685f4bf638a94f9af9f6fccfd040c2c Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 10 Nov 2023 15:39:10 +0000 Subject: [PATCH 127/218] create gene2trans object to have max pool size and pool replenishing --- rest_VariantValidator/utils/object_pool.py | 33 +++++++++++++--------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/rest_VariantValidator/utils/object_pool.py b/rest_VariantValidator/utils/object_pool.py index 6e09193..63d17b1 100644 --- a/rest_VariantValidator/utils/object_pool.py +++ b/rest_VariantValidator/utils/object_pool.py @@ -2,10 +2,12 @@ from VariantValidator import Validator from VariantFormatter import simpleVariantFormatter + class ObjectPool: - def __init__(self, object_type, pool_size=10): - self.pool_size = pool_size - self.objects = [object_type() for _ in range(pool_size)] + def __init__(self, object_type, initial_pool_size=10, max_pool_size=10): + self.pool_size = initial_pool_size + self.max_pool_size = max_pool_size + self.objects = [object_type() for _ in range(initial_pool_size)] self.lock = threading.Lock() self.condition = threading.Condition(self.lock) @@ -18,13 +20,16 @@ def get_object(self): def return_object(self, obj): with self.condition: - self.objects.append(obj) - self.condition.notify() # Notify waiting threads that an object is available + if len(self.objects) < self.max_pool_size: + self.objects.append(obj) + self.condition.notify() # Notify waiting threads that an object is available + class SimpleVariantFormatterPool: - def __init__(self, pool_size=10): - self.pool_size = pool_size - self.pool = [simpleVariantFormatter for _ in range(pool_size)] + def __init__(self, initial_pool_size=10, max_pool_size=10): + self.pool_size = initial_pool_size + self.max_pool_size = max_pool_size + self.pool = [simpleVariantFormatter for _ in range(initial_pool_size)] self.lock = threading.Lock() self.condition = threading.Condition(self.lock) @@ -37,13 +42,15 @@ def get(self): def return_object(self, obj): with self.condition: - self.pool.append(obj) - self.condition.notify() # Notify waiting threads that a formatter is available + if len(self.pool) < self.max_pool_size: + self.pool.append(obj) + self.condition.notify() # Notify waiting threads that a formatter is available + # Create shared object pools -vval_object_pool = ObjectPool(Validator, pool_size=1) -g2t_object_pool = ObjectPool(Validator, pool_size=1) -simple_variant_formatter_pool = SimpleVariantFormatterPool(pool_size=1) +vval_object_pool = ObjectPool(Validator, initial_pool_size=2, max_pool_size=3) +g2t_object_pool = ObjectPool(Validator, initial_pool_size=2, max_pool_size=3) +simple_variant_formatter_pool = SimpleVariantFormatterPool(initial_pool_size=2, max_pool_size=3) # # Copyright (C) 2016-2023 VariantValidator Contributors From bfb22a6a65af4352eed80d4b45df46da6475e176 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 10 Nov 2023 18:43:04 +0000 Subject: [PATCH 128/218] update locust and correct VF endpoint error --- locust/locust.py | 62 ++++++------------- locust/test_set.py | 27 ++++++++ .../endpoints/variantformatter_endpoints.py | 4 +- 3 files changed, 47 insertions(+), 46 deletions(-) diff --git a/locust/locust.py b/locust/locust.py index fe7635a..3332804 100644 --- a/locust/locust.py +++ b/locust/locust.py @@ -1,57 +1,31 @@ from locust import HttpUser, TaskSet, task, between import test_set -""" -Run locust against local dev server: -Activate the API in dev mode in a terminal - -$ python wsgi.py - -Run locust in a second terminal - -$ locust -f locust/locust.py - -Perform test at http://127.0.0.1:8089 (docs at https://docs.locust.io/en/latest/quickstart.html) - - -# Run locust against app on a server - -$ locust -f locust/locust.py - -Perform test at http://127.0.0.1:8089 (docs at https://docs.locust.io/en/latest/quickstart.html) - -""" - - -class UserBehavior(HttpUser): - """ - Endpoint load-testing for the VariantValidator REST API - - Current tasks: - @task(1) tests the variantvalidator endpoints with a randonly selected variant from a list - @task(2) tests the gene2transcript endpoints with a randonly selected gene symbol from a list - - Note: These are the two most heavily used tools, but more tests will be added - """ +class UserBehavior(TaskSet): @task(2) - def index(self): - random_task = test_set.gene_list() - url = "VariantValidator/tools/gene2transcripts/%s?content-type=application/json" % random_task + def gene2transcripts_v2_task(self): + gene_symbol = test_set.gene_list() + url = f"https://rest.variantvalidator.org/VariantValidator/tools/gene2transcripts_v2/{gene_symbol}/mane_select/all/GRCh38?content-type=application%2Fjson" self.client.get(url) @task(1) - def profile(self): - odd_job = test_set.variant_list() - # Make a request to the current VariantValidator rest-API - url = '/'.join(['VariantValidator/variantvalidator', - 'GRCh37', - odd_job, - 'all?content-type=application/json' - ]) + def variantvalidator_task(self): + variant_id = test_set.variant_list() + url = f"/VariantValidator/variantvalidator/GRCh37/{variant_id}/all?content-type=application/json" + self.client.get(url) + # Simulate a long response time (5 minutes) + self.wait(300) + + @task(3) + def additional_task(self): + odd_task = test_set.vf_list() + url = f"https://rest.variantvalidator.org/LOVD/lovd/GRCh38/{odd_task}/all/all/False/True?content-type=application%2Fjson" self.client.get(url) + # Simulate a long response time (5 minutes) + self.wait(300) class WebsiteUser(HttpUser): - task_set = UserBehavior + tasks = [UserBehavior] wait_time = between(1, 10) # seconds diff --git a/locust/test_set.py b/locust/test_set.py index 42a257b..a5553f2 100644 --- a/locust/test_set.py +++ b/locust/test_set.py @@ -343,3 +343,30 @@ def gene_list(): random_number = random.randint(0, 6) random_task = tests[random_number] return random_task + + +def vf_list(): + tests = [ + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NC_000016.9:g.2099572TC>T", + "NM_000088.3:c.589GG>CT", + "NC_000005.9:g.35058667_35058668AG=", + "NC_000005.9:g.35058667_35058668AG=", + "NC_000005.9:g.35058667_35058668AG=", + "NC_000005.9:g.35058667_35058668AG=", + "NC_000005.9:g.35058667_35058668AG=", + "NC_000005.9:g.35058667_35058668AG=", + "NC_000005.9:g.35058667_35058668AG=", + "NC_000005.9:g.35058667_35058668AG=" + ] + random_number = random.randint(0, 319) + odd_task = tests[random_number] + return odd_task diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index 53617e2..03fc6b6 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -58,8 +58,8 @@ def get(self, genome_build, variant_description, transcript_model, select_transc simple_formatter = simple_variant_formatter_pool.get() try: - content = simple_formatter.simpleVariantFormatter.format(variant_description, genome_build, transcript_model, - select_transcripts, checkonly) + content = simple_formatter.format(variant_description, genome_build, transcript_model, + select_transcripts, checkonly) except Exception as e: # Handle the exception and customize the error response return {"error": str(e)}, 500 From 60160eeb687443ac4d119f9d686488c7c4f4bc28 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 13 Nov 2023 20:04:45 +0000 Subject: [PATCH 129/218] update locust and correct VF endpoint error --- Dockerfile | 3 --- locust/locust.py | 12 +++--------- rest_VariantValidator/utils/object_pool.py | 6 +++--- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2115e13..8349900 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,9 +3,6 @@ FROM python:3.10 # Set the working directory to /app WORKDIR /app -# Copy the current directory contents into the container's /app directory -COPY . /app - # Create logging directory RUN mkdir /usr/local/share/logs diff --git a/locust/locust.py b/locust/locust.py index 3332804..d5119e3 100644 --- a/locust/locust.py +++ b/locust/locust.py @@ -1,30 +1,24 @@ from locust import HttpUser, TaskSet, task, between import test_set - class UserBehavior(TaskSet): - @task(2) + @task(2, max_wait=300) def gene2transcripts_v2_task(self): gene_symbol = test_set.gene_list() url = f"https://rest.variantvalidator.org/VariantValidator/tools/gene2transcripts_v2/{gene_symbol}/mane_select/all/GRCh38?content-type=application%2Fjson" self.client.get(url) - @task(1) + @task(1, max_wait=300) def variantvalidator_task(self): variant_id = test_set.variant_list() url = f"/VariantValidator/variantvalidator/GRCh37/{variant_id}/all?content-type=application/json" self.client.get(url) - # Simulate a long response time (5 minutes) - self.wait(300) - @task(3) + @task(3, max_wait=300) def additional_task(self): odd_task = test_set.vf_list() url = f"https://rest.variantvalidator.org/LOVD/lovd/GRCh38/{odd_task}/all/all/False/True?content-type=application%2Fjson" self.client.get(url) - # Simulate a long response time (5 minutes) - self.wait(300) - class WebsiteUser(HttpUser): tasks = [UserBehavior] diff --git a/rest_VariantValidator/utils/object_pool.py b/rest_VariantValidator/utils/object_pool.py index 63d17b1..601f20e 100644 --- a/rest_VariantValidator/utils/object_pool.py +++ b/rest_VariantValidator/utils/object_pool.py @@ -48,9 +48,9 @@ def return_object(self, obj): # Create shared object pools -vval_object_pool = ObjectPool(Validator, initial_pool_size=2, max_pool_size=3) -g2t_object_pool = ObjectPool(Validator, initial_pool_size=2, max_pool_size=3) -simple_variant_formatter_pool = SimpleVariantFormatterPool(initial_pool_size=2, max_pool_size=3) +vval_object_pool = ObjectPool(Validator, initial_pool_size=3, max_pool_size=5) +g2t_object_pool = ObjectPool(Validator, initial_pool_size=3, max_pool_size=5) +simple_variant_formatter_pool = SimpleVariantFormatterPool(initial_pool_size=3, max_pool_size=5) # # Copyright (C) 2016-2023 VariantValidator Contributors From eb98e47b5c79c568801d560276dab7d1a3c024a1 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 15 Nov 2023 12:55:45 +0000 Subject: [PATCH 130/218] Add develpment mode into the docker build and update the manual --- Dockerfile | 7 +- configuration/docker.ini | 2 +- db_dockerfiles/seqrepo/Dockerfile | 8 +- docker-compose-dev.yml | 46 ++++++++++ docker-compose.yml | 12 +-- docs/DOCKER.md | 135 ++++++++++++++---------------- 6 files changed, 124 insertions(+), 86 deletions(-) create mode 100644 docker-compose-dev.yml diff --git a/Dockerfile b/Dockerfile index 8349900..bb82ee4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,11 @@ -FROM python:3.10 +FROM python:3.11 # Set the working directory to /app WORKDIR /app +# Copy the current directory contents into the container's /app directory +COPY . /app + # Create logging directory RUN mkdir /usr/local/share/logs @@ -21,7 +24,7 @@ RUN git config http.postBuffer 500000000 RUN pip install --upgrade pip # Install the tool -RUN pip install . +RUN pip install -e . # Copy the config file into the container home directory COPY configuration/docker.ini /root/.variantvalidator diff --git a/configuration/docker.ini b/configuration/docker.ini index b95f6e5..ed6a703 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -8,7 +8,7 @@ version = vvdb_2023_8 [seqrepo] version = VV_SR_2023_05/master -location = /usr/local/share/seqrepo +location = /usr/local/share/seqdata require_threading = True [postgres] diff --git a/db_dockerfiles/seqrepo/Dockerfile b/db_dockerfiles/seqrepo/Dockerfile index 4679253..b287a49 100644 --- a/db_dockerfiles/seqrepo/Dockerfile +++ b/db_dockerfiles/seqrepo/Dockerfile @@ -4,13 +4,13 @@ RUN apt-get update RUN apt-get install -y wget -RUN mkdir -p /usr/local/share/seqrepo +RUN mkdir -p /usr/local/share/seqdata -RUN wget --output-document=/usr/local/share/seqrepo/VV_SR_2023_05.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2023_05.tar +RUN wget --output-document=/usr/local/share/seqdata/VV_SR_2023_05.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2023_05.tar -RUN tar -xvf /usr/local/share/seqrepo/VV_SR_2023_05.tar --directory /usr/local/share/seqrepo +RUN tar -xvf /usr/local/share/seqdata/VV_SR_2023_05.tar --directory /usr/local/share/seqdata -RUN rm /usr/local/share/seqrepo/VV_SR_2023_05.tar +RUN rm /usr/local/share/seqdata/VV_SR_2023_05.tar ENTRYPOINT [] diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml new file mode 100644 index 0000000..9bdcaeb --- /dev/null +++ b/docker-compose-dev.yml @@ -0,0 +1,46 @@ +version: '3' + +services: + dev-mode: + extends: + file: docker-compose.yml + service: rest-variantvalidator + depends_on: + - rv-vdb + - rv-vvta + - rv-seqrepo + volumes: + - rest_vv_dev_volume:/app + - vv-logs:/usr/local/share/logs # Mount volume for logs + - seqdata:/usr/local/share/seqdata # Mount volume for sequence data + ports: + - "5000:5000" + - "5050:5050" + - "8000:8000" + - "9000:9000" + expose: + - "5000" # Expose ports for external access + - "5050" + - "8000" + - "8080" + +volumes: + rest_vv_dev_volume: + driver: local + driver_opts: + type: 'none' + o: 'bind' + device: '${PWD}' + seqdata: + driver: local + driver_opts: + type: 'none' + o: 'bind' + device: '${HOME}/variantvalidator_data/seqdata' + vv-logs: + driver: local + driver_opts: + type: 'none' + o: 'bind' + device: '${HOME}/variantvalidator_data/logs' + diff --git a/docker-compose.yml b/docker-compose.yml index 9ac8a28..8977ebe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,7 +25,7 @@ services: context: . dockerfile: db_dockerfiles/seqrepo/Dockerfile volumes: - - seqdata:/usr/local/share/seqrepo # Mount volume for sequence data + - seqdata:/usr/local/share/seqdata # Mount volume for sequence data rest-variantvalidator: build: . @@ -38,8 +38,7 @@ services: - rv-seqrepo volumes: - vv-logs:/usr/local/share/logs # Mount volume for logs - - seqdata:/usr/local/share/seqrepo # Mount volume for sequence data - - rest-vv-app:/app/rest_VariantValidator # Mount the app directory to your system + - seqdata:/usr/local/share/seqdata # Mount volume for sequence data ports: - "5000:5000" - "5050:5050" @@ -64,9 +63,4 @@ volumes: type: 'none' o: 'bind' device: '${HOME}/variantvalidator_data/logs' - rest-vv-app: - driver: local - driver_opts: - type: 'none' - o: 'bind' - device: ${PWD}/rest_VariantValidator + diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 3dbde72..ed96eed 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -50,55 +50,93 @@ $ docker-compose pull - Create a directory for sharing resources between your computer and the container ```bash -$ mkdir -p ~/variantvalidator_data/seqdata -$ mkdir -p ~/variantvalidator_data/logs +$ mkdir -p ~/variantvalidator_data/seqdata && mkdir -p ~/variantvalidator_data/logs ``` -*i.e.,* a directory called `variantvalidator_data/seqdata` in your `home` directory +*i.e.,* a directory called `variantvalidator_data` in your `home` directory with sub-directories `seqdata` and `logs` -- Build +#### Build and startup procedure +rest_VariantValidator can be built in Production mode and Development mode. Development mode mounts the root directory +of the host git Repository to the equivalent project directory in the docker container. This means that changes to the +code on the host machine are mapped into the container allowing on-the-fly development. +Choose one of the following commands to build and start the rest_VariantValidaor containers + +- Production build ```bash -$ docker-compose build --no-cache +# Build +$ docker-compose build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator +# Run +$ docker-compose up -d ``` - -- Complete build - - The first time you do this, it will complete the build process, for example, populating the required the databases - - The build takes a while because the vv databases are large. However, this is a significant improvement on previou - s versions. Build time is ~30 minutes (depending on the speed of you computer and internet connection) - - The build has completed when you see - +- Development and testing build +```bash +# Build +$ docker-compose -f docker-compose.yml -f docker-compose-dev.yml build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator +# Run +$ docker-compose -f docker-compose.yml -f docker-compose-dev.yml up -d ``` -Creating rest_variantvalidator_rv-vvta_1 ... done -Creating rest_variantvalidator_rv-vdb_1 ... done -Creating rest_variantvalidator_rv-seqrepo_1 ... done -Creating rest_variantvalidator_rest-variantvalidator_1 ... done +- The build stage has completed when you see +``` + => [rest-variantvalidator 10/10] COPY configuration/docker.ini /root/.variantvalidator 0.0s + => [rest-variantvalidator] exporting to image 2.3s + => => exporting layers 2.3s + => => writing image sha256:097829685d99c7b308563dbee52009bc3dd7d79e85e195d454f3bf602afd5d95 0.0s + => => naming to docker.io/library/rest_variantvalidator-rest-variantvalidator ``` -Use this command to complete the build and wait for the above messages +- Use this command to complete the build and wait for the above messages ```bash $ docker-compose up -d rv-vvta && \ docker-compose up -d rv-vdb && \ docker-compose up -d rv-seqrepo && \ docker-compose up -d rest-variantvalidator ``` +- Or for a development and testing build, swap for this command +```bash +$ docker-compose up -d rv-vvta && \ + docker-compose up -d rv-vdb && \ + docker-compose up -d rv-seqrepo && \ + docker-compose -f docker-compose.yml -f docker-compose-dev.yml up -d dev-mode +``` + +- The containers are started and running when you see +```bash +Creating rest_variantvalidator_rv-vvta_1 ... done +Creating rest_variantvalidator_rv-vdb_1 ... done +Creating rest_variantvalidator_rv-seqrepo_1 ... done +Creating rest_variantvalidator_rest-variantvalidator_1 ... done +``` ### Test the build ```bash # Run PyTest (all tests should pass) -$ docker exec rest_variantvalidator_rest-variantvalidator_1 pytest +$ docker exec rest_variantvalidator-rest-variantvalidator-1 pytest +``` +Note: Different host Operating Systems name the container using slightly different conventions e.g. underscores instead +of hyphens. To find your container name run the command + +```bash +$ docker ps +******************************************************************************************************************************************************************************************************************************************************************************* +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +75078f429d72 rest_variantvalidator-rest-variantvalidator "/bin/bash -c 'sleep…" 41 seconds ago Up 39 seconds 0.0.0.0:5000->5000/tcp, 0.0.0.0:5050->5050/tcp, 0.0.0.0:8000->8000/tcp, 0.0.0.0:9000->9000/tcp, 8080/tcp rest_variantvalidator-rest-variantvalidator-1 +******************************************************************************************************************************************************************************************************************************************************************************* ``` +***Note: In Development and testing builds, the container name will be e.g. rest_variantvalidator-dev-mode-1*** # Run the server ```bash # Start the container in detached mode -$ docker exec -it rest_variantvalidator_rest-variantvalidator_1 gunicorn -b 0.0.0.0:8000 --timeout 600 wsgi:app --threads=5 --chdir ./rest_VariantValidator/ +$ docker exec -it rest_variantvalidator-rest-variantvalidator-1 gunicorn -b 0.0.0.0:8000 --timeout 600 wsgi:app --threads=5 --chdir ./rest_VariantValidator/ ``` Optional: If your docker instance has multiple available cores, you can increase processing power by starting multiple workers e.g. ```bash -docker exec -it rest_variantvalidator_rest-variantvalidator_1 gunicorn -b 0.0.0.0:8000 --workers 3 --timeout 600 wsgi:app --threads=5 --chdir ./rest_VariantValidator/ +docker exec -it rest_variantvalidator-rest-variantvalidator-1 gunicorn -b 0.0.0.0:8000 --workers 3 --timeout 600 wsgi:app --threads=5 --chdir ./rest_VariantValidator/ ``` +***Note: In Development and testing builds, the container name will be e.g. rest_variantvalidator-dev-mode-1*** + In a web browser navigate to [http://0.0.0.0:8000](http://0.0.0.0:8000) @@ -207,56 +245,15 @@ $ docker exec -it rest_variantvalidator-rest-variantvalidator-1 python bin/varia ``` ## Developing VariantValidator in Docker -The container has been configured with git installed. This means that you can clone Repos directly into the container - -To develop VariantValidator in the container +Create the development and testing build and changes you make in the cloned Repo should map into the container -Start the container in detached mode - -```bash -$ docker-compose exec rest_variantvalidator-rest-variantvalidator-1 bash -``` - -ON YOUR COMPUTER change into the share directory - -```bash -$ cd ~/share -``` - -Then create a directory for development - -```bash -$ mkdir DevelopmentRepos -$ cd ~/share/DevelopmentRepos -``` - -Clone the VariantValidator Repo - -```bash -$ git clone https://github.com/openvar/variantValidator.git -``` - -Checkout the develop branch - -```bash -$ git checkout develop -$ git pull -``` - -Create an new branch for your developments +Create a new branch for your developments ```bash $ git branch name_of_branch $ git checkout name_of_branch ``` -IN THE CONTAINER, pip install the code so it can be run by the container - -```bash -$ cd /usr/local/share/DevelopmentRepos/variantValidator -$ pip install -e . -``` - You can then use the containers Python interpreter to run queries, e.g. ```python @@ -305,19 +302,17 @@ python rest_variantValidator/app.py To update a container, use ```bash -$ docker-compose down -$ docker-compose build --no-cache -$ docker-compose up +$ docker-compose build --build ``` +where is a service listed in the docker-compose.yml -## Deleting rest_variantValidator -Update requires that the restvv container is deleted from your system. This is not achieved by removing the container +Once re-built, start all containers as normal -If you are only running rest_variantValidator in docker, we recommend deleting and re-building all containers +## Deleting rest_variantValidator ```bash # Remove the specific containers -$ docker-compose rm +$ docker-compose rm # OR Delete all containers on your system $ docker-compose down From a6fc86caebf7f2d54d955574268a088ba62fb2df Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 15 Nov 2023 13:54:46 +0000 Subject: [PATCH 131/218] Add badging for codecov --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f5e8e19..6febf49 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,13 @@ -# About rest_variantValidator +# rest_VariantValidator + +## Status +[![codecov](https://codecov.io/gh/openvar/rest_variantValidator/graph/badge.svg?token=DE92ZVZT3F)](https://codecov.io/gh/openvar/rest_variantValidator) + +## About rest_variantValidator rest_variantValidator is a rest web interface for VariantValidator -# About VariantValidator +## About VariantValidator VariantValidator is a user-friendly software tool designed to validate the syntax and parameters of DNA variant descriptions according to the HGVS Sequence Variant From 3462a5c71033072aeb149bdbbb83081195517748 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 15 Nov 2023 14:45:38 +0000 Subject: [PATCH 132/218] Update locust file --- locust/locust.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/locust/locust.py b/locust/locust.py index d5119e3..6a813b6 100644 --- a/locust/locust.py +++ b/locust/locust.py @@ -1,6 +1,7 @@ from locust import HttpUser, TaskSet, task, between import test_set + class UserBehavior(TaskSet): @task(2, max_wait=300) def gene2transcripts_v2_task(self): @@ -20,6 +21,24 @@ def additional_task(self): url = f"https://rest.variantvalidator.org/LOVD/lovd/GRCh38/{odd_task}/all/all/False/True?content-type=application%2Fjson" self.client.get(url) + class WebsiteUser(HttpUser): tasks = [UserBehavior] wait_time = between(1, 10) # seconds + +# +# Copyright (C) 2016-2023 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# From 456d26ec7f2d376a73197ec59d7516d2624759c8 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 15 Nov 2023 14:55:19 +0000 Subject: [PATCH 133/218] Add wsgi test --- tests/test_wsgi.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/test_wsgi.py diff --git a/tests/test_wsgi.py b/tests/test_wsgi.py new file mode 100644 index 0000000..542b840 --- /dev/null +++ b/tests/test_wsgi.py @@ -0,0 +1,18 @@ +# Import necessary packages +import pytest +from rest_VariantValidator.wsgi import application # Import your Flask app + + +# Fixture to set up the test client +@pytest.fixture(scope='module') +def client(): + application.testing = True + return application.test_client() # Create a test client to interact with the app + + +# Test function for the /hello/ endpoint +def test_hello_endpoint(client): + response = client.get('/hello/') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK + assert response.json["status"] == "hello_world" # Check the JSON response content + assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response From ed3ce1bf95d7baf357f56d2fb91008b8addafa83 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 16 Nov 2023 15:18:08 +0000 Subject: [PATCH 134/218] Increase MySQL docker connections --- db_dockerfiles/vdb/Dockerfile | 5 ++ docs/DOCKER.md | 4 -- rest_VariantValidator/app.py | 4 +- rest_VariantValidator/wsgi.py | 53 +++++----------- tests/test_endpoints.py | 1 + tests/test_internal_servers.py | 65 ++++++++++++++++++++ tests/{test_wsgi.py => test_wsgi_gateway.py} | 10 ++- wsgi.py | 22 ++++--- 8 files changed, 106 insertions(+), 58 deletions(-) create mode 100644 tests/test_internal_servers.py rename tests/{test_wsgi.py => test_wsgi_gateway.py} (61%) diff --git a/db_dockerfiles/vdb/Dockerfile b/db_dockerfiles/vdb/Dockerfile index 148eeb7..e916638 100644 --- a/db_dockerfiles/vdb/Dockerfile +++ b/db_dockerfiles/vdb/Dockerfile @@ -11,5 +11,10 @@ RUN apt-get update && apt-get install -y \ RUN rm -rf /var/lib/apt/lists/* +# Set the max_connections directly in the my.cnf +RUN echo '[mysqld]' >> /etc/mysql/my.cnf && \ + echo 'max_connections=250' >> /etc/mysql/my.cnf + RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2023_08.sql.gz -O /docker-entrypoint-initdb.d/validator_2023_08.sql.gz +CMD ["mysqld"] diff --git a/docs/DOCKER.md b/docs/DOCKER.md index ed96eed..22664d1 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -65,15 +65,11 @@ Choose one of the following commands to build and start the rest_VariantValidaor ```bash # Build $ docker-compose build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator -# Run -$ docker-compose up -d ``` - Development and testing build ```bash # Build $ docker-compose -f docker-compose.yml -f docker-compose-dev.yml build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator -# Run -$ docker-compose -f docker-compose.yml -f docker-compose-dev.yml up -d ``` - The build stage has completed when you see ``` diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 5bb7425..0a558cc 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -168,8 +168,10 @@ def default_error_handler(e): # Allows app to be run in debug mode if __name__ == '__main__': + # Read the port from the environment variable, defaulting to 8000 if not set + port = int(os.environ.get('PORT', 5000)) application.debug = True # Enable debugging mode - application.run(host="127.0.0.1", port=5000) # Specify a host and port fot the app + application.run(host="127.0.0.1", port=port) # Specify a host and port fot the app # diff --git a/rest_VariantValidator/wsgi.py b/rest_VariantValidator/wsgi.py index 2caafcd..60d7931 100644 --- a/rest_VariantValidator/wsgi.py +++ b/rest_VariantValidator/wsgi.py @@ -1,43 +1,22 @@ """ Gunicorn wsgi gateway file """ -from rest_VariantValidator.app import application +import os +from rest_VariantValidator.app import application as app +from configparser import ConfigParser +from VariantValidator.settings import CONFIG_DIR -if __name__ == '__main__': - from configparser import ConfigParser - from VariantValidator.settings import CONFIG_DIR - - config = ConfigParser() - config.read(CONFIG_DIR) - - if config["logging"]["log"] == "True": - application.debug = True - application.config['PROPAGATE_EXCEPTIONS'] = True - else: - application.debug = False - application.config['PROPAGATE_EXCEPTIONS'] = False - - # Define the Gunicorn entry point - from gunicorn.app.wsgiapp import WSGIApplication +config = ConfigParser() +config.read(CONFIG_DIR) +if config["logging"]["log"] == "True": + app.debug = True + app.config['PROPAGATE_EXCEPTIONS'] = True +else: + app.debug = False + app.config['PROPAGATE_EXCEPTIONS'] = False - class GunicornApp(WSGIApplication): - def init(self, parser, opts, args): - return { - 'bind': '0.0.0.0:8000', - 'workers': 3, - 'timeout': 600, - 'chdir': './rest_VariantValidator/' - } - - - if __name__ == '__main__': - options = { - 'bind': '0.0.0.0:8000', - 'workers': 3, - 'timeout': 600, - 'chdir': './rest_VariantValidator/' - } - GunicornApp().run() - - application.run(host="127.0.0.1", port=8000) +if __name__ == '__main__': + # Read the port from the environment variable, defaulting to 8000 if not set + port = int(os.environ.get('PORT', 8000)) + app.run(host="127.0.0.1", port=port) diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 232d2af..17e0183 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -1,3 +1,4 @@ + # Import necessary packages import pytest from rest_VariantValidator.app import application # Import your Flask app diff --git a/tests/test_internal_servers.py b/tests/test_internal_servers.py new file mode 100644 index 0000000..b0750c5 --- /dev/null +++ b/tests/test_internal_servers.py @@ -0,0 +1,65 @@ +import os +import subprocess +import time +from unittest import TestCase +import requests + +class TestInternalServers(TestCase): + @classmethod + def setUpClass(cls): + # Set the PORT environment variable for WSGI server + os.environ['PORT'] = '8001' + + # Start the WSGI server as a separate process + cls.wsgi_server_process = subprocess.Popen(['python', 'rest_VariantValidator/wsgi.py']) + time.sleep(30) + + # Set the PORT environment variable for APP server + os.environ['PORT'] = '5001' + + # Start the app server as a separate process + cls.app_server_process = subprocess.Popen(['python', 'rest_VariantValidator/app.py']) + time.sleep(30) + + @classmethod + def tearDownClass(cls): + # Terminate the WSGI server process + cls.wsgi_server_process.terminate() + cls.wsgi_server_process.wait(timeout=30) + wsgi_exit_code = cls.wsgi_server_process.poll() + print(f"WSGI Server Exit Code: {wsgi_exit_code}") + + # Terminate the app server process + cls.app_server_process.terminate() + cls.app_server_process.wait(timeout=30) + app_exit_code = cls.app_server_process.poll() + print(f"App Server Exit Code: {app_exit_code}") + + if wsgi_exit_code != 0: + wsgi_pid = cls.wsgi_server_process.pid + # Forcefully kill all processes listening on port 8001 + subprocess.run(['pkill', '-f', f':{wsgi_pid}']) + assert wsgi_exit_code == 0, f"WSGI Server termination failed with exit code {wsgi_exit_code}" + else: + assert wsgi_exit_code == 0, f"WSGI Server termination failed with exit code {wsgi_exit_code}" + if app_exit_code != 0: + app_pid = cls.app_server_process.pid # Fix this line + # Forcefully kill all processes listening on port 5001 + subprocess.run(['pkill', '-f', f':{app_pid}']) + assert app_exit_code == 0, f"App Server termination failed with exit code {app_exit_code}" + else: + assert app_exit_code == 0, f"App Server termination failed with exit code {app_exit_code}" + + def check_server(self, endpoint, port): + # http://127.0.0.1:8000/hello/?content-type=application%2Fjson + response = requests.get(f'http://127.0.0.1:{port}/{endpoint}/?content-type=application%2Fjson') + assert response.status_code == 200 + assert "status" in response.json().keys() + + def test_wsgi_internal_server(self): + # Check the WSGI server + self.check_server('hello', 8001) + + def test_app_server(self): + # Check the app server + self.check_server('hello', 5001) diff --git a/tests/test_wsgi.py b/tests/test_wsgi_gateway.py similarity index 61% rename from tests/test_wsgi.py rename to tests/test_wsgi_gateway.py index 542b840..2efbb49 100644 --- a/tests/test_wsgi.py +++ b/tests/test_wsgi_gateway.py @@ -1,17 +1,15 @@ -# Import necessary packages +from rest_VariantValidator.wsgi import app import pytest -from rest_VariantValidator.wsgi import application # Import your Flask app -# Fixture to set up the test client @pytest.fixture(scope='module') def client(): - application.testing = True - return application.test_client() # Create a test client to interact with the app + app.testing = True + return app.test_client() # Create a test client to interact with the app # Test function for the /hello/ endpoint -def test_hello_endpoint(client): +def test_wsgi_gateway(client): response = client.get('/hello/') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert response.json["status"] == "hello_world" # Check the JSON response content diff --git a/wsgi.py b/wsgi.py index dae525d..6731bae 100644 --- a/wsgi.py +++ b/wsgi.py @@ -3,15 +3,17 @@ """ from rest_VariantValidator.app import application as application +from configparser import ConfigParser +from VariantValidator.settings import CONFIG_DIR + +config = ConfigParser() +config.read(CONFIG_DIR) +if config["logging"]["log"] == "True": + application.debug = True + application.config['PROPAGATE_EXCEPTIONS'] = True +else: + application.debug = False + application.config['PROPAGATE_EXCEPTIONS'] = False + if __name__ == '__main__': - from configparser import ConfigParser - from VariantValidator.settings import CONFIG_DIR - config = ConfigParser() - config.read(CONFIG_DIR) - if config["logging"]["log"] == "True": - application.debug = True - application.config['PROPAGATE_EXCEPTIONS'] = True - else: - application.debug = False - application.config['PROPAGATE_EXCEPTIONS'] = False application.run(host="127.0.0.1", port=8000) From 6017ce6eae35d6f69281d17f7d32fe0e29fb14c8 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 24 Nov 2023 12:20:35 +0000 Subject: [PATCH 135/218] Start building CI --- Jenkinsfile | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 Jenkinsfile diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..517b508 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,85 @@ +pipeline { + agent { + docker { + image 'docker/compose:1.27.4' + args '-v /var/run/docker.sock:/var/run/docker.sock' + } + } + + environment { + CODECOV_TOKEN = credentials('CODECOV_TOKEN_rest_variantvalidator') + CONTAINER_SUFFIX = "${BUILD_NUMBER}" + DOCKER_NETWORK = "rest-variantvalidator_docker_network-${CONTAINER_SUFFIX}" + DATA_VOLUME = "docker-shared-space" + } + + stages { + stage("Clone Repository Remove dangling docker components and Create Docker Network") { + steps { + checkout scm + sh 'docker system prune --all --volumes --force' + sh 'docker network create $DOCKER_NETWORK' + } + } + + stage("Switch to Git Branch") { + steps { + sh "git checkout ${BRANCH_NAME}" + } + } + + stage("Build and Run Services with Docker Compose") { + steps { + script { + // Build and run services using docker-compose with container names including the build number + sh 'docker-compose build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator' + sh 'docker-compose up -d --build --force-recreate rv-vvta-${CONTAINER_SUFFIX} rv-vdb-${CONTAINER_SUFFIX} rv-seqrepo-${CONTAINER_SUFFIX} rest-variantvalidator-${CONTAINER_SUFFIX}' + + // Wait for the PostgreSQL container to be ready + def connectionSuccessful = false + for (int attempt = 1; attempt <= 5; attempt++) { + echo "Attempt $attempt to connect to the database..." + def exitCode = sh(script: ''' + docker-compose exec -e PGPASSWORD=uta_admin rest-variantvalidator-${CONTAINER_SUFFIX} psql -U uta_admin -d vvta -h rv-vvta-${CONTAINER_SUFFIX} -p 54321 + ''', returnStatus: true) + + if (exitCode == 0) { + connectionSuccessful = true + echo "Connected successfully! Running tests..." + break + } + + echo "Connection failed. Waiting for 60 seconds before the next attempt..." + sleep 60 + } + + if (!connectionSuccessful) { + error "All connection attempts failed. Exiting..." + } + } + } + } + + stage("Run Pytest and Codecov") { + steps { + script { + // Run pytest && Run Codecov with the provided token and branch name + sh 'docker-compose exec rest-variantvalidator-${CONTAINER_SUFFIX} pytest -n 3 --cov=VariantValidator --cov=VariantFormatter --cov-report=term tests/' + + // Send coverage report to Codecov + sh 'docker-compose exec rest-variantvalidator-${CONTAINER_SUFFIX} codecov -t $CODECOV_TOKEN -b ${BRANCH_NAME}' + } + } + } + } + + post { + always { + script { + // Cleanup Docker and Docker Compose + sh 'docker-compose down -v' + sh 'docker network rm $DOCKER_NETWORK' + } + } + } +} From bbf65d85350a0d21cb0883d0b920defa2050d179 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 24 Nov 2023 12:24:23 +0000 Subject: [PATCH 136/218] Start building CI --- Jenkinsfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Jenkinsfile b/Jenkinsfile index 517b508..f068d2f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -17,6 +17,7 @@ pipeline { stage("Clone Repository Remove dangling docker components and Create Docker Network") { steps { checkout scm + sh 'apt update && apt install -y git' sh 'docker system prune --all --volumes --force' sh 'docker network create $DOCKER_NETWORK' } From 8d893e1b52ebd474f3c9835623ea18a75e534904 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 24 Nov 2023 12:30:30 +0000 Subject: [PATCH 137/218] Start building CI --- Jenkinsfile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index f068d2f..f687e7a 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,7 +1,7 @@ pipeline { agent { docker { - image 'docker/compose:1.27.4' + image 'docker:24.0.6-git' args '-v /var/run/docker.sock:/var/run/docker.sock' } } @@ -14,10 +14,15 @@ pipeline { } stages { + stage("Install Docker Compose") { + steps { + sh 'apt update && apt install -y docker-compose' + } + } + stage("Clone Repository Remove dangling docker components and Create Docker Network") { steps { checkout scm - sh 'apt update && apt install -y git' sh 'docker system prune --all --volumes --force' sh 'docker network create $DOCKER_NETWORK' } From 7b03b74932d8d335afeda7de83b2ecc4de7884ad Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 24 Nov 2023 15:06:51 +0000 Subject: [PATCH 138/218] Start building CI --- Jenkinsfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index f687e7a..06d0c22 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,7 +2,6 @@ pipeline { agent { docker { image 'docker:24.0.6-git' - args '-v /var/run/docker.sock:/var/run/docker.sock' } } From c7bb7b51856e52df35aeba4e1437fd22ba0f5928 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 24 Nov 2023 15:15:50 +0000 Subject: [PATCH 139/218] Start building CI --- Jenkinsfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Jenkinsfile b/Jenkinsfile index 06d0c22..7bc5d27 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -22,6 +22,7 @@ pipeline { stage("Clone Repository Remove dangling docker components and Create Docker Network") { steps { checkout scm + sh 'docker-compose down -v' sh 'docker system prune --all --volumes --force' sh 'docker network create $DOCKER_NETWORK' } From 6221c504deddf7027f1ad03b39312eafc5427612 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 24 Nov 2023 15:22:06 +0000 Subject: [PATCH 140/218] Start building CI --- Jenkinsfile | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 7bc5d27..60dd26f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -33,14 +33,16 @@ pipeline { sh "git checkout ${BRANCH_NAME}" } } - - stage("Build and Run Services with Docker Compose") { + stage("Build and Run containers") { + steps { + // Build and run services using docker-compose with container names including the build number + sh 'docker-compose build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator' + sh 'docker-compose up -d --build --force-recreate rv-vvta-${CONTAINER_SUFFIX} rv-vdb-${CONTAINER_SUFFIX} rv-seqrepo-${CONTAINER_SUFFIX} rest-variantvalidator-${CONTAINER_SUFFIX}' + } + } + stage("Connect and run Pytest") { steps { script { - // Build and run services using docker-compose with container names including the build number - sh 'docker-compose build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator' - sh 'docker-compose up -d --build --force-recreate rv-vvta-${CONTAINER_SUFFIX} rv-vdb-${CONTAINER_SUFFIX} rv-seqrepo-${CONTAINER_SUFFIX} rest-variantvalidator-${CONTAINER_SUFFIX}' - // Wait for the PostgreSQL container to be ready def connectionSuccessful = false for (int attempt = 1; attempt <= 5; attempt++) { From 802e4c6f06d703da7e4afb9bc122c764f62b8994 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 24 Nov 2023 15:24:59 +0000 Subject: [PATCH 141/218] Start building CI --- Jenkinsfile | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 60dd26f..ddff555 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -11,28 +11,24 @@ pipeline { DOCKER_NETWORK = "rest-variantvalidator_docker_network-${CONTAINER_SUFFIX}" DATA_VOLUME = "docker-shared-space" } - stages { - stage("Install Docker Compose") { - steps { - sh 'apt update && apt install -y docker-compose' - } - } - stage("Clone Repository Remove dangling docker components and Create Docker Network") { steps { - checkout scm - sh 'docker-compose down -v' - sh 'docker system prune --all --volumes --force' - sh 'docker network create $DOCKER_NETWORK' + checkout scm // Checkout the source code from the configured source code management system + sh 'docker system prune --all --volumes --force' // Remove unused Docker resources + sh 'docker network create $DOCKER_NETWORK' // Create a Docker network for containers } } - stage("Switch to Git Branch") { steps { sh "git checkout ${BRANCH_NAME}" } } + stage("Install Docker Compose") { + steps { + sh 'apt update && apt install -y docker-compose' + } + } stage("Build and Run containers") { steps { // Build and run services using docker-compose with container names including the build number From f3d84fbf0cb1ec932f918c9c3b352bf45de122f9 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 27 Nov 2023 10:14:38 +0000 Subject: [PATCH 142/218] Start building CI --- Jenkinsfile | 6 +++--- docker-compose.yml | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index ddff555..5d56108 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -31,9 +31,9 @@ pipeline { } stage("Build and Run containers") { steps { - // Build and run services using docker-compose with container names including the build number - sh 'docker-compose build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator' - sh 'docker-compose up -d --build --force-recreate rv-vvta-${CONTAINER_SUFFIX} rv-vdb-${CONTAINER_SUFFIX} rv-seqrepo-${CONTAINER_SUFFIX} rest-variantvalidator-${CONTAINER_SUFFIX}' + // Build and run services using docker-compose with container names including the build number + sh "docker-compose build --no-cache rv-vvta${CONTAINER_SUFFIX} rv-vdb${CONTAINER_SUFFIX} rv-seqrepo${CONTAINER_SUFFIX} rest-variantvalidator${CONTAINER_SUFFIX}" + sh "docker-compose up -d rv-vvta${CONTAINER_SUFFIX} && docker-compose up -d rv-vdb${CONTAINER_SUFFIX} && docker-compose up -d rv-seqrepo${CONTAINER_SUFFIX} && docker-compose up -d rest-variantvalidator${CONTAINER_SUFFIX}" } } stage("Connect and run Pytest") { diff --git a/docker-compose.yml b/docker-compose.yml index 8977ebe..6b2d1d0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,4 @@ + version: '3' services: @@ -5,6 +6,7 @@ services: build: context: . dockerfile: db_dockerfiles/vdb/Dockerfile + container_name: rv-vdb${CONTAINER_SUFFIX} ports: - "33061:3306" expose: @@ -14,6 +16,7 @@ services: build: context: . dockerfile: db_dockerfiles/vvta/Dockerfile + container_name: rv-vvta${CONTAINER_SUFFIX} ports: - "54321:5432" expose: @@ -24,11 +27,13 @@ services: build: context: . dockerfile: db_dockerfiles/seqrepo/Dockerfile + container_name: rv-seqrepo${CONTAINER_SUFFIX} volumes: - seqdata:/usr/local/share/seqdata # Mount volume for sequence data rest-variantvalidator: build: . + container_name: rest-variantvalidator${CONTAINER_SUFFIX} entrypoint: /bin/bash command: ["-c", "sleep infinity"] restart: always @@ -63,4 +68,3 @@ volumes: type: 'none' o: 'bind' device: '${HOME}/variantvalidator_data/logs' - From 83501dcb17f96e0463a1bc3588a0ef983b9033f5 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 27 Nov 2023 10:20:17 +0000 Subject: [PATCH 143/218] Start building CI --- Jenkinsfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 5d56108..9effc40 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -22,11 +22,12 @@ pipeline { stage("Switch to Git Branch") { steps { sh "git checkout ${BRANCH_NAME}" + sh "git pull" } } stage("Install Docker Compose") { steps { - sh 'apt update && apt install -y docker-compose' + sh 'apk update && apk add -y docker-compose' } } stage("Build and Run containers") { From 275b66d5d0042a45fc88469751ad599fc425d958 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 27 Nov 2023 10:24:51 +0000 Subject: [PATCH 144/218] Start building CI --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 9effc40..f1decdf 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -27,7 +27,7 @@ pipeline { } stage("Install Docker Compose") { steps { - sh 'apk update && apk add -y docker-compose' + sh 'apk update && apk add docker-compose' } } stage("Build and Run containers") { From 569ec95d4ba91d99d14a844a8be3025e3fd2f3f2 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 27 Nov 2023 10:38:39 +0000 Subject: [PATCH 145/218] Start building CI --- Jenkinsfile | 4 ++-- docker-compose.yml | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index f1decdf..bae1a26 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -33,8 +33,8 @@ pipeline { stage("Build and Run containers") { steps { // Build and run services using docker-compose with container names including the build number - sh "docker-compose build --no-cache rv-vvta${CONTAINER_SUFFIX} rv-vdb${CONTAINER_SUFFIX} rv-seqrepo${CONTAINER_SUFFIX} rest-variantvalidator${CONTAINER_SUFFIX}" - sh "docker-compose up -d rv-vvta${CONTAINER_SUFFIX} && docker-compose up -d rv-vdb${CONTAINER_SUFFIX} && docker-compose up -d rv-seqrepo${CONTAINER_SUFFIX} && docker-compose up -d rest-variantvalidator${CONTAINER_SUFFIX}" + sh 'docker-compose --project-name rest-VariantValidator-ci build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator' + sh 'docker-compose --project-name rest-VariantValidator-ci up -d rv-vvta && docker-compose --project-name rest-VariantValidator-ci up -d rv-vdb && docker-compose --project-name rest-VariantValidator-ci up -d rv-seqrepo && docker-compose --project-name rest-VariantValidator-ci up -d rest-variantvalidator' } } stage("Connect and run Pytest") { diff --git a/docker-compose.yml b/docker-compose.yml index 6b2d1d0..8977ebe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ - version: '3' services: @@ -6,7 +5,6 @@ services: build: context: . dockerfile: db_dockerfiles/vdb/Dockerfile - container_name: rv-vdb${CONTAINER_SUFFIX} ports: - "33061:3306" expose: @@ -16,7 +14,6 @@ services: build: context: . dockerfile: db_dockerfiles/vvta/Dockerfile - container_name: rv-vvta${CONTAINER_SUFFIX} ports: - "54321:5432" expose: @@ -27,13 +24,11 @@ services: build: context: . dockerfile: db_dockerfiles/seqrepo/Dockerfile - container_name: rv-seqrepo${CONTAINER_SUFFIX} volumes: - seqdata:/usr/local/share/seqdata # Mount volume for sequence data rest-variantvalidator: build: . - container_name: rest-variantvalidator${CONTAINER_SUFFIX} entrypoint: /bin/bash command: ["-c", "sleep infinity"] restart: always @@ -68,3 +63,4 @@ volumes: type: 'none' o: 'bind' device: '${HOME}/variantvalidator_data/logs' + From 648c5c72f29c94290ef19b087e2f2b1c60eebf0e Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 27 Nov 2023 10:39:39 +0000 Subject: [PATCH 146/218] Start building CI --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index bae1a26..5c8a5fd 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -33,8 +33,8 @@ pipeline { stage("Build and Run containers") { steps { // Build and run services using docker-compose with container names including the build number - sh 'docker-compose --project-name rest-VariantValidator-ci build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator' - sh 'docker-compose --project-name rest-VariantValidator-ci up -d rv-vvta && docker-compose --project-name rest-VariantValidator-ci up -d rv-vdb && docker-compose --project-name rest-VariantValidator-ci up -d rv-seqrepo && docker-compose --project-name rest-VariantValidator-ci up -d rest-variantvalidator' + sh 'docker-compose --project-name rest-variantvalidator-ci build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator' + sh 'docker-compose --project-name rest-variantvalidator-ci up -d rv-vvta && docker-compose --project-name rest-VariantValidator-ci up -d rv-vdb && docker-compose --project-name rest-VariantValidator-ci up -d rv-seqrepo && docker-compose --project-name rest-VariantValidator-ci up -d rest-variantvalidator' } } stage("Connect and run Pytest") { From 83a69dd56beacf7110f39ae8986fdf254d5ed7ef Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 27 Nov 2023 11:53:57 +0000 Subject: [PATCH 147/218] Start building CI --- Jenkinsfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 5c8a5fd..87a0615 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -8,7 +8,6 @@ pipeline { environment { CODECOV_TOKEN = credentials('CODECOV_TOKEN_rest_variantvalidator') CONTAINER_SUFFIX = "${BUILD_NUMBER}" - DOCKER_NETWORK = "rest-variantvalidator_docker_network-${CONTAINER_SUFFIX}" DATA_VOLUME = "docker-shared-space" } stages { @@ -16,7 +15,6 @@ pipeline { steps { checkout scm // Checkout the source code from the configured source code management system sh 'docker system prune --all --volumes --force' // Remove unused Docker resources - sh 'docker network create $DOCKER_NETWORK' // Create a Docker network for containers } } stage("Switch to Git Branch") { From 9a7bc37dd1bb87233b423f5b27547981dcd07c5e Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 27 Nov 2023 11:57:25 +0000 Subject: [PATCH 148/218] Start building CI --- Jenkinsfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Jenkinsfile b/Jenkinsfile index 87a0615..9129711 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -31,6 +31,7 @@ pipeline { stage("Build and Run containers") { steps { // Build and run services using docker-compose with container names including the build number + sh 'mkdir -p ~/variantvalidator_data/seqdata && mkdir -p ~/variantvalidator_data/logs' sh 'docker-compose --project-name rest-variantvalidator-ci build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator' sh 'docker-compose --project-name rest-variantvalidator-ci up -d rv-vvta && docker-compose --project-name rest-VariantValidator-ci up -d rv-vdb && docker-compose --project-name rest-VariantValidator-ci up -d rv-seqrepo && docker-compose --project-name rest-VariantValidator-ci up -d rest-variantvalidator' } From dbe5203155374d4fb7be379d0d59be7a0267c4e8 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 29 Nov 2023 10:50:36 +0000 Subject: [PATCH 149/218] Start building CI --- Jenkinsfile | 2 +- README.md | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 9129711..1be7135 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -33,7 +33,7 @@ pipeline { // Build and run services using docker-compose with container names including the build number sh 'mkdir -p ~/variantvalidator_data/seqdata && mkdir -p ~/variantvalidator_data/logs' sh 'docker-compose --project-name rest-variantvalidator-ci build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator' - sh 'docker-compose --project-name rest-variantvalidator-ci up -d rv-vvta && docker-compose --project-name rest-VariantValidator-ci up -d rv-vdb && docker-compose --project-name rest-VariantValidator-ci up -d rv-seqrepo && docker-compose --project-name rest-VariantValidator-ci up -d rest-variantvalidator' + sh 'docker-compose --project-name rest-variantvalidator-ci up -d rv-vvta && docker-compose --project-name rest-variantvalidator-ci up -d rv-vdb && docker-compose --project-name rest-variantvalidator-ci up -d rv-seqrepo && docker-compose --project-name rest-variantvalidator-ci up -d rest-variantvalidator' } } stage("Connect and run Pytest") { diff --git a/README.md b/README.md index 6febf49..6ee4506 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # rest_VariantValidator - -## Status +[![Build Status](http://127.0.0.1:8080/job/rest_VariantValidator%20CI/job/master/badge/icon)](http://127.0.0.1:8080/job/rest_VariantValidator%20CI/job/master/) [![codecov](https://codecov.io/gh/openvar/rest_variantValidator/graph/badge.svg?token=DE92ZVZT3F)](https://codecov.io/gh/openvar/rest_variantValidator) ## About rest_variantValidator From 1609e1f98f1d133fa859764812fcfc7f65099599 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 29 Nov 2023 11:38:18 +0000 Subject: [PATCH 150/218] Start building CI --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 1be7135..4d2c986 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -31,7 +31,7 @@ pipeline { stage("Build and Run containers") { steps { // Build and run services using docker-compose with container names including the build number - sh 'mkdir -p ~/variantvalidator_data/seqdata && mkdir -p ~/variantvalidator_data/logs' + sh 'mkdir -p ${HOME}/variantvalidator_data/seqdata && mkdir -p ${HOME}/variantvalidator_data/logs' sh 'docker-compose --project-name rest-variantvalidator-ci build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator' sh 'docker-compose --project-name rest-variantvalidator-ci up -d rv-vvta && docker-compose --project-name rest-variantvalidator-ci up -d rv-vdb && docker-compose --project-name rest-variantvalidator-ci up -d rv-seqrepo && docker-compose --project-name rest-variantvalidator-ci up -d rest-variantvalidator' } From 3cac484e6d24083d856a31f5e76257d970634dc1 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 29 Nov 2023 12:45:48 +0000 Subject: [PATCH 151/218] Start building CI --- Jenkinsfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 4d2c986..86e1386 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -32,6 +32,10 @@ pipeline { steps { // Build and run services using docker-compose with container names including the build number sh 'mkdir -p ${HOME}/variantvalidator_data/seqdata && mkdir -p ${HOME}/variantvalidator_data/logs' + sh 'pwd' + sh 'ls -l' + sh 'ls ${HOME}/variantvalidator_data/seqdata' + sh 'ls ${HOME}/variantvalidator_data/logs' sh 'docker-compose --project-name rest-variantvalidator-ci build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator' sh 'docker-compose --project-name rest-variantvalidator-ci up -d rv-vvta && docker-compose --project-name rest-variantvalidator-ci up -d rv-vdb && docker-compose --project-name rest-variantvalidator-ci up -d rv-seqrepo && docker-compose --project-name rest-variantvalidator-ci up -d rest-variantvalidator' } From 88a47a833fbbeee610a7a622e5c14a5157113f46 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 29 Nov 2023 13:19:45 +0000 Subject: [PATCH 152/218] Start building CI --- Jenkinsfile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 86e1386..11e1de9 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -31,11 +31,12 @@ pipeline { stage("Build and Run containers") { steps { // Build and run services using docker-compose with container names including the build number - sh 'mkdir -p ${HOME}/variantvalidator_data/seqdata && mkdir -p ${HOME}/variantvalidator_data/logs' - sh 'pwd' - sh 'ls -l' - sh 'ls ${HOME}/variantvalidator_data/seqdata' - sh 'ls ${HOME}/variantvalidator_data/logs' + // Create the directories within the Jenkins home directory + sh 'mkdir -p /var/jenkins_home/variantvalidator_data/seqdata && mkdir -p /var/jenkins_home/variantvalidator_data/logs' + + // List the contents of the parent directory to verify the created directories + sh 'ls -l /var/jenkins_home/variantvalidator_data' + sh 'docker-compose --project-name rest-variantvalidator-ci build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator' sh 'docker-compose --project-name rest-variantvalidator-ci up -d rv-vvta && docker-compose --project-name rest-variantvalidator-ci up -d rv-vdb && docker-compose --project-name rest-variantvalidator-ci up -d rv-seqrepo && docker-compose --project-name rest-variantvalidator-ci up -d rest-variantvalidator' } From 60dde13473675fc1caeb15c0fa1dd30e51dcfdc6 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 29 Nov 2023 13:46:35 +0000 Subject: [PATCH 153/218] Start building CI --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index 11e1de9..82c53d5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -32,7 +32,7 @@ pipeline { steps { // Build and run services using docker-compose with container names including the build number // Create the directories within the Jenkins home directory - sh 'mkdir -p /var/jenkins_home/variantvalidator_data/seqdata && mkdir -p /var/jenkins_home/variantvalidator_data/logs' + sh 'mkdir -p /var/lib/docker/volumes/rest-variantvalidator-ci/variantvalidator_data/seqdata && mkdir -p /var/lib/docker/volumes/rest-variantvalidator-ci/variantvalidator_data/logs' // List the contents of the parent directory to verify the created directories sh 'ls -l /var/jenkins_home/variantvalidator_data' From 71057c42bc5f505108b341add37d76acfe5be4f1 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 29 Nov 2023 14:32:56 +0000 Subject: [PATCH 154/218] Start building CI --- Jenkinsfile | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 82c53d5..302d36b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -31,12 +31,11 @@ pipeline { stage("Build and Run containers") { steps { // Build and run services using docker-compose with container names including the build number - // Create the directories within the Jenkins home directory - sh 'mkdir -p /var/lib/docker/volumes/rest-variantvalidator-ci/variantvalidator_data/seqdata && mkdir -p /var/lib/docker/volumes/rest-variantvalidator-ci/variantvalidator_data/logs' - - // List the contents of the parent directory to verify the created directories - sh 'ls -l /var/jenkins_home/variantvalidator_data' - + sh 'mkdir -p /root/variantvalidator_data/seqdata && mkdir -p /root/variantvalidator_data/logs' + sh 'pwd' + sh 'ls -l' + sh 'ls /root/variantvalidator_data/seqdata' + sh 'ls /root/variantvalidator_data/logs' sh 'docker-compose --project-name rest-variantvalidator-ci build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator' sh 'docker-compose --project-name rest-variantvalidator-ci up -d rv-vvta && docker-compose --project-name rest-variantvalidator-ci up -d rv-vdb && docker-compose --project-name rest-variantvalidator-ci up -d rv-seqrepo && docker-compose --project-name rest-variantvalidator-ci up -d rest-variantvalidator' } From 6c69282789477f6bedcb6583a8167bc71ed5c645 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 1 Dec 2023 11:14:45 +0000 Subject: [PATCH 155/218] Reformat the docker-compose mounts --- docker-compose.yml | 15 +++------------ docs/DOCKER.md | 17 +++++++++-------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 8977ebe..1305a00 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,7 +25,7 @@ services: context: . dockerfile: db_dockerfiles/seqrepo/Dockerfile volumes: - - seqdata:/usr/local/share/seqdata # Mount volume for sequence data + - ${SEQDATA_VOLUME}:/usr/local/share/seqdata # Mount volume for sequence data rest-variantvalidator: build: . @@ -37,8 +37,8 @@ services: - rv-vvta - rv-seqrepo volumes: - - vv-logs:/usr/local/share/logs # Mount volume for logs - - seqdata:/usr/local/share/seqdata # Mount volume for sequence data + - ${VV_LOGS_VOLUME}:/usr/local/share/logs # Mount volume for logs + - ${SEQDATA_VOLUME}:/usr/local/share/seqdata # Mount volume for sequence data ports: - "5000:5000" - "5050:5050" @@ -53,14 +53,5 @@ services: volumes: seqdata: driver: local - driver_opts: - type: 'none' - o: 'bind' - device: '${HOME}/variantvalidator_data/seqdata' vv-logs: driver: local - driver_opts: - type: 'none' - o: 'bind' - device: '${HOME}/variantvalidator_data/logs' - diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 22664d1..a511adb 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -48,12 +48,6 @@ compiling SeqRepo* $ docker-compose pull ``` -- Create a directory for sharing resources between your computer and the container -```bash -$ mkdir -p ~/variantvalidator_data/seqdata && mkdir -p ~/variantvalidator_data/logs -``` -*i.e.,* a directory called `variantvalidator_data` in your `home` directory with sub-directories `seqdata` and `logs` - #### Build and startup procedure rest_VariantValidator can be built in Production mode and Development mode. Development mode mounts the root directory @@ -85,9 +79,16 @@ $ docker-compose -f docker-compose.yml -f docker-compose-dev.yml build --no-cach $ docker-compose up -d rv-vvta && \ docker-compose up -d rv-vdb && \ docker-compose up -d rv-seqrepo && \ - docker-compose up -d rest-variantvalidator + docker-compose up -d rest-variantvalidator -v '${HOME}/variantvalidator_data/seqdata' -v '${HOME}/variantvalidator_data/logs' ``` -- Or for a development and testing build, swap for this command +- Or for a development and testing build, swap for these commands + +- Create directories for sharing resources between your computer and the containers +```bash +$ mkdir -p ~/variantvalidator_data/seqdata && mkdir -p ~/variantvalidator_data/logs +``` +*i.e.,* a directory called `variantvalidator_data` in your `home` directory with sub-directories `seqdata` and `logs` + ```bash $ docker-compose up -d rv-vvta && \ docker-compose up -d rv-vdb && \ From aecf37e391add11017bfa9bf83e92fbf97f25844 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 1 Dec 2023 12:28:08 +0000 Subject: [PATCH 156/218] Reformat the docker-compose mounts --- docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1305a00..07d2713 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,7 +25,7 @@ services: context: . dockerfile: db_dockerfiles/seqrepo/Dockerfile volumes: - - ${SEQDATA_VOLUME}:/usr/local/share/seqdata # Mount volume for sequence data + - seqdata:/usr/local/share/seqdata # Mount volume for sequence data rest-variantvalidator: build: . @@ -37,8 +37,8 @@ services: - rv-vvta - rv-seqrepo volumes: - - ${VV_LOGS_VOLUME}:/usr/local/share/logs # Mount volume for logs - - ${SEQDATA_VOLUME}:/usr/local/share/seqdata # Mount volume for sequence data + - vv-logs:/usr/local/share/logs # Mount volume for logs + - seqdata:/usr/local/share/seqdata # Mount volume for sequence data ports: - "5000:5000" - "5050:5050" From 95147f575c6bafc18e7be8adbb8b74fe975e700d Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 1 Dec 2023 13:58:21 +0000 Subject: [PATCH 157/218] Reformat the docker-compose mounts --- docker-compose.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 07d2713..f59e9a5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -49,9 +49,3 @@ services: - "5050" - "8000" - "8080" - -volumes: - seqdata: - driver: local - vv-logs: - driver: local From 6f21e467b686b80f54d43f2c0345cface81ba808 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 1 Dec 2023 14:21:01 +0000 Subject: [PATCH 158/218] Reformat the docker-compose mounts --- docker-compose.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index f59e9a5..07d2713 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -49,3 +49,9 @@ services: - "5050" - "8000" - "8080" + +volumes: + seqdata: + driver: local + vv-logs: + driver: local From d865469f5a899d6b3211be1b209009f79342c941 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 6 Dec 2023 10:29:17 +0000 Subject: [PATCH 159/218] Update to use JSON inputs and increase coverage of tests --- Jenkinsfile | 2 +- .../endpoints/lovd_endpoints.py | 12 ++-- .../endpoints/variantformatter_endpoints.py | 8 ++- .../endpoints/variantvalidator_endpoints.py | 25 ++++++++- .../utils/input_formatting.py | 40 ++++++++++++++ tests/test_endpoints.py | 35 +++++++++++- tests/test_input_formatting.py | 55 +++++++++++++++++++ 7 files changed, 168 insertions(+), 9 deletions(-) create mode 100644 rest_VariantValidator/utils/input_formatting.py create mode 100644 tests/test_input_formatting.py diff --git a/Jenkinsfile b/Jenkinsfile index 302d36b..4cebf64 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -72,7 +72,7 @@ pipeline { steps { script { // Run pytest && Run Codecov with the provided token and branch name - sh 'docker-compose exec rest-variantvalidator-${CONTAINER_SUFFIX} pytest -n 3 --cov=VariantValidator --cov=VariantFormatter --cov-report=term tests/' + sh 'docker-compose exec rest-variantvalidator-${CONTAINER_SUFFIX} pytest --cov=rest_VariantValidator --cov-report=term tests/' // Send coverage report to Codecov sh 'docker-compose exec rest-variantvalidator-${CONTAINER_SUFFIX} codecov -t $CODECOV_TOKEN -b ${BRANCH_NAME}' diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index d156b85..6836954 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -1,13 +1,9 @@ # Import modules import ast from flask_restx import Namespace, Resource -from rest_VariantValidator.utils import request_parser, representations +from rest_VariantValidator.utils import request_parser, representations, input_formatting from rest_VariantValidator.utils.object_pool import simple_variant_formatter_pool -# Import variantFormatter -import VariantFormatter -import VariantFormatter.simpleVariantFormatter - def ordereddict_to_dict(value): for k, v in value.items(): @@ -80,7 +76,13 @@ def get(self, genome_build, variant_description, transcript_model, select_transc if liftover == 'False' or liftover == 'false': liftover = False + # Import formatter object from pool simple_formatter = simple_variant_formatter_pool.get() + + # Convert inputs to JSON arrays + variant_description = input_formatting.format_input(variant_description) + select_transcripts = input_formatting.format_input(select_transcripts) + try: content = simple_formatter.format(variant_description, genome_build, transcript_model, select_transcripts, checkonly, liftover) diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index 03fc6b6..62be00a 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -1,6 +1,6 @@ # Import modules from flask_restx import Namespace, Resource -from rest_VariantValidator.utils import request_parser, representations +from rest_VariantValidator.utils import request_parser, representations, input_formatting from rest_VariantValidator.utils.object_pool import simple_variant_formatter_pool """ @@ -56,7 +56,13 @@ def get(self, genome_build, variant_description, transcript_model, select_transc if checkonly == 'True' or checkonly == 'true': checkonly = True + # Import formatter from pool simple_formatter = simple_variant_formatter_pool.get() + + # Convert inputs to JSON arrays + variant_description = input_formatting.format_input(variant_description) + select_transcripts = input_formatting.format_input(select_transcripts) + try: content = simple_formatter.format(variant_description, genome_build, transcript_model, select_transcripts, checkonly) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 54d3ee0..f94bd47 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -1,6 +1,6 @@ # Import modules from flask_restx import Namespace, Resource -from rest_VariantValidator.utils import exceptions, request_parser, representations +from rest_VariantValidator.utils import exceptions, request_parser, representations, input_formatting from rest_VariantValidator.utils.object_pool import vval_object_pool, g2t_object_pool """ @@ -51,7 +51,14 @@ class VariantValidatorClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) def get(self, genome_build, variant_description, select_transcripts): + + # Import object from vval pool vval = vval_object_pool.get_object() + + # Convert inputs to JSON arrays + variant_description = input_formatting.format_input(variant_description) + select_transcripts = input_formatting.format_input(select_transcripts) + try: # Validate using the VariantValidator Python Library validate = vval.validate(variant_description, genome_build, select_transcripts) @@ -85,7 +92,13 @@ class Gene2transcriptsClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) def get(self, gene_query): + + # Get vvval object from pool vval = g2t_object_pool.get_object() + + # Convert inputs to JSON arrays + gene_query = input_formatting.format_input(gene_query) + try: content = vval.gene2transcripts(gene_query) except ConnectionError: @@ -137,7 +150,14 @@ class Gene2transcriptsV2Class(Resource): # Add documentation about the parser @api.expect(parser, validate=True) def get(self, gene_query, limit_transcripts, transcript_set, genome_build): + + # Get vval object from pool vval = g2t_object_pool.get_object() + + # Convert inputs to JSON arrays + gene_query = input_formatting.format_input(gene_query) + limit_transcripts = input_formatting.format_input(limit_transcripts) + try: if genome_build not in ["GRCh37", "GRCh38"]: genome_build = None @@ -177,7 +197,10 @@ class Hgvs2referenceClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) def get(self, hgvs_description): + + # Get vval object from pool vval = vval_object_pool.get_object() + try: content = vval.hgvs2ref(hgvs_description) except Exception as e: diff --git a/rest_VariantValidator/utils/input_formatting.py b/rest_VariantValidator/utils/input_formatting.py new file mode 100644 index 0000000..4cd7692 --- /dev/null +++ b/rest_VariantValidator/utils/input_formatting.py @@ -0,0 +1,40 @@ +import json + + +def format_input(data_string): + """ + Takes an input string. Tries to convert from JSON to list, otherwise converts a string into a list. + Then goes on to check for pipe delimited data and splits if necessary + The output is a JSON array + """ + data_string = str(data_string) + try: + data_list = json.loads(data_string) + except json.decoder.JSONDecodeError: + data_intermediate = data_string.replace("|gom", "&gom") + data_intermediate = data_intermediate.replace("|lom", "&lom") + pre_data_list = data_intermediate.split("|") + if not isinstance(pre_data_list, list): + pre_data_list = [pre_data_list] + data_list = [] + for entry in pre_data_list: + entry = entry.replace("&", "|") + data_list.append(entry) + return json.dumps(data_list) + +# +# Copyright (C) 2016-2023 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 17e0183..a73ae4e 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -15,5 +15,38 @@ def client(): def test_hello_endpoint(client): response = client.get('/hello/') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK - assert response.json["status"] == "hello_world" # Check the JSON response content assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + + +def test_lovd_endpoint(client): + response = client.get('/LOVD/lovd/GRCh38/17-50198002-C-A/all/mane/True/False?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK + assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + + +def test_vf_endpoint(client): + response = client.get('/VariantFormatter/variantformatter/GRCh38/NC_000017.10%3Ag.48275363C%3EA/all/mane/True?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK + assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + + +def test_vv_endpoint(client): + response = client.get('/VariantValidator/variantvalidator/GRCh38/NM_000088.3%3Ac.589G%3ET/mane?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK + assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + + +def test_g2t_endpoint(client): + response = client.get('/VariantValidator/tools/gene2transcripts/COL1A1?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK + + +def test_g2t2_endpoint(client): + response = client.get('/VariantValidator/tools/gene2transcripts_v2/COL1A1%7CCOL1A2%7CCOL5A1/mane/all/GRCh38?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK + + +def test_h2ref_endpoint(client): + response = client.get('/VariantValidator/tools/hgvs2reference/NM_000088.3%3Ac.589G%3ET?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK + diff --git a/tests/test_input_formatting.py b/tests/test_input_formatting.py new file mode 100644 index 0000000..7b626e2 --- /dev/null +++ b/tests/test_input_formatting.py @@ -0,0 +1,55 @@ +import unittest +import json +from rest_VariantValidator.utils import input_formatting + + +class TestFormatInput(unittest.TestCase): + + def test_json_input(self): + data_string = '[1, 2, 3]' + result = input_formatting.format_input(data_string) + self.assertEqual(result, '[1, 2, 3]') + + def test_string_input(self): + data_string = '1|2|3' + result = input_formatting.format_input(data_string) + self.assertEqual(result, '["1", "2", "3"]') + + def test_json_array_input(self): + data_string = '["a", "b", "c"]' + result = input_formatting.format_input(data_string) + self.assertEqual(result, '["a", "b", "c"]') + + def test_mixed_input(self): + data_string = '[1, 2, 3]|4|5' + result = input_formatting.format_input(data_string) + self.assertEqual(result, '["[1, 2, 3]", "4", "5"]') + + def test_pipe_and_special_characters(self): + data_string = 'a|b|c|gomd|e|lomf' + result = input_formatting.format_input(data_string) + self.assertEqual(result, '["a", "b", "c|gomd", "e|lomf"]') + + def test_empty_input(self): + data_string = '' + result = input_formatting.format_input(data_string) + self.assertEqual(result, '[""]') + + def test_multiple_pipe_symbols(self): + data_string = 'a||b||c' + result = input_formatting.format_input(data_string) + self.assertEqual(result, '["a", "", "b", "", "c"]') + + def test_json_array_string_input(self): + data_string = '["apple", "banana", "cherry"]' + result = input_formatting.format_input(data_string) + self.assertEqual(result, '["apple", "banana", "cherry"]') + + def test_result_is_json(self): + data_string = '[1, 2, 3]' + result = input_formatting.format_input(data_string) + self.assertIsInstance(result, str) + json_result = json.loads(result) + self.assertEqual(json_result, [1, 2, 3]) + + From bbface87087f19067c24b3ece864cd251b69a950 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 6 Dec 2023 14:10:25 +0000 Subject: [PATCH 160/218] Workgin on CI --- docker-compose.yml | 8 ++++++++ docs/DOCKER.md | 10 +++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 07d2713..927ca81 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -53,5 +53,13 @@ services: volumes: seqdata: driver: local + driver_opts: + type: 'none' + o: 'bind' + device: '${HOME}/variantvalidator_data/seqdata' vv-logs: driver: local + driver_opts: + type: 'none' + o: 'bind' + device: '${HOME}/variantvalidator_data/logs' \ No newline at end of file diff --git a/docs/DOCKER.md b/docs/DOCKER.md index a511adb..fe4a03b 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -52,7 +52,7 @@ $ docker-compose pull rest_VariantValidator can be built in Production mode and Development mode. Development mode mounts the root directory of the host git Repository to the equivalent project directory in the docker container. This means that changes to the -code on the host machine are mapped into the container allowing on-the-fly development. +code on the host machine are mapped into the container allowing on-the-fly development. It also allows you to view logs. Choose one of the following commands to build and start the rest_VariantValidaor containers - Production build @@ -65,7 +65,7 @@ $ docker-compose build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidato # Build $ docker-compose -f docker-compose.yml -f docker-compose-dev.yml build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator ``` -- The build stage has completed when you see +- The build stage has completed when you see the following message or something similar ``` => [rest-variantvalidator 10/10] COPY configuration/docker.ini /root/.variantvalidator 0.0s => [rest-variantvalidator] exporting to image 2.3s @@ -74,12 +74,12 @@ $ docker-compose -f docker-compose.yml -f docker-compose-dev.yml build --no-cach => => naming to docker.io/library/rest_variantvalidator-rest-variantvalidator ``` -- Use this command to complete the build and wait for the above messages +- Use this command to complete the standard build and wait for the above messages ```bash $ docker-compose up -d rv-vvta && \ docker-compose up -d rv-vdb && \ docker-compose up -d rv-seqrepo && \ - docker-compose up -d rest-variantvalidator -v '${HOME}/variantvalidator_data/seqdata' -v '${HOME}/variantvalidator_data/logs' + docker-compose up -d rest-variantvalidator ``` - Or for a development and testing build, swap for these commands @@ -96,7 +96,7 @@ $ docker-compose up -d rv-vvta && \ docker-compose -f docker-compose.yml -f docker-compose-dev.yml up -d dev-mode ``` -- The containers are started and running when you see +- In both cases containers are started and running when you see messages similar to ```bash Creating rest_variantvalidator_rv-vvta_1 ... done Creating rest_variantvalidator_rv-vdb_1 ... done From ee1e980bc916b570ee3055453e590b1ef589cd51 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 6 Dec 2023 19:08:34 +0000 Subject: [PATCH 161/218] Switch off genomic variant processing with select_transcripts = raw for VV endpoint --- .../endpoints/variantvalidator_endpoints.py | 9 ++++++ .../utils/input_formatting.py | 5 +++- tests/test_endpoints.py | 29 +++++++++++++++++++ tests/test_input_formatting.py | 2 +- 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index f94bd47..c0e724a 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -55,6 +55,15 @@ def get(self, genome_build, variant_description, select_transcripts): # Import object from vval pool vval = vval_object_pool.get_object() + # Switch off select_transcripts = all or raw for genomic variants + if "all" in select_transcripts or "raw" in select_transcripts: + if "c." not in variant_description and "n." not in variant_description and "r." not in variant_description \ + and "p." not in variant_description: + return {"Not Found": "Setting select_transcripts to 'all' or 'raw' is deprecated for genomic " + "variant processing using this endpoint. Use another option, the LOVD " + "endpoint or https://variantvalidator.org/service/validate/batch/ and contact " + "admin for fair usage information"}, 404 + # Convert inputs to JSON arrays variant_description = input_formatting.format_input(variant_description) select_transcripts = input_formatting.format_input(select_transcripts) diff --git a/rest_VariantValidator/utils/input_formatting.py b/rest_VariantValidator/utils/input_formatting.py index 4cd7692..85f91a0 100644 --- a/rest_VariantValidator/utils/input_formatting.py +++ b/rest_VariantValidator/utils/input_formatting.py @@ -20,7 +20,10 @@ def format_input(data_string): for entry in pre_data_list: entry = entry.replace("&", "|") data_list.append(entry) - return json.dumps(data_list) + if len(data_list) == 1: + return str(data_list[0]) + else: + return json.dumps(data_list) # # Copyright (C) 2016-2023 VariantValidator Contributors diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index a73ae4e..acecd88 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -50,3 +50,32 @@ def test_h2ref_endpoint(client): response = client.get('/VariantValidator/tools/hgvs2reference/NM_000088.3%3Ac.589G%3ET?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK + +def test_vv_endpoint_all_tx(client): + response = client.get('/VariantValidator/variantvalidator/GRCh38/NM_000088.3%3Ac.589G%3ET/all?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK + assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + + +def test_vv_endpoint_all_vcf(client): + response = client.get('/VariantValidator/variantvalidator/GRCh38/17-50198002-C-A/all?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 404 # Check if the response status code is 200 OK + assert "metadata" not in response.json.keys() # Check if "metadata" key is in the JSON response + + +def test_vv_endpoint_all_genomic(client): + response = client.get('/VariantValidator/variantvalidator/GRCh38/NC_000017.10:g.48275363C>A/all?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 404 # Check if the response status code is 200 OK + assert "metadata" not in response.json.keys() # Check if "metadata" key is in the JSON response + + +def test_vv_endpoint_mane_genomic(client): + response = client.get('/VariantValidator/variantvalidator/GRCh38/NC_000017.10:g.48275363C>A/mane?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK + assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + + +def test_vv_endpoint_mane_vcf(client): + response = client.get('/VariantValidator/variantvalidator/GRCh38/17-50198002-C-A/mane?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK + assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response \ No newline at end of file diff --git a/tests/test_input_formatting.py b/tests/test_input_formatting.py index 7b626e2..a4c464b 100644 --- a/tests/test_input_formatting.py +++ b/tests/test_input_formatting.py @@ -33,7 +33,7 @@ def test_pipe_and_special_characters(self): def test_empty_input(self): data_string = '' result = input_formatting.format_input(data_string) - self.assertEqual(result, '[""]') + self.assertEqual(result, '') def test_multiple_pipe_symbols(self): data_string = 'a||b||c' From 38dd8c7e72f00b290b50dd3e33196eda72b6d095 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 6 Dec 2023 19:35:30 +0000 Subject: [PATCH 162/218] Switch off genomic variant processing with select_transcripts = raw for VV endpoint --- .../endpoints/variantvalidator_endpoints.py | 6 +++++- tests/test_endpoints.py | 12 ++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index c0e724a..7ae4bdf 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -56,13 +56,17 @@ def get(self, genome_build, variant_description, select_transcripts): vval = vval_object_pool.get_object() # Switch off select_transcripts = all or raw for genomic variants - if "all" in select_transcripts or "raw" in select_transcripts: + if ("all" in select_transcripts or "raw" in select_transcripts) and "auth" not in select_transcripts: if "c." not in variant_description and "n." not in variant_description and "r." not in variant_description \ and "p." not in variant_description: return {"Not Found": "Setting select_transcripts to 'all' or 'raw' is deprecated for genomic " "variant processing using this endpoint. Use another option, the LOVD " "endpoint or https://variantvalidator.org/service/validate/batch/ and contact " "admin for fair usage information"}, 404 + elif "auth_all" in select_transcripts: + select_transcripts = "all" + elif "auth_raw" in select_transcripts: + select_transcripts = "raw" # Convert inputs to JSON arrays variant_description = input_formatting.format_input(variant_description) diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index acecd88..3b0764d 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -78,4 +78,16 @@ def test_vv_endpoint_mane_genomic(client): def test_vv_endpoint_mane_vcf(client): response = client.get('/VariantValidator/variantvalidator/GRCh38/17-50198002-C-A/mane?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK + assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + + +def test_vv_endpoint_auth_raw_vcf(client): + response = client.get('/VariantValidator/variantvalidator/GRCh38/17-50198002-C-A/auth_all?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK + assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + + +def test_vv_endpoint_auth_all_vcf(client): + response = client.get('/VariantValidator/variantvalidator/GRCh38/17-50198002-C-A/auth_raw?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response \ No newline at end of file From dad5dfe06a21a30d8a53c28c872177aec405ff70 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 6 Dec 2023 21:46:53 +0000 Subject: [PATCH 163/218] Switch off genomic variant processing with select_transcripts = raw for VV endpoint --- .../endpoints/variantvalidator_endpoints.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 7ae4bdf..7b14a71 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -60,9 +60,10 @@ def get(self, genome_build, variant_description, select_transcripts): if "c." not in variant_description and "n." not in variant_description and "r." not in variant_description \ and "p." not in variant_description: return {"Not Found": "Setting select_transcripts to 'all' or 'raw' is deprecated for genomic " - "variant processing using this endpoint. Use another option, the LOVD " - "endpoint or https://variantvalidator.org/service/validate/batch/ and contact " - "admin for fair usage information"}, 404 + "variant processing using this endpoint. Contact admin on " + "https://variantvalidator.org/help/contact/ for updated instructions and" + "fair usage information; use another option; or use the LOVD endpoint which is " + "designed for integration into pipelines"}, 404 elif "auth_all" in select_transcripts: select_transcripts = "all" elif "auth_raw" in select_transcripts: From ed6ba76e4dc3f2ef35fa4df9bc5df46ef51592a3 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 6 Dec 2023 21:49:29 +0000 Subject: [PATCH 164/218] Switch off genomic variant processing with select_transcripts = raw for VV endpoint --- docs/DOCKER.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index fe4a03b..ba5a287 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -55,6 +55,10 @@ of the host git Repository to the equivalent project directory in the docker con code on the host machine are mapped into the container allowing on-the-fly development. It also allows you to view logs. Choose one of the following commands to build and start the rest_VariantValidaor containers +- Create directories for sharing resources between your computer and the containers +```bash +$ mkdir -p ~/variantvalidator_data/seqdata && mkdir -p ~/variantvalidator_data/logs + - Production build ```bash # Build @@ -83,9 +87,6 @@ $ docker-compose up -d rv-vvta && \ ``` - Or for a development and testing build, swap for these commands -- Create directories for sharing resources between your computer and the containers -```bash -$ mkdir -p ~/variantvalidator_data/seqdata && mkdir -p ~/variantvalidator_data/logs ``` *i.e.,* a directory called `variantvalidator_data` in your `home` directory with sub-directories `seqdata` and `logs` From 945b95cb7c2b695094a3af42212b9f327360f765 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 7 Dec 2023 13:31:40 +0000 Subject: [PATCH 165/218] Update CI script with correct volume naming --- Jenkinsfile | 15 +++++++++------ .../endpoints/variantvalidator_endpoints.py | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 4cebf64..460b72c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -8,7 +8,7 @@ pipeline { environment { CODECOV_TOKEN = credentials('CODECOV_TOKEN_rest_variantvalidator') CONTAINER_SUFFIX = "${BUILD_NUMBER}" - DATA_VOLUME = "docker-shared-space" + DATA_VOLUME = "${HOME}/variantvalidator_data" } stages { stage("Clone Repository Remove dangling docker components and Create Docker Network") { @@ -31,11 +31,14 @@ pipeline { stage("Build and Run containers") { steps { // Build and run services using docker-compose with container names including the build number - sh 'mkdir -p /root/variantvalidator_data/seqdata && mkdir -p /root/variantvalidator_data/logs' - sh 'pwd' - sh 'ls -l' - sh 'ls /root/variantvalidator_data/seqdata' - sh 'ls /root/variantvalidator_data/logs' + sh "mkdir -p ${HOME}/variantvalidator_data/seqdata && mkdir -p ${HOME}/variantvalidator_data/logs" + + // Add ls commands to check if the directories exist + sh "ls -l ${HOME}/variantvalidator_data" + sh "ls -l ${HOME}/variantvalidator_data/seqdata" + sh "ls -l ${HOME}/variantvalidator_data/logs" + + // Create and run the containers sh 'docker-compose --project-name rest-variantvalidator-ci build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator' sh 'docker-compose --project-name rest-variantvalidator-ci up -d rv-vvta && docker-compose --project-name rest-variantvalidator-ci up -d rv-vdb && docker-compose --project-name rest-variantvalidator-ci up -d rv-seqrepo && docker-compose --project-name rest-variantvalidator-ci up -d rest-variantvalidator' } diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 7b14a71..3bbd67c 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -62,7 +62,7 @@ def get(self, genome_build, variant_description, select_transcripts): return {"Not Found": "Setting select_transcripts to 'all' or 'raw' is deprecated for genomic " "variant processing using this endpoint. Contact admin on " "https://variantvalidator.org/help/contact/ for updated instructions and" - "fair usage information; use another option; or use the LOVD endpoint which is " + " fair usage information; use another option; or use the LOVD endpoint which is " "designed for integration into pipelines"}, 404 elif "auth_all" in select_transcripts: select_transcripts = "all" From 3b368da64f2ff2b779209493221bced2c62af51a Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 8 Dec 2023 10:50:03 +0000 Subject: [PATCH 166/218] Update CI script with correct volume naming --- Jenkinsfile | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 460b72c..7b91573 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -8,13 +8,14 @@ pipeline { environment { CODECOV_TOKEN = credentials('CODECOV_TOKEN_rest_variantvalidator') CONTAINER_SUFFIX = "${BUILD_NUMBER}" - DATA_VOLUME = "${HOME}/variantvalidator_data" + DATA_VOLUME = "/root/variantvalidator_data/" } + stages { stage("Clone Repository Remove dangling docker components and Create Docker Network") { steps { - checkout scm // Checkout the source code from the configured source code management system - sh 'docker system prune --all --volumes --force' // Remove unused Docker resources + checkout scm + sh 'docker system prune --all --volumes --force' } } stage("Switch to Git Branch") { @@ -30,23 +31,26 @@ pipeline { } stage("Build and Run containers") { steps { - // Build and run services using docker-compose with container names including the build number - sh "mkdir -p ${HOME}/variantvalidator_data/seqdata && mkdir -p ${HOME}/variantvalidator_data/logs" + script { + sh """ + mkdir -p ${DATA_VOLUME}/seqdata + mkdir -p ${DATA_VOLUME}/logs - // Add ls commands to check if the directories exist - sh "ls -l ${HOME}/variantvalidator_data" - sh "ls -l ${HOME}/variantvalidator_data/seqdata" - sh "ls -l ${HOME}/variantvalidator_data/logs" + # Add ls commands to check if the directories exist + ls -l ${DATA_VOLUME} + ls -l ${DATA_VOLUME}/seqdata + ls -l ${DATA_VOLUME}/logs - // Create and run the containers - sh 'docker-compose --project-name rest-variantvalidator-ci build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator' - sh 'docker-compose --project-name rest-variantvalidator-ci up -d rv-vvta && docker-compose --project-name rest-variantvalidator-ci up -d rv-vdb && docker-compose --project-name rest-variantvalidator-ci up -d rv-seqrepo && docker-compose --project-name rest-variantvalidator-ci up -d rest-variantvalidator' + # Create and run the containers + docker-compose --project-name rest-variantvalidator-ci build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator + docker-compose --project-name rest-variantvalidator-ci up -d rv-vvta && docker-compose --project-name rest-variantvalidator-ci up -d rv-vdb && docker-compose --project-name rest-variantvalidator-ci up -d rv-seqrepo && docker-compose --project-name rest-variantvalidator-ci up -d rest-variantvalidator + """ + } } } stage("Connect and run Pytest") { steps { script { - // Wait for the PostgreSQL container to be ready def connectionSuccessful = false for (int attempt = 1; attempt <= 5; attempt++) { echo "Attempt $attempt to connect to the database..." @@ -70,14 +74,10 @@ pipeline { } } } - stage("Run Pytest and Codecov") { steps { script { - // Run pytest && Run Codecov with the provided token and branch name sh 'docker-compose exec rest-variantvalidator-${CONTAINER_SUFFIX} pytest --cov=rest_VariantValidator --cov-report=term tests/' - - // Send coverage report to Codecov sh 'docker-compose exec rest-variantvalidator-${CONTAINER_SUFFIX} codecov -t $CODECOV_TOKEN -b ${BRANCH_NAME}' } } @@ -87,7 +87,6 @@ pipeline { post { always { script { - // Cleanup Docker and Docker Compose sh 'docker-compose down -v' sh 'docker network rm $DOCKER_NETWORK' } From ad9ef721321fa14a04854e78ddb0c3f38b528e00 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 8 Dec 2023 11:55:28 +0000 Subject: [PATCH 167/218] Update CI script with correct volume naming --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 7b91573..78c1f2f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -33,8 +33,8 @@ pipeline { steps { script { sh """ - mkdir -p ${DATA_VOLUME}/seqdata - mkdir -p ${DATA_VOLUME}/logs + mkdir -p ${DATA_VOLUME}seqdata + mkdir -p ${DATA_VOLUME}logs # Add ls commands to check if the directories exist ls -l ${DATA_VOLUME} From d9d4d1b1ec731bfb641fb6c831c66076790738ec Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 11 Dec 2023 13:14:13 +0000 Subject: [PATCH 168/218] corect gene2transcriptsv2 --- .../endpoints/variantvalidator_endpoints.py | 4 ++++ tests/test_endpoints.py | 7 ++++++- tests/test_internal_servers.py | 8 ++++---- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 3bbd67c..7c8ba8f 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -170,8 +170,12 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build): # Convert inputs to JSON arrays gene_query = input_formatting.format_input(gene_query) + if isinstance(gene_query, str): + gene_query = [gene_query] limit_transcripts = input_formatting.format_input(limit_transcripts) + print(gene_query) + try: if genome_build not in ["GRCh37", "GRCh38"]: genome_build = None diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 3b0764d..bc73557 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -90,4 +90,9 @@ def test_vv_endpoint_auth_raw_vcf(client): def test_vv_endpoint_auth_all_vcf(client): response = client.get('/VariantValidator/variantvalidator/GRCh38/17-50198002-C-A/auth_raw?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK - assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response \ No newline at end of file + assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + + +def test_vv_g2tv2(client): + response = client.get('/VariantValidator/tools/gene2transcripts_v2/AMPD1/mane/refseq/GRCh38?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK \ No newline at end of file diff --git a/tests/test_internal_servers.py b/tests/test_internal_servers.py index b0750c5..a66e319 100644 --- a/tests/test_internal_servers.py +++ b/tests/test_internal_servers.py @@ -12,26 +12,26 @@ def setUpClass(cls): # Start the WSGI server as a separate process cls.wsgi_server_process = subprocess.Popen(['python', 'rest_VariantValidator/wsgi.py']) - time.sleep(30) + time.sleep(60) # Set the PORT environment variable for APP server os.environ['PORT'] = '5001' # Start the app server as a separate process cls.app_server_process = subprocess.Popen(['python', 'rest_VariantValidator/app.py']) - time.sleep(30) + time.sleep(60) @classmethod def tearDownClass(cls): # Terminate the WSGI server process cls.wsgi_server_process.terminate() - cls.wsgi_server_process.wait(timeout=30) + cls.wsgi_server_process.wait(timeout=60) wsgi_exit_code = cls.wsgi_server_process.poll() print(f"WSGI Server Exit Code: {wsgi_exit_code}") # Terminate the app server process cls.app_server_process.terminate() - cls.app_server_process.wait(timeout=30) + cls.app_server_process.wait(timeout=60) app_exit_code = cls.app_server_process.poll() print(f"App Server Exit Code: {app_exit_code}") From 445f89934e7c26cfbae46a216a64495dc20154dd Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 11 Dec 2023 15:52:54 +0000 Subject: [PATCH 169/218] add a few more asserts to tests --- locust/{locust.py => locustfile.py} | 12 +++++----- locust/test_set.py | 5 ++++- .../endpoints/variantvalidator_endpoints.py | 4 ---- .../utils/input_formatting.py | 6 ++--- tests/test_endpoints.py | 22 ++++++++++++++++++- tests/test_input_formatting.py | 2 +- 6 files changed, 34 insertions(+), 17 deletions(-) rename locust/{locust.py => locustfile.py} (76%) diff --git a/locust/locust.py b/locust/locustfile.py similarity index 76% rename from locust/locust.py rename to locust/locustfile.py index 6a813b6..46fef60 100644 --- a/locust/locust.py +++ b/locust/locustfile.py @@ -3,22 +3,22 @@ class UserBehavior(TaskSet): - @task(2, max_wait=300) + @task(2) def gene2transcripts_v2_task(self): gene_symbol = test_set.gene_list() - url = f"https://rest.variantvalidator.org/VariantValidator/tools/gene2transcripts_v2/{gene_symbol}/mane_select/all/GRCh38?content-type=application%2Fjson" + url = f"/VariantValidator/tools/gene2transcripts_v2/{gene_symbol}/mane/all/GRCh38?content-type=application%2Fjson" self.client.get(url) - @task(1, max_wait=300) + @task(1) def variantvalidator_task(self): variant_id = test_set.variant_list() - url = f"/VariantValidator/variantvalidator/GRCh37/{variant_id}/all?content-type=application/json" + url = f"/VariantValidator/variantvalidator/GRCh37/{variant_id}/mane?content-type=application/json" self.client.get(url) - @task(3, max_wait=300) + @task(3) def additional_task(self): odd_task = test_set.vf_list() - url = f"https://rest.variantvalidator.org/LOVD/lovd/GRCh38/{odd_task}/all/all/False/True?content-type=application%2Fjson" + url = f"/LOVD/lovd/GRCh38/{odd_task}/all/all/False/True?content-type=application%2Fjson" self.client.get(url) diff --git a/locust/test_set.py b/locust/test_set.py index a5553f2..f4ad6db 100644 --- a/locust/test_set.py +++ b/locust/test_set.py @@ -337,7 +337,10 @@ def gene_list(): "COL5A1", "BRCA2", "HBB", - "DMD" + "DMD", + "BRAF", + "HTT", + "NR2E3" ] random_number = random.randint(0, 6) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 7c8ba8f..3bbd67c 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -170,12 +170,8 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build): # Convert inputs to JSON arrays gene_query = input_formatting.format_input(gene_query) - if isinstance(gene_query, str): - gene_query = [gene_query] limit_transcripts = input_formatting.format_input(limit_transcripts) - print(gene_query) - try: if genome_build not in ["GRCh37", "GRCh38"]: genome_build = None diff --git a/rest_VariantValidator/utils/input_formatting.py b/rest_VariantValidator/utils/input_formatting.py index 85f91a0..cf72321 100644 --- a/rest_VariantValidator/utils/input_formatting.py +++ b/rest_VariantValidator/utils/input_formatting.py @@ -20,10 +20,8 @@ def format_input(data_string): for entry in pre_data_list: entry = entry.replace("&", "|") data_list.append(entry) - if len(data_list) == 1: - return str(data_list[0]) - else: - return json.dumps(data_list) + + return json.dumps(data_list) # # Copyright (C) 2016-2023 VariantValidator Contributors diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index bc73557..8b92d69 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -22,6 +22,14 @@ def test_lovd_endpoint(client): response = client.get('/LOVD/lovd/GRCh38/17-50198002-C-A/all/mane/True/False?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + assert "17-50198002-C-A" in response.json.keys() + +def test_lovd_endpoint_multi(client): + response = client.get('/LOVD/lovd/GRCh38/17-50198002-C-A|17-50198002-C-T/all/mane/True/False?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK + assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + assert "17-50198002-C-A" in response.json.keys() + assert "17-50198002-C-T" in response.json.keys() def test_vf_endpoint(client): @@ -36,14 +44,24 @@ def test_vv_endpoint(client): assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response +def test_vv_endpoint_multi(client): + response = client.get('/VariantValidator/variantvalidator/GRCh38/NM_000088.3%3Ac.589G%3ET|NM_000088.3%3Ac.589G%3EA/mane?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK + assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + assert "NM_000088.3:c.589G>T" in response.json.keys() + assert "NM_000088.3:c.589G>A" in response.json.keys() + + def test_g2t_endpoint(client): response = client.get('/VariantValidator/tools/gene2transcripts/COL1A1?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK + assert len(response.json) == 1 def test_g2t2_endpoint(client): response = client.get('/VariantValidator/tools/gene2transcripts_v2/COL1A1%7CCOL1A2%7CCOL5A1/mane/all/GRCh38?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK + assert len(response.json) == 3 def test_h2ref_endpoint(client): @@ -95,4 +113,6 @@ def test_vv_endpoint_auth_all_vcf(client): def test_vv_g2tv2(client): response = client.get('/VariantValidator/tools/gene2transcripts_v2/AMPD1/mane/refseq/GRCh38?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint - assert response.status_code == 200 # Check if the response status code is 200 OK \ No newline at end of file + assert response.status_code == 200 # Check if the response status code is 200 OK + assert len(response.json) == 1 + diff --git a/tests/test_input_formatting.py b/tests/test_input_formatting.py index a4c464b..7b626e2 100644 --- a/tests/test_input_formatting.py +++ b/tests/test_input_formatting.py @@ -33,7 +33,7 @@ def test_pipe_and_special_characters(self): def test_empty_input(self): data_string = '' result = input_formatting.format_input(data_string) - self.assertEqual(result, '') + self.assertEqual(result, '[""]') def test_multiple_pipe_symbols(self): data_string = 'a||b||c' From a885175d4223cb6233e45df37133264dd9ff4f0b Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 11 Dec 2023 19:11:35 +0000 Subject: [PATCH 170/218] correct bug in VV --- .../endpoints/variantvalidator_endpoints.py | 6 ++++++ tests/test_endpoints.py | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 3bbd67c..b835423 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -72,6 +72,10 @@ def get(self, genome_build, variant_description, select_transcripts): # Convert inputs to JSON arrays variant_description = input_formatting.format_input(variant_description) select_transcripts = input_formatting.format_input(select_transcripts) + if select_transcripts == '["all"]': + select_transcripts = "all" + if select_transcripts == '["raw"]': + select_transcripts = "raw" try: # Validate using the VariantValidator Python Library @@ -171,6 +175,8 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build): # Convert inputs to JSON arrays gene_query = input_formatting.format_input(gene_query) limit_transcripts = input_formatting.format_input(limit_transcripts) + if len(limit_transcripts) == 1: + limit_transcripts = limit_transcripts[0] try: if genome_build not in ["GRCh37", "GRCh38"]: diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 8b92d69..6abffd4 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -39,9 +39,10 @@ def test_vf_endpoint(client): def test_vv_endpoint(client): - response = client.get('/VariantValidator/variantvalidator/GRCh38/NM_000088.3%3Ac.589G%3ET/mane?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + response = client.get('/VariantValidator/variantvalidator/GRCh38/NM_000088.3%3Ac.589G%3ET/all?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + assert "NM_000088.3:c.589G>T" in response.json.keys() def test_vv_endpoint_multi(client): From 37a46b2f683538652defc049f748dc4b758886a9 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Tue, 12 Dec 2023 11:31:48 +0000 Subject: [PATCH 171/218] Update handling of all and raw transcripts --- rest_VariantValidator/endpoints/lovd_endpoints.py | 4 ++++ .../endpoints/variantformatter_endpoints.py | 4 ++++ tests/test_endpoints.py | 12 ++++++++++++ 3 files changed, 20 insertions(+) diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index 6836954..ca2ddde 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -82,6 +82,10 @@ def get(self, genome_build, variant_description, transcript_model, select_transc # Convert inputs to JSON arrays variant_description = input_formatting.format_input(variant_description) select_transcripts = input_formatting.format_input(select_transcripts) + if select_transcripts == '["all"]': + select_transcripts = "all" + if select_transcripts == '["raw"]': + select_transcripts = "raw" try: content = simple_formatter.format(variant_description, genome_build, transcript_model, diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index 62be00a..25f0f32 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -62,6 +62,10 @@ def get(self, genome_build, variant_description, transcript_model, select_transc # Convert inputs to JSON arrays variant_description = input_formatting.format_input(variant_description) select_transcripts = input_formatting.format_input(select_transcripts) + if select_transcripts == '["all"]': + select_transcripts = "all" + if select_transcripts == '["raw"]': + select_transcripts = "raw" try: content = simple_formatter.format(variant_description, genome_build, transcript_model, diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 6abffd4..156cbc5 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -117,3 +117,15 @@ def test_vv_g2tv2(client): assert response.status_code == 200 # Check if the response status code is 200 OK assert len(response.json) == 1 +def test_lovd_endpoint_all(client): + response = client.get('/LOVD/lovd/GRCh37/1%3A43815009%3AG%3AT/refseq/all/False/True?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK + assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + assert "1:43815009:G:T" in response.json.keys() + +def test_lovd_endpoint_raw(client): + response = client.get('/LOVD/lovd/GRCh37/1%3A43815009%3AG%3AT/refseq/raw/False/True?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint + assert response.status_code == 200 # Check if the response status code is 200 OK + assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + assert "1:43815009:G:T" in response.json.keys() + From f4e8d43069fef58c439e4a7bdbcc05bf02a135c5 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Tue, 9 Jan 2024 10:35:08 +0000 Subject: [PATCH 172/218] Update Jenkinsfile --- Jenkinsfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Jenkinsfile b/Jenkinsfile index 78c1f2f..f8eb2d3 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -89,6 +89,7 @@ pipeline { script { sh 'docker-compose down -v' sh 'docker network rm $DOCKER_NETWORK' + sh 'docker system prune --all --volumes --force' } } } From 69bb9cfb52d86ac43a840f7475d89ee3732008de Mon Sep 17 00:00:00 2001 From: John-F-Wagstaff Date: Mon, 8 Jan 2024 13:12:07 +0000 Subject: [PATCH 173/218] Add dummy auth fallback for later use This dummy auth allows us to include the same function decorators and switch the auth/no auth status depending on circumstances. --- .../utils/verify_password.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 rest_VariantValidator/utils/verify_password.py diff --git a/rest_VariantValidator/utils/verify_password.py b/rest_VariantValidator/utils/verify_password.py new file mode 100644 index 0000000..ee2fd63 --- /dev/null +++ b/rest_VariantValidator/utils/verify_password.py @@ -0,0 +1,34 @@ +from flask_httpauth import MultiAuth +from datetime import datetime +from flask import g +from functools import wraps + +# Unified verification decorator, dummy for non auth versions of the api +class DummyAuth(): + def login_required(self,null=None): + def login(f): + @wraps(f) + def wrapper(*args, **kwargs): + return f(*args, **kwargs) + return wrapper + return login + +auth = DummyAuth() + +# +# Copyright (C) 2016-2022 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + From 0679769b2a43ee6d27a71cfaf7c25aa0233531ae Mon Sep 17 00:00:00 2001 From: John-F-Wagstaff Date: Mon, 8 Jan 2024 13:16:35 +0000 Subject: [PATCH 174/218] Add optional authentication to the endpoints This depends on the presence of the dummy auth endpoint and authentication is optional depending on the presence or absence of the authentication module. We may change this to use the settings file later. Tested in the null auth state. --- rest_VariantValidator/endpoints/lovd_endpoints.py | 6 ++++++ .../endpoints/variantformatter_endpoints.py | 6 ++++++ .../endpoints/variantvalidator_endpoints.py | 7 +++++++ 3 files changed, 19 insertions(+) diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index ca2ddde..f8c1f9d 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -3,6 +3,11 @@ from flask_restx import Namespace, Resource from rest_VariantValidator.utils import request_parser, representations, input_formatting from rest_VariantValidator.utils.object_pool import simple_variant_formatter_pool +# get login authentication, if needed, or dummy auth if not present +try: + from VariantValidator_APIs.db_auth.verify_password import auth +except ModuleNotFoundError: + from rest_VariantValidator.utils.verify_password import auth def ordereddict_to_dict(value): @@ -62,6 +67,7 @@ def ordereddict_to_dict(value): class LOVDClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) + @auth.login_required() def get(self, genome_build, variant_description, transcript_model, select_transcripts, checkonly, liftover): if transcript_model == 'None' or transcript_model == 'none': transcript_model = None diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index 25f0f32..1ea2859 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -2,6 +2,11 @@ from flask_restx import Namespace, Resource from rest_VariantValidator.utils import request_parser, representations, input_formatting from rest_VariantValidator.utils.object_pool import simple_variant_formatter_pool +# get login authentication, if needed, or dummy auth if not present +try: + from VariantValidator_APIs.db_auth.verify_password import auth +except ModuleNotFoundError: + from rest_VariantValidator.utils.verify_password import auth """ Create a parser object locally @@ -46,6 +51,7 @@ class VariantFormatterClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) + @auth.login_required() def get(self, genome_build, variant_description, transcript_model, select_transcripts, checkonly): if transcript_model == 'None' or transcript_model == 'none': transcript_model = None diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index b835423..0aafefb 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -2,6 +2,12 @@ from flask_restx import Namespace, Resource from rest_VariantValidator.utils import exceptions, request_parser, representations, input_formatting from rest_VariantValidator.utils.object_pool import vval_object_pool, g2t_object_pool +# get login authentication, if needed, or dummy auth if not present +try: + from VariantValidator_APIs.db_auth.verify_password import auth +except ModuleNotFoundError: + from rest_VariantValidator.utils.verify_password import auth + """ Create a parser object locally @@ -50,6 +56,7 @@ class VariantValidatorClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) + @auth.login_required() def get(self, genome_build, variant_description, select_transcripts): # Import object from vval pool From 7d4f5baae91dcae7a6dc205a5d3b4695dc53889c Mon Sep 17 00:00:00 2001 From: John-F-Wagstaff Date: Mon, 8 Jan 2024 13:41:35 +0000 Subject: [PATCH 175/218] Add documentation to endpoints for auth Documentation triggers when auth is loaded, but non when it is not available. Tested in both the auth present and the null auth state. --- rest_VariantValidator/endpoints/__init__.py | 35 ++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/rest_VariantValidator/endpoints/__init__.py b/rest_VariantValidator/endpoints/__init__.py index 0e68684..1bcb2bb 100644 --- a/rest_VariantValidator/endpoints/__init__.py +++ b/rest_VariantValidator/endpoints/__init__.py @@ -7,6 +7,37 @@ from .variantformatter_endpoints import api as ns_vf from .lovd_endpoints import api as ns_lovd from .hello import api as ns_hello +#attempt to pull in and use/document auth api if it is available +try: + + from VariantValidator_APIs.db_auth.auth_endpoints import auth_api as ns_auth + # Set auth for API + authorizations = { + 'apikey': { + 'type': 'apiKey', + 'in': 'header', + 'name': 'Authorization' + }, + 'basic_pwd': { + 'type': 'basic', + 'name': 'VV_API_PWD' + } + } + security = ['apikey', 'basic_pwd'] + sec_descripton = ''' +## Security +For now the Swagger documented endpoints retain the last entered login even on +page refresh, at least on some browsers, to "log out" please enter a trivial +invalid login e.g. username:none password: none to overwrite this. + +For logging in via a token please prefix your token with "Bearer " (including +the space). +''' +except ModuleNotFoundError: + ns_auth = None + authorizations = None + security = None + sec_descripton ='' # Obtain VariantValidator related metadata vval = VariantValidator.Validator() @@ -43,7 +74,9 @@ def specs_url(self): " - [VVTA](https://www528.lamp.le.ac.uk/) release " + config_dict['vvta_version'] + "\n" " - [vvSeqRepo](https://www528.lamp.le.ac.uk/) release " - + config_dict['vvseqrepo_db'].split('/')[-2] + + config_dict['vvseqrepo_db'].split('/')[-2] + sec_descripton, + authorizations=authorizations, + security=security ) # Add the namespaces to the API From 9f339e2bd6c7ac04c40437de17d1977f67cc4013 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 18 Jan 2024 12:09:54 +0000 Subject: [PATCH 176/218] Update to use dummy auth code --- bin/batch_validator.py | 2 +- bin/update_vdb.py | 2 +- bin/variant_validator.py | 2 +- bin/vv_configure.py | 2 +- configuration/docker.ini | 2 +- docs/DOCKER.md | 13 ++----------- locust/locustfile.py | 2 +- pyproject.toml | 1 + rest_VariantValidator/__init__.py | 2 +- rest_VariantValidator/app.py | 2 +- rest_VariantValidator/endpoints/__init__.py | 2 +- rest_VariantValidator/endpoints/hello.py | 2 +- rest_VariantValidator/endpoints/lovd_endpoints.py | 2 +- .../endpoints/variantformatter_endpoints.py | 2 +- rest_VariantValidator/utils/exceptions.py | 2 +- rest_VariantValidator/utils/input_formatting.py | 2 +- rest_VariantValidator/utils/object_pool.py | 2 +- rest_VariantValidator/utils/representations.py | 2 +- rest_VariantValidator/utils/request_parser.py | 2 +- rest_VariantValidator/utils/verify_password.py | 11 +++++------ 20 files changed, 25 insertions(+), 34 deletions(-) diff --git a/bin/batch_validator.py b/bin/batch_validator.py index 26bf25f..3ea61b7 100644 --- a/bin/batch_validator.py +++ b/bin/batch_validator.py @@ -84,7 +84,7 @@ outfile.close() # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/bin/update_vdb.py b/bin/update_vdb.py index 6abd1c2..df31d3b 100644 --- a/bin/update_vdb.py +++ b/bin/update_vdb.py @@ -16,7 +16,7 @@ update_vv_db.update() # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/bin/variant_validator.py b/bin/variant_validator.py index cb831f9..7803b3a 100644 --- a/bin/variant_validator.py +++ b/bin/variant_validator.py @@ -54,7 +54,7 @@ def output_results(valoutput, outformat, with_meta): args.output.write(output_results(output, args.output_format, args.meta) + '\n') # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/bin/vv_configure.py b/bin/vv_configure.py index 941d59c..26552c7 100644 --- a/bin/vv_configure.py +++ b/bin/vv_configure.py @@ -59,7 +59,7 @@ def read_settings(): config.write(fh) # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/configuration/docker.ini b/configuration/docker.ini index ed6a703..525c405 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -32,7 +32,7 @@ api_key = OPTIONAL # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/docs/DOCKER.md b/docs/DOCKER.md index ba5a287..b7c723d 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -39,15 +39,9 @@ VariantValidator [manual](https://github.com/openvar/variantValidator/blob/maste ## Build the container -*Note: some of these steps take >>1hr to complete depending on the speed of your internet connection, particularly +*Note: some of these steps take ~1hr to complete depending on the speed of your internet connection, particularly compiling SeqRepo* -- Pull images - -```bash -$ docker-compose pull -``` - #### Build and startup procedure rest_VariantValidator can be built in Production mode and Development mode. Development mode mounts the root directory @@ -67,7 +61,7 @@ $ docker-compose build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidato - Development and testing build ```bash # Build -$ docker-compose -f docker-compose.yml -f docker-compose-dev.yml build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator +$ docker-compose -f docker-compose.yml -f docker-compose-dev.yml build --no-cache rv-vvta rv-vdb rv-seqrepo dev-mode ``` - The build stage has completed when you see the following message or something similar ``` @@ -87,9 +81,6 @@ $ docker-compose up -d rv-vvta && \ ``` - Or for a development and testing build, swap for these commands -``` -*i.e.,* a directory called `variantvalidator_data` in your `home` directory with sub-directories `seqdata` and `logs` - ```bash $ docker-compose up -d rv-vvta && \ docker-compose up -d rv-vdb && \ diff --git a/locust/locustfile.py b/locust/locustfile.py index 46fef60..85121f6 100644 --- a/locust/locustfile.py +++ b/locust/locustfile.py @@ -27,7 +27,7 @@ class WebsiteUser(HttpUser): wait_time = between(1, 10) # seconds # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/pyproject.toml b/pyproject.toml index eeef8dc..69e464a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ dependencies = [ "Werkzeug==2.3.7", "MarkupSafe==2.1.3", "flask-cors==4.0.0", + "flask_httpauth==4.8.0", # Dependencies from other repositories, specified with their repository URLs and package names "vvhgvs@git+https://github.com/openvar/vv_hgvs@master", diff --git a/rest_VariantValidator/__init__.py b/rest_VariantValidator/__init__.py index cbf88ab..09f6c68 100644 --- a/rest_VariantValidator/__init__.py +++ b/rest_VariantValidator/__init__.py @@ -13,7 +13,7 @@ __version__ = None # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 0a558cc..d9fc7ca 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -175,7 +175,7 @@ def default_error_handler(e): # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/__init__.py b/rest_VariantValidator/endpoints/__init__.py index 1bcb2bb..17c4768 100644 --- a/rest_VariantValidator/endpoints/__init__.py +++ b/rest_VariantValidator/endpoints/__init__.py @@ -87,7 +87,7 @@ def specs_url(self): # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py index 348c4b7..10252d6 100644 --- a/rest_VariantValidator/endpoints/hello.py +++ b/rest_VariantValidator/endpoints/hello.py @@ -78,7 +78,7 @@ def get(self, error_code): # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index f8c1f9d..d0d59c7 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -123,7 +123,7 @@ def get(self, genome_build, variant_description, transcript_model, select_transc # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index 1ea2859..274863e 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -97,7 +97,7 @@ def get(self, genome_build, variant_description, transcript_model, select_transc return content # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/utils/exceptions.py b/rest_VariantValidator/utils/exceptions.py index 07c5179..dcb5e7c 100644 --- a/rest_VariantValidator/utils/exceptions.py +++ b/rest_VariantValidator/utils/exceptions.py @@ -2,7 +2,7 @@ class RemoteConnectionError(Exception): code = 504 # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/utils/input_formatting.py b/rest_VariantValidator/utils/input_formatting.py index cf72321..120f9cf 100644 --- a/rest_VariantValidator/utils/input_formatting.py +++ b/rest_VariantValidator/utils/input_formatting.py @@ -24,7 +24,7 @@ def format_input(data_string): return json.dumps(data_list) # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/utils/object_pool.py b/rest_VariantValidator/utils/object_pool.py index 601f20e..9d4a872 100644 --- a/rest_VariantValidator/utils/object_pool.py +++ b/rest_VariantValidator/utils/object_pool.py @@ -53,7 +53,7 @@ def return_object(self, obj): simple_variant_formatter_pool = SimpleVariantFormatterPool(initial_pool_size=3, max_pool_size=5) # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/utils/representations.py b/rest_VariantValidator/utils/representations.py index e1ebcc1..7761b9f 100644 --- a/rest_VariantValidator/utils/representations.py +++ b/rest_VariantValidator/utils/representations.py @@ -26,7 +26,7 @@ def application_json(data, code, headers): return resp # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/utils/request_parser.py b/rest_VariantValidator/utils/request_parser.py index 2b7ad94..350a03d 100644 --- a/rest_VariantValidator/utils/request_parser.py +++ b/rest_VariantValidator/utils/request_parser.py @@ -11,7 +11,7 @@ # -# Copyright (C) 2016-2023 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/utils/verify_password.py b/rest_VariantValidator/utils/verify_password.py index ee2fd63..c86316e 100644 --- a/rest_VariantValidator/utils/verify_password.py +++ b/rest_VariantValidator/utils/verify_password.py @@ -1,10 +1,8 @@ -from flask_httpauth import MultiAuth -from datetime import datetime -from flask import g from functools import wraps -# Unified verification decorator, dummy for non auth versions of the api -class DummyAuth(): + +class DummyAuth: + # Unified verification decorator, dummy for non auth versions of the api def login_required(self,null=None): def login(f): @wraps(f) @@ -13,10 +11,11 @@ def wrapper(*args, **kwargs): return wrapper return login + auth = DummyAuth() # -# Copyright (C) 2016-2022 VariantValidator Contributors +# Copyright (C) 2016-2024 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as From ddfe21dddfe11d15ab9dc1ac64207735121bc6f8 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 1 Feb 2024 10:10:55 +0000 Subject: [PATCH 177/218] Few tweaks to docker documentation --- docs/DOCKER.md | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index b7c723d..8e7dcf1 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -88,12 +88,12 @@ $ docker-compose up -d rv-vvta && \ docker-compose -f docker-compose.yml -f docker-compose-dev.yml up -d dev-mode ``` -- In both cases containers are started and running when you see messages similar to +- You may need to run the above command a second time, until you see a response similar to ```bash -Creating rest_variantvalidator_rv-vvta_1 ... done -Creating rest_variantvalidator_rv-vdb_1 ... done -Creating rest_variantvalidator_rv-seqrepo_1 ... done -Creating rest_variantvalidator_rest-variantvalidator_1 ... done + ✔ Container rest_variantvalidator-rv-vvta-1 Running 0.0s + ✔ Container rest_variantvalidator-rv-seqrepo-1 Running 0.0s + ✔ Container rest_variantvalidator-rv-vdb-1 Running 0.0s + ✔ Container rest_variantvalidator-dev-mode-1 Running ``` ### Test the build @@ -308,6 +308,19 @@ $ docker-compose down $ docker system prune -a --volumes ``` +Run the following command to make sure all rest_validator containers are stopped +```bash +$ docker ps +``` + +Stop any individual containers by running + +```bash +$ docker stop +``` + +Then re-run the docker system prune command + ***If you choose this option, make sure you see the container restvv being re-created and all Python packages being reinstalled in the printed logs, otherwise the container may not actually be rebuilt and the contained modules may not update*** From d54011717d6beaa02d318eaad5104fa9881478bd Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 2 Feb 2024 15:34:29 +0000 Subject: [PATCH 178/218] Update to jenkinsfile --- Jenkinsfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index f8eb2d3..9ec4828 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -41,6 +41,9 @@ pipeline { ls -l ${DATA_VOLUME}/seqdata ls -l ${DATA_VOLUME}/logs + echo "Contents of ${DATA_VOLUME}:" + ls -lR ${DATA_VOLUME} + # Create and run the containers docker-compose --project-name rest-variantvalidator-ci build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator docker-compose --project-name rest-variantvalidator-ci up -d rv-vvta && docker-compose --project-name rest-variantvalidator-ci up -d rv-vdb && docker-compose --project-name rest-variantvalidator-ci up -d rv-seqrepo && docker-compose --project-name rest-variantvalidator-ci up -d rest-variantvalidator From abfce14700b9fc746e3a10a619b1983b1bb1d55c Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 2 Feb 2024 15:44:55 +0000 Subject: [PATCH 179/218] Update to jenkinsfile --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 9ec4828..f2d3497 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -38,8 +38,8 @@ pipeline { # Add ls commands to check if the directories exist ls -l ${DATA_VOLUME} - ls -l ${DATA_VOLUME}/seqdata - ls -l ${DATA_VOLUME}/logs + ls -l ${DATA_VOLUME}seqdata + ls -l ${DATA_VOLUME}logs echo "Contents of ${DATA_VOLUME}:" ls -lR ${DATA_VOLUME} From b079c4c7470fd6ef68bc19dbd397ca8b14a91c9a Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 16 Feb 2024 12:27:35 +0000 Subject: [PATCH 180/218] Bug fix issue https://github.com/openvar/variantValidator/issues/579 --- .../endpoints/lovd_endpoints.py | 4 ++-- .../endpoints/variantvalidator_endpoints.py | 20 ++++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index d0d59c7..1081f55 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -44,8 +44,8 @@ def ordereddict_to_dict(value): "> None or all (all transcripts at the latest versions)\n" "> raw (all transcripts all version)\n" "> select (select transcripts)\n" - "> mane (MANE select transcripts)\n" - "> mane_select (MANE select and MANE Plus Clinical transcripts)\n" + "> mane (MANE Select transcripts)\n" + "> mane_select (MANE Select and MANE Plus Clinical transcripts)\n" "\n***Single***\n" "> NM_000093.4\n" "\n***Multiple***\n" diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 0aafefb..56bd22d 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -110,9 +110,13 @@ def get(self, genome_build, variant_description, select_transcripts): @api.route("/tools/gene2transcripts/") -@api.param("gene_query", "***HGNC gene symbol or transcript ID***\n" +@api.param("gene_query", "***HGNC gene symbol, HGNC ID, or transcript ID***\n" "\nCurrent supported transcript IDs" - "\n- RefSeq") + "\n- RefSeq\n" + "\n***Single***\n" + "> COL1A1\n" + "> HGNC:2197\n" + "> NM_000088.4\n") class Gene2transcriptsClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) @@ -124,8 +128,10 @@ def get(self, gene_query): # Convert inputs to JSON arrays gene_query = input_formatting.format_input(gene_query) + print(gene_query) + try: - content = vval.gene2transcripts(gene_query) + content = vval.gene2transcripts(gene_query)[0] except ConnectionError: message = "Cannot connect to rest.genenames.org, please try again later" g2t_object_pool.return_object(vval) @@ -133,6 +139,8 @@ def get(self, gene_query): finally: g2t_object_pool.return_object(vval) + print(content) + # Collect Arguments args = parser.parse_args() @@ -152,11 +160,13 @@ def get(self, gene_query): "") @api.param("gene_query", "***HGNC gene symbol, HGNC ID, or transcript ID***\n" "\nCurrent supported transcript IDs" - "\n- RefSeq or Ensembl""\n" + "\n- RefSeq or Ensembl\n" "\n***Single***\n" "> COL1A1\n" + "> HGNC:2197\n" + "> NM_000088.4\n" "\n***Multiple***\n" - "> COL1A1|COL1A2|COL5A1\n") + "> COL1A1|COL1A2|COL5A1|HGNC:2197\n") @api.param("limit_transcripts", "***Return all possible transcripts***\n" "> False\n" "\n***Single***\n" From 2a386b772ca8572bd75bc26f19b311ed8a8edab1 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 16 Feb 2024 15:02:45 +0000 Subject: [PATCH 181/218] Tweaks to docs and genes2transcript V1 --- .../endpoints/variantvalidator_endpoints.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 56bd22d..bb2c69c 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -128,8 +128,6 @@ def get(self, gene_query): # Convert inputs to JSON arrays gene_query = input_formatting.format_input(gene_query) - print(gene_query) - try: content = vval.gene2transcripts(gene_query)[0] except ConnectionError: @@ -139,8 +137,6 @@ def get(self, gene_query): finally: g2t_object_pool.return_object(vval) - print(content) - # Collect Arguments args = parser.parse_args() @@ -202,7 +198,7 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build): limit_transcripts = None content = vval.gene2transcripts(gene_query, select_transcripts=limit_transcripts, transcript_set=transcript_set, genome_build=genome_build, - batch_output=True) + batch_output=True, validator=vval) except ConnectionError: message = "Cannot connect to rest.genenames.org, please try again later" g2t_object_pool.return_object(vval) From f6b846f1b22deea6a6340cc6e507dfc098aef5cd Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 21 Feb 2024 10:59:27 +0000 Subject: [PATCH 182/218] update tests --- tests/test_endpoints.py | 2 +- tests/test_internal_servers.py | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 156cbc5..8265df0 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -56,7 +56,7 @@ def test_vv_endpoint_multi(client): def test_g2t_endpoint(client): response = client.get('/VariantValidator/tools/gene2transcripts/COL1A1?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK - assert len(response.json) == 1 + assert len(response.json) == 6 def test_g2t2_endpoint(client): diff --git a/tests/test_internal_servers.py b/tests/test_internal_servers.py index a66e319..af246ab 100644 --- a/tests/test_internal_servers.py +++ b/tests/test_internal_servers.py @@ -8,19 +8,27 @@ class TestInternalServers(TestCase): @classmethod def setUpClass(cls): # Set the PORT environment variable for WSGI server - os.environ['PORT'] = '8001' + os.environ['PORT'] = '8002' # Start the WSGI server as a separate process cls.wsgi_server_process = subprocess.Popen(['python', 'rest_VariantValidator/wsgi.py']) time.sleep(60) + # Check if the WSGI server started correctly + if cls.wsgi_server_process.poll() is not None: + raise RuntimeError('WSGI server failed to start') + # Set the PORT environment variable for APP server - os.environ['PORT'] = '5001' + os.environ['PORT'] = '5002' # Start the app server as a separate process cls.app_server_process = subprocess.Popen(['python', 'rest_VariantValidator/app.py']) time.sleep(60) + # Check if the app server started correctly + if cls.app_server_process.poll() is not None: + raise RuntimeError('App server failed to start') + @classmethod def tearDownClass(cls): # Terminate the WSGI server process @@ -58,8 +66,9 @@ def check_server(self, endpoint, port): def test_wsgi_internal_server(self): # Check the WSGI server - self.check_server('hello', 8001) + self.check_server('hello', 8002) def test_app_server(self): # Check the app server - self.check_server('hello', 5001) + self.check_server('hello', 5002) + From 2332853a737e9a27bc0ff766932b4d9e6ea864a9 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 21 Feb 2024 15:14:30 +0000 Subject: [PATCH 183/218] upgrades to JenkinsFile --- Jenkinsfile | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index f2d3497..7f1284c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,3 +1,13 @@ +Sure, I can help you modify your `Jenkinsfile` to include some additional steps. Here's a step-by-step plan: + +1. Add a step to check if the directory `/root/variantvalidator_data/seqdata` exists. If it doesn't, create it. +2. Add a step to check if the directory `/root/variantvalidator_data/logs` exists. If it doesn't, create it. +3. Add a step to list the contents of the `/root/variantvalidator_data/` directory to verify that the directories were created successfully. +4. Continue with the existing steps to build and run the Docker containers. + +Here's the modified `Jenkinsfile`: + +```groovy pipeline { agent { docker { @@ -29,22 +39,27 @@ pipeline { sh 'apk update && apk add docker-compose' } } - stage("Build and Run containers") { + stage("Check and Create Directories") { steps { script { sh """ - mkdir -p ${DATA_VOLUME}seqdata - mkdir -p ${DATA_VOLUME}logs + if [ ! -d "${DATA_VOLUME}seqdata" ]; then + mkdir -p ${DATA_VOLUME}seqdata + fi - # Add ls commands to check if the directories exist - ls -l ${DATA_VOLUME} - ls -l ${DATA_VOLUME}seqdata - ls -l ${DATA_VOLUME}logs - - echo "Contents of ${DATA_VOLUME}:" - ls -lR ${DATA_VOLUME} + if [ ! -d "${DATA_VOLUME}logs" ]; then + mkdir -p ${DATA_VOLUME}logs + fi - # Create and run the containers + ls -l ${DATA_VOLUME} + """ + } + } + } + stage("Build and Run containers") { + steps { + script { + sh """ docker-compose --project-name rest-variantvalidator-ci build --no-cache rv-vvta rv-vdb rv-seqrepo rest-variantvalidator docker-compose --project-name rest-variantvalidator-ci up -d rv-vvta && docker-compose --project-name rest-variantvalidator-ci up -d rv-vdb && docker-compose --project-name rest-variantvalidator-ci up -d rv-seqrepo && docker-compose --project-name rest-variantvalidator-ci up -d rest-variantvalidator """ @@ -97,3 +112,6 @@ pipeline { } } } +``` + +This script checks if the directories exist and creates them if they don't. It then lists the contents of the `/root/variantvalidator_data/` directory to verify that the directories were created successfully. The rest of the script is the same as your original script. \ No newline at end of file From 32d9b9d61a601a5b19771404bdb5ec5def34a168 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 15 Mar 2024 15:46:21 +0000 Subject: [PATCH 184/218] add rate limiting --- locust/locustfile.py | 2 +- pyproject.toml | 3 +- rest_VariantValidator/app.py | 127 +++++++++++------- rest_VariantValidator/endpoints/hello.py | 10 +- .../endpoints/lovd_endpoints.py | 3 + .../endpoints/variantformatter_endpoints.py | 3 + .../endpoints/variantvalidator_endpoints.py | 11 ++ rest_VariantValidator/utils/limiter.py | 21 +++ rest_VariantValidator/wsgi.py | 17 +++ tests/test_endpoints.py | 3 +- tests/test_handlers.py | 27 ++-- 11 files changed, 159 insertions(+), 68 deletions(-) create mode 100644 rest_VariantValidator/utils/limiter.py diff --git a/locust/locustfile.py b/locust/locustfile.py index 85121f6..33ad805 100644 --- a/locust/locustfile.py +++ b/locust/locustfile.py @@ -24,7 +24,7 @@ def additional_task(self): class WebsiteUser(HttpUser): tasks = [UserBehavior] - wait_time = between(1, 10) # seconds + wait_time = between(0.1, 2) # seconds # # Copyright (C) 2016-2024 VariantValidator Contributors diff --git a/pyproject.toml b/pyproject.toml index 69e464a..b4074d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,8 @@ dependencies = [ "Werkzeug==2.3.7", "MarkupSafe==2.1.3", "flask-cors==4.0.0", - "flask_httpauth==4.8.0", + "flask_httpauth==4.8.0", + "flask_limiter==3.5.1", # Dependencies from other repositories, specified with their repository URLs and package names "vvhgvs@git+https://github.com/openvar/vv_hgvs@master", diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index d9fc7ca..c693ecf 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -1,7 +1,3 @@ -""" -Simple rest interface for VariantValidator built using Flask Flask-RESTPlus and Swagger UI -""" - # Import modules from flask import Flask, request from rest_VariantValidator.endpoints import api @@ -14,6 +10,8 @@ import logging.config from configparser import ConfigParser from VariantValidator import settings as vv_settings +from rest_VariantValidator.utils.limiter import limiter +from werkzeug.exceptions import InternalServerError, Forbidden, TooManyRequests # Set document root ROOT = os.path.dirname(os.path.abspath(__file__)) @@ -29,15 +27,10 @@ """ if config['logging'].getboolean('log') is True: logger = logging.getLogger('rest_VariantValidator') - # We are setting 2 types of logging. To screen at the level DEBUG console_level = config['logging']['console'].upper() log_console_level = logging.getLevelName(console_level) logger.setLevel(log_console_level) - # We will also log to a file - # Log with a rotating file-handler - # This sets the maximum size of the log to 0.5Mb and allows two additional logs - # The logs are then deleted and replaced in rotation try: parent = config['logging']['file_path'] except KeyError: @@ -46,23 +39,21 @@ maxBytes=500000, backupCount=2) - # We want to minimise the amount of information we log to capturing bugs file_level = config['logging']['file'].upper() log_file_level = logging.getLevelName(file_level) logHandler.setLevel(log_file_level) logger.addHandler(logHandler) - """ Create a parser object locally """ parser = request_parser.parser -# Define the application as a Flask app with the name defined by __name__ (i.e. the name of the current module) -# Most tutorials define application as "app", but I have had issues with this when it comes to deployment, -# so application is recommended +# Define the application as a Flask app with the name defined by __name__ application = Flask(__name__) +# Create a limiter instance and attach it to your Flask application +limiter.init_app(application) api.init_app(application) # By default, show all endpoints (collapsed) @@ -71,20 +62,9 @@ # enable CORS CORS(application, resources={r'/*': {'origins': '*'}}) - """ Representations - - Adds a response-type into the "Response content type" drop-down menu displayed in Swagger - - When selected, the APP will return the correct response-header and content type - - The default for flask-RESTPlus is application/json - -Note - - The decorators are assigned to the functions """ -# Add additional representations using the @api.representation decorator -# Requires the module make_response from flask and dict-to-xml - - @api.representation('text/xml') def application_xml(data, code, headers): resp = representations.xml(data, code, headers) @@ -98,29 +78,21 @@ def application_json(data, code, headers): resp.headers['Content-Type'] = 'application/json' return resp - """ Error handlers - - exceptions has now been imported from utils! """ -# Simple function that creates an error message that we will log def log_exception(exception_type): - # We want to know the arguments passed and the path so we can replicate the error params = dict(request.args) params['path'] = request.path - # Create the message and log - message = '%s occurred at %s with params=%s' % (exception_type, time.ctime(), params) + message = '%s occurred at %s with params=%s\n' % (exception_type, time.ctime(), params) logger.exception(message, exc_info=True) @application.errorhandler(exceptions.RemoteConnectionError) def remote_connection_error_handler(e): - # Add the Exception to the log ensuring that exc_info is True so that a traceback is also logged - log_exception('RemoteConnectionError') - - # Collect Arguments + log_exception('ApplicationRemoteConnectionError') args = parser.parse_args() if args['content-type'] != 'text/xml': return application_json({'message': str(e)}, @@ -134,45 +106,96 @@ def remote_connection_error_handler(e): @application.errorhandler(404) def not_found_error_handler(e): - # Add the Exception to the log ensuring that exc_info is True so that a traceback is also logged - log_exception('NotFoundError') - - # Collect Arguments + log_exception('ApplicationNotFoundError') + logger.warning(str(e)) args = parser.parse_args() if args['content-type'] != 'text/xml': - return application_json({'message': 'Requested Endpoint not found'}, + return application_json({'message': 'Requested Endpoint not found: See the documentation at https://rest.variantvalidator.org'}, 404, None) else: - return application_xml({'message': 'Requested Endpoint not found'}, + return application_xml({'message': 'Requested Endpoint not found: See the documentation at https://rest.variantvalidator.org'}, 404, None) @application.errorhandler(500) -def default_error_handler(e): - # Add the Exception to the log ensuring that exc_info is True so that a traceback is also logged - log_exception('InternalServerError') - - # Collect Arguments +def internal_server_error_handler(e): + log_exception('ApplicationInternalServerError') args = parser.parse_args() if args['content-type'] != 'text/xml': - return application_json({'message': 'unhandled error: contact https://variantvalidator.org/contact_admin/'}, + return application_json({'message': 'Unhandled error: contact https://variantvalidator.org/contact_admin/'}, 500, None) else: - return application_xml({'message': 'unhandled error: contact https://variantvalidator.org/contact_admin/'}, + return application_xml({'message': 'Unhandled error: contact https://variantvalidator.org/contact_admin/'}, 500, None) +@application.errorhandler(429) +def too_many_requests_error_handler(e): + log_exception('ApplicationTooManyRequestsError') + args = parser.parse_args() + if args['content-type'] != 'text/xml': + return application_json({'message': 'Rate limit hit for this endpoint: See the endpoint documentation at https://rest.variantvalidator.org'}, + 429, + None) + else: + return application_xml({'message': 'Rate limit hit for this endpoint: See the endpoint documentation at https://rest.variantvalidator.org'}, + 429, + None) + + +@application.errorhandler(403) +def forbidden_error_handler(e): + log_exception('ApplicationForbiddenError') + args = parser.parse_args() + if args['content-type'] != 'text/xml': + return application_json({'message': 'Forbidden: You do not have the necessary permissions.'}, + 403, + None) + else: + return application_xml({'message': 'Forbidden: You do not have the necessary permissions.'}, + 403, + None) + + +@api.errorhandler(InternalServerError) +def api_internal_server_error_handler(e): + log_exception('APIInternalServerError') + args = parser.parse_args() + if args['content-type'] != 'text/xml': + return {'message': 'Unhandled error: contact https://variantvalidator.org/contact_admin/'}, 500, {'Content-Type': 'application/json'} + else: + return {'message': 'Unhandled error: contact https://variantvalidator.org/contact_admin/'}, 500, {'Content-Type': 'text/xml'} + + +@api.errorhandler(TooManyRequests) +def api_too_many_requests_error_handler(e): + log_exception('APITooManyRequestsError') + args = parser.parse_args() + if args['content-type'] != 'text/xml': + return {'message': 'Rate limit hit for this endpoint: See the endpoint documentation at https://rest.variantvalidator.org'}, 429, {'Content-Type': 'application/json'} + else: + return {'message': 'Rate limit hit for this endpoint: See the endpoint documentation at https://rest.variantvalidator.org'}, 429, {'Content-Type': 'text/xml'} + + +@api.errorhandler(Forbidden) +def api_forbidden_error_handler(e): + log_exception('APIForbiddenError') + args = parser.parse_args() + if args['content-type'] != 'text/xml': + return {'message': 'Forbidden: You do not have the necessary permissions.'}, 403, {'Content-Type': 'application/json'} + else: + return {'message': 'Forbidden: You do not have the necessary permissions.'}, 403, {'Content-Type': 'text/xml'} + + # Allows app to be run in debug mode if __name__ == '__main__': - # Read the port from the environment variable, defaulting to 8000 if not set port = int(os.environ.get('PORT', 5000)) - application.debug = True # Enable debugging mode - application.run(host="127.0.0.1", port=port) # Specify a host and port fot the app - + application.debug = True + application.run(host="127.0.0.1", port=port) # # Copyright (C) 2016-2024 VariantValidator Contributors diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py index 10252d6..f8eb63c 100644 --- a/rest_VariantValidator/endpoints/hello.py +++ b/rest_VariantValidator/endpoints/hello.py @@ -65,13 +65,15 @@ class ExceptionClass(Resource): @api.expect(parser, validate=True) def get(self, error_code): if error_code == 400: - abort(400, "Bad Request") + abort(400) elif error_code == 403: - abort(403, "Forbidden") + abort(403) elif error_code == 404: - abort(404, "Not Found") + abort(404) elif error_code == 500: - abort(500, "Internal Server Error") + abort(500) + elif error_code == 429: + abort(429) elif error_code == 999: raise exceptions.RemoteConnectionError('https://rest.variantvalidator.org/variantvalidator currently ' 'unavailable') diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index 1081f55..792c6c5 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -3,6 +3,7 @@ from flask_restx import Namespace, Resource from rest_VariantValidator.utils import request_parser, representations, input_formatting from rest_VariantValidator.utils.object_pool import simple_variant_formatter_pool +from rest_VariantValidator.utils.limiter import limiter # get login authentication, if needed, or dummy auth if not present try: from VariantValidator_APIs.db_auth.verify_password import auth @@ -27,6 +28,7 @@ def ordereddict_to_dict(value): @api.route("/lovd////" "//") +@api.doc(description="This endpoint has a rate limit of 4 requests per second.") @api.param("variant_description", "***Genomic HGVS***\n" "> - NC_000017.10:g.48275363C>A\n" "\n***Pseudo-VCF***\n" @@ -68,6 +70,7 @@ class LOVDClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) @auth.login_required() + @limiter.limit("4/second") def get(self, genome_build, variant_description, transcript_model, select_transcripts, checkonly, liftover): if transcript_model == 'None' or transcript_model == 'none': transcript_model = None diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index 274863e..68c542f 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -2,6 +2,7 @@ from flask_restx import Namespace, Resource from rest_VariantValidator.utils import request_parser, representations, input_formatting from rest_VariantValidator.utils.object_pool import simple_variant_formatter_pool +from rest_VariantValidator.utils.limiter import limiter # get login authentication, if needed, or dummy auth if not present try: from VariantValidator_APIs.db_auth.verify_password import auth @@ -18,6 +19,7 @@ @api.route("/variantformatter////" "/") +@api.doc(description="This endpoint has a rate limit of 4 requests per second.") @api.param("variant_description", "***Genomic HGVS***\n" "> - NC_000017.10:g.48275363C>A\n" "\n***Pseudo-VCF***\n" @@ -52,6 +54,7 @@ class VariantFormatterClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) @auth.login_required() + @limiter.limit("4/second") def get(self, genome_build, variant_description, transcript_model, select_transcripts, checkonly): if transcript_model == 'None' or transcript_model == 'none': transcript_model = None diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index bb2c69c..d864a0d 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -2,6 +2,7 @@ from flask_restx import Namespace, Resource from rest_VariantValidator.utils import exceptions, request_parser, representations, input_formatting from rest_VariantValidator.utils.object_pool import vval_object_pool, g2t_object_pool +from rest_VariantValidator.utils.limiter import limiter # get login authentication, if needed, or dummy auth if not present try: from VariantValidator_APIs.db_auth.verify_password import auth @@ -18,6 +19,7 @@ @api.route("/variantvalidator///") +@api.doc(description="This endpoint has a rate limit of 2 requests per second.") @api.param("select_transcripts", "***Return all possible transcripts***\n" "> all (at the latest version for each transcript)\n" "> raw (all versions of each transcript)\n" @@ -57,6 +59,7 @@ class VariantValidatorClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) @auth.login_required() + @limiter.limit("2/second") def get(self, genome_build, variant_description, select_transcripts): # Import object from vval pool @@ -110,6 +113,7 @@ def get(self, genome_build, variant_description, select_transcripts): @api.route("/tools/gene2transcripts/") +@api.doc(description="This endpoint has a rate limit of 1 request per second.") @api.param("gene_query", "***HGNC gene symbol, HGNC ID, or transcript ID***\n" "\nCurrent supported transcript IDs" "\n- RefSeq\n" @@ -120,6 +124,8 @@ def get(self, genome_build, variant_description, select_transcripts): class Gene2transcriptsClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) + @auth.login_required() + @limiter.limit("1/second") def get(self, gene_query): # Get vvval object from pool @@ -154,6 +160,7 @@ def get(self, gene_query): @api.route("/tools/gene2transcripts_v2////" "") +@api.doc(description="This endpoint has a rate limit of 1 request per second.") @api.param("gene_query", "***HGNC gene symbol, HGNC ID, or transcript ID***\n" "\nCurrent supported transcript IDs" "\n- RefSeq or Ensembl\n" @@ -180,6 +187,8 @@ def get(self, gene_query): class Gene2transcriptsV2Class(Resource): # Add documentation about the parser @api.expect(parser, validate=True) + @auth.login_required() + @limiter.limit("1/second") def get(self, gene_query, limit_transcripts, transcript_set, genome_build): # Get vval object from pool @@ -229,6 +238,8 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build): class Hgvs2referenceClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) + @auth.login_required() + @limiter.limit("4/second") def get(self, hgvs_description): # Get vval object from pool diff --git a/rest_VariantValidator/utils/limiter.py b/rest_VariantValidator/utils/limiter.py new file mode 100644 index 0000000..079afb4 --- /dev/null +++ b/rest_VariantValidator/utils/limiter.py @@ -0,0 +1,21 @@ +from flask_limiter import Limiter +from flask_limiter.util import get_remote_address + +limiter = Limiter(key_func=get_remote_address) + +# +# Copyright (C) 2016-2024 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/rest_VariantValidator/wsgi.py b/rest_VariantValidator/wsgi.py index 60d7931..fd616c5 100644 --- a/rest_VariantValidator/wsgi.py +++ b/rest_VariantValidator/wsgi.py @@ -20,3 +20,20 @@ # Read the port from the environment variable, defaulting to 8000 if not set port = int(os.environ.get('PORT', 8000)) app.run(host="127.0.0.1", port=port) + +# +# Copyright (C) 2016-2024 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 8265df0..8163097 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -1,6 +1,7 @@ # Import necessary packages import pytest +import time from rest_VariantValidator.app import application # Import your Flask app @@ -123,9 +124,9 @@ def test_lovd_endpoint_all(client): assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response assert "1:43815009:G:T" in response.json.keys() + def test_lovd_endpoint_raw(client): response = client.get('/LOVD/lovd/GRCh37/1%3A43815009%3AG%3AT/refseq/raw/False/True?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response assert "1:43815009:G:T" in response.json.keys() - diff --git a/tests/test_handlers.py b/tests/test_handlers.py index af287d8..4e4ae67 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -6,30 +6,39 @@ # Fixture to set up the test client @pytest.fixture(scope='module') def client(): - application.testing = True + application.testing = False + application.debug = False + application.config['PROPAGATE_EXCEPTIONS'] = True return application.test_client() # Create a test client to interact with the app # Test function for exception handlers def test_bad_request_url(client): - response = client.get('/nonexistent/?content-type=application%2Fjson') # Send GET request to a nonexistent endpoint + response = client.get('/nonexistent/', headers={'Content-Type': 'application/json'}) assert response.status_code == 404 # Check if the response status code is 404 NOT FOUND - assert response.json["message"] == "Requested Endpoint not found" + assert response.json["message"] == "Requested Endpoint not found: See the documentation at https://rest.variantvalidator.org" def test_bad_request_code(client): - response = client.get('/trigger_error/404') # Send a GET request to a nonexistent endpoint + response = client.get('/trigger_error/404', headers={'Content-Type': 'application/json'}) assert response.status_code == 404 # Check if the response status code is 404 NOT FOUND - assert response.json["message"] == "Requested Endpoint not found" + assert response.json["message"] == "Requested Endpoint not found: See the documentation at https://rest.variantvalidator.org" def test_error_code(client): - response = client.get('/hello/trigger_error/500') # Send a GET request to a nonexistent endpoint - assert response.status_code == 500 # Check if the response status code is 404 NOT FOUND - assert response.json["message"] == "Internal Server Error" + response = client.get('/hello/trigger_error/500', headers={'Content-Type': 'application/json'}) + assert response.status_code == 500 + assert response.json["message"] == "Unhandled error: contact https://variantvalidator.org/contact_admin/" + + +def test_limit_code(client): + response = client.get('/hello/trigger_error/429', headers={'Content-Type': 'application/json'}) + assert response.status_code == 429 + assert response.json["message"] == "Rate limit hit for this endpoint: See the endpoint documentation at https://rest.variantvalidator.org" def test_connection_error(client): - response = client.get('/hello/trigger_error/999') # Send a GET request to a nonexistent endpoint + response = client.get('/hello/trigger_error/999', headers={'Content-Type': 'application/json'}) # Send a GET request to a nonexistent endpoint assert response.status_code == 504 # Check if the response status code is 404 NOT FOUND assert response.json["message"] == "https://rest.variantvalidator.org/variantvalidator currently unavailable" + From 2fb81cd5c8c447bbf8c5c990d9930584c69123d4 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Tue, 16 Apr 2024 09:44:09 +0100 Subject: [PATCH 185/218] code tewaks --- Jenkinsfile | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 7f1284c..69a7fde 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,13 +1,3 @@ -Sure, I can help you modify your `Jenkinsfile` to include some additional steps. Here's a step-by-step plan: - -1. Add a step to check if the directory `/root/variantvalidator_data/seqdata` exists. If it doesn't, create it. -2. Add a step to check if the directory `/root/variantvalidator_data/logs` exists. If it doesn't, create it. -3. Add a step to list the contents of the `/root/variantvalidator_data/` directory to verify that the directories were created successfully. -4. Continue with the existing steps to build and run the Docker containers. - -Here's the modified `Jenkinsfile`: - -```groovy pipeline { agent { docker { @@ -112,6 +102,3 @@ pipeline { } } } -``` - -This script checks if the directories exist and creates them if they don't. It then lists the contents of the `/root/variantvalidator_data/` directory to verify that the directories were created successfully. The rest of the script is the same as your original script. \ No newline at end of file From f9133538164b877a8feb2fa183380f2d379da8f3 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 17 Apr 2024 14:21:27 +0100 Subject: [PATCH 186/218] tweaks --- rest_VariantValidator/utils/object_pool.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rest_VariantValidator/utils/object_pool.py b/rest_VariantValidator/utils/object_pool.py index 9d4a872..2a1c6d8 100644 --- a/rest_VariantValidator/utils/object_pool.py +++ b/rest_VariantValidator/utils/object_pool.py @@ -48,9 +48,9 @@ def return_object(self, obj): # Create shared object pools -vval_object_pool = ObjectPool(Validator, initial_pool_size=3, max_pool_size=5) -g2t_object_pool = ObjectPool(Validator, initial_pool_size=3, max_pool_size=5) -simple_variant_formatter_pool = SimpleVariantFormatterPool(initial_pool_size=3, max_pool_size=5) +vval_object_pool = ObjectPool(Validator, initial_pool_size=8, max_pool_size=10) +g2t_object_pool = ObjectPool(Validator, initial_pool_size=6, max_pool_size=10) +simple_variant_formatter_pool = SimpleVariantFormatterPool(initial_pool_size=8, max_pool_size=10) # # Copyright (C) 2016-2024 VariantValidator Contributors From 3176aa2d7faac6673594505caca459b51fd2b4fc Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 17 Apr 2024 14:22:43 +0100 Subject: [PATCH 187/218] object pool tweaks --- rest_VariantValidator/utils/object_pool.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rest_VariantValidator/utils/object_pool.py b/rest_VariantValidator/utils/object_pool.py index 9d4a872..2a1c6d8 100644 --- a/rest_VariantValidator/utils/object_pool.py +++ b/rest_VariantValidator/utils/object_pool.py @@ -48,9 +48,9 @@ def return_object(self, obj): # Create shared object pools -vval_object_pool = ObjectPool(Validator, initial_pool_size=3, max_pool_size=5) -g2t_object_pool = ObjectPool(Validator, initial_pool_size=3, max_pool_size=5) -simple_variant_formatter_pool = SimpleVariantFormatterPool(initial_pool_size=3, max_pool_size=5) +vval_object_pool = ObjectPool(Validator, initial_pool_size=8, max_pool_size=10) +g2t_object_pool = ObjectPool(Validator, initial_pool_size=6, max_pool_size=10) +simple_variant_formatter_pool = SimpleVariantFormatterPool(initial_pool_size=8, max_pool_size=10) # # Copyright (C) 2016-2024 VariantValidator Contributors From 0142d3a85d69ab1a8f63568f9d866e2ab7813337 Mon Sep 17 00:00:00 2001 From: John-F-Wagstaff Date: Wed, 1 May 2024 11:36:12 +0100 Subject: [PATCH 188/218] Add a rate limited endpoint to the hello endpoints This endpoint has a long and predictable (will not need to be changed in case of user behaviour/load changes) limit time of 1 second between requests, and is intended for testing. --- rest_VariantValidator/endpoints/hello.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py index f8eb63c..8438f85 100644 --- a/rest_VariantValidator/endpoints/hello.py +++ b/rest_VariantValidator/endpoints/hello.py @@ -1,5 +1,6 @@ from flask_restx import Namespace, Resource from rest_VariantValidator.utils import request_parser, representations, exceptions +from rest_VariantValidator.utils.limiter import limiter from flask import abort # Import VariantValidator code @@ -59,6 +60,13 @@ def get(self): "metadata": config_dict } +@api.route('/limit') +class LimitedRateHelllo(Resource): + @limiter.limit("1/second") + @api.expect(parser, validate=True) + def get(self): + return { "status": "not yet hitting the rate limit" } + @api.route('/trigger_error/') class ExceptionClass(Resource): From a4f672fa95b2de2c814f730f84a49757dceb4787 Mon Sep 17 00:00:00 2001 From: John-F-Wagstaff Date: Wed, 1 May 2024 11:39:01 +0100 Subject: [PATCH 189/218] Add tests for the rate limiting code These are all unfortunately dependent on a somewhat fragile overwrite of the default test client behaviour, so may break with otherwise non-problematic code changes in the future. There does not seem to be a better way of doing these tests however, so we just have to tolerate this issue. --- tests/test_limiters.py | 51 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/test_limiters.py diff --git a/tests/test_limiters.py b/tests/test_limiters.py new file mode 100644 index 0000000..a855626 --- /dev/null +++ b/tests/test_limiters.py @@ -0,0 +1,51 @@ +"""Tests for the rate limiting code +Currently limited to just testing that it works and keeps to the requested time. +This code relies on the /hello/limit endpoint and relies on it's 1 access per- +second limit. +""" +# Import necessary packages +import time +import pytest +from flask import g +from rest_VariantValidator.utils.limiter import limiter +from rest_VariantValidator.app import application # Import your Flask app + +# Fixture to set up the test client +@pytest.fixture(scope='module',name='client') +def rate_limit_test_client(): + "Return a test client that works for rate limiting" + application.testing = False + application.debug = False + application.config['PROPAGATE_EXCEPTIONS'] = True + # This is fragile, and previous workarounds have failed before, so may + # break on updates to the limiter or flask test client code. + with application.app_context(): + setattr(g, '%s_rate_limiting_complete' % limiter._key_prefix, False) + return application.test_client() # Create a test client to interact with the app + + +# test the limiter works as intended, +def test_limit_endpoint_error_immediate(client): + "Provoke a limiter error by repeated requests to a limited endpoint" + response = client.get('/hello/limit', headers={'Content-Type': 'application/json'}) + response = client.get('/hello/limit', headers={'Content-Type': 'application/json'}) + assert response.status_code == 429 + assert response.json["message"] == "Rate limit hit for this endpoint: See the endpoint documentation at https://rest.variantvalidator.org" + +def test_limit_endpoint_error_delayed(client): + """ + Provoke a limiter error by repeated requests to a limited endpoint, + but with a partial (sub 1 second) delay. + """ + response = client.get('/hello/limit', headers={'Content-Type': 'application/json'}) + time.sleep(0.5) + response = client.get('/hello/limit', headers={'Content-Type': 'application/json'}) + assert response.status_code == 429 + assert response.json["message"] == "Rate limit hit for this endpoint: See the endpoint documentation at https://rest.variantvalidator.org" + +def test_limit_endpoint_success(client): + "Repeat the same as above, but with a 1 second delay to exceed limit" + response = client.get('/hello/limit', headers={'Content-Type': 'application/json'}) + time.sleep(1) + response = client.get('/hello/limit', headers={'Content-Type': 'application/json'}) + assert response.status_code == 200 From d0429ff79d26529e657316e902f6d52d0e1a979e Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 3 May 2024 13:04:03 +0100 Subject: [PATCH 190/218] fixed the endpoint tests --- db_dockerfiles/seqrepo/Dockerfile | 6 +++--- db_dockerfiles/vdb/Dockerfile | 2 +- db_dockerfiles/vvta/Dockerfile | 4 ++-- tests/test_endpoints.py | 25 ++++++++++++++++++++++++- tests/test_internal_servers.py | 19 ++++++++++++------- tests/test_limiters.py | 14 ++++++++------ 6 files changed, 50 insertions(+), 20 deletions(-) diff --git a/db_dockerfiles/seqrepo/Dockerfile b/db_dockerfiles/seqrepo/Dockerfile index b287a49..85e1c85 100644 --- a/db_dockerfiles/seqrepo/Dockerfile +++ b/db_dockerfiles/seqrepo/Dockerfile @@ -6,11 +6,11 @@ RUN apt-get install -y wget RUN mkdir -p /usr/local/share/seqdata -RUN wget --output-document=/usr/local/share/seqdata/VV_SR_2023_05.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2023_05.tar +RUN wget --output-document=/usr/local/share/seqdata/VV_SR_2024_01.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2024_01.tar -RUN tar -xvf /usr/local/share/seqdata/VV_SR_2023_05.tar --directory /usr/local/share/seqdata +RUN tar -xvf /usr/local/share/seqdata/VV_SR_2024_01.tar --directory /usr/local/share/seqdata -RUN rm /usr/local/share/seqdata/VV_SR_2023_05.tar +RUN rm /usr/local/share/seqdata/VV_SR_2024_01.tar ENTRYPOINT [] diff --git a/db_dockerfiles/vdb/Dockerfile b/db_dockerfiles/vdb/Dockerfile index e916638..ed7b4f4 100644 --- a/db_dockerfiles/vdb/Dockerfile +++ b/db_dockerfiles/vdb/Dockerfile @@ -15,6 +15,6 @@ RUN rm -rf /var/lib/apt/lists/* RUN echo '[mysqld]' >> /etc/mysql/my.cnf && \ echo 'max_connections=250' >> /etc/mysql/my.cnf -RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2023_08.sql.gz -O /docker-entrypoint-initdb.d/validator_2023_08.sql.gz +RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2024_05.sql.gz -O /docker-entrypoint-initdb.d/validator_2024_05.sql.gz CMD ["mysqld"] diff --git a/db_dockerfiles/vvta/Dockerfile b/db_dockerfiles/vvta/Dockerfile index 402a81e..136ece5 100644 --- a/db_dockerfiles/vvta/Dockerfile +++ b/db_dockerfiles/vvta/Dockerfile @@ -14,7 +14,7 @@ RUN apt-get update && \ RUN echo "shared_buffers = 2GB" > /docker-entrypoint-initdb.d/postgresql.conf # Step 1: Download the file -RUN wget https://www528.lamp.le.ac.uk/vvdata/vvta/vvta_2023_05_no_seq.sql.gz -O input_file.sql.gz +RUN wget https://www528.lamp.le.ac.uk/vvdata/vvta/vvta_2024_01_no_seq.sql.gz -O input_file.sql.gz # Step 2: Extract the gzipped file RUN gzip -dq input_file.sql.gz @@ -27,5 +27,5 @@ RUN rm input_file.sql RUN gzip modified_file.sql # Step 5: Move the modified and compressed file to the desired location -RUN mv modified_file.sql.gz /docker-entrypoint-initdb.d/vvta_2023_05_noseq.sql.gz +RUN mv modified_file.sql.gz /docker-entrypoint-initdb.d/vvta_2024_01_noseq.sql.gz diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 8163097..4d3a969 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -5,11 +5,14 @@ from rest_VariantValidator.app import application # Import your Flask app +# Fixture to set up the test client # Fixture to set up the test client @pytest.fixture(scope='module') def client(): application.testing = True - return application.test_client() # Create a test client to interact with the app + test_client = application.test_client() # Create a test client to interact with the app + + yield test_client # This is where the tests will run # Test function for the /hello/ endpoint @@ -17,6 +20,8 @@ def test_hello_endpoint(client): response = client.get('/hello/') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + time.sleep(1) + def test_lovd_endpoint(client): @@ -24,6 +29,7 @@ def test_lovd_endpoint(client): assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response assert "17-50198002-C-A" in response.json.keys() + time.sleep(1) def test_lovd_endpoint_multi(client): response = client.get('/LOVD/lovd/GRCh38/17-50198002-C-A|17-50198002-C-T/all/mane/True/False?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint @@ -31,12 +37,14 @@ def test_lovd_endpoint_multi(client): assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response assert "17-50198002-C-A" in response.json.keys() assert "17-50198002-C-T" in response.json.keys() + time.sleep(1) def test_vf_endpoint(client): response = client.get('/VariantFormatter/variantformatter/GRCh38/NC_000017.10%3Ag.48275363C%3EA/all/mane/True?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + time.sleep(1) def test_vv_endpoint(client): @@ -44,6 +52,7 @@ def test_vv_endpoint(client): assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response assert "NM_000088.3:c.589G>T" in response.json.keys() + time.sleep(1) def test_vv_endpoint_multi(client): @@ -52,77 +61,90 @@ def test_vv_endpoint_multi(client): assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response assert "NM_000088.3:c.589G>T" in response.json.keys() assert "NM_000088.3:c.589G>A" in response.json.keys() + time.sleep(1) def test_g2t_endpoint(client): response = client.get('/VariantValidator/tools/gene2transcripts/COL1A1?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert len(response.json) == 6 + time.sleep(1) def test_g2t2_endpoint(client): response = client.get('/VariantValidator/tools/gene2transcripts_v2/COL1A1%7CCOL1A2%7CCOL5A1/mane/all/GRCh38?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert len(response.json) == 3 + time.sleep(1) def test_h2ref_endpoint(client): response = client.get('/VariantValidator/tools/hgvs2reference/NM_000088.3%3Ac.589G%3ET?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK + time.sleep(1) def test_vv_endpoint_all_tx(client): response = client.get('/VariantValidator/variantvalidator/GRCh38/NM_000088.3%3Ac.589G%3ET/all?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + time.sleep(1) def test_vv_endpoint_all_vcf(client): response = client.get('/VariantValidator/variantvalidator/GRCh38/17-50198002-C-A/all?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 404 # Check if the response status code is 200 OK assert "metadata" not in response.json.keys() # Check if "metadata" key is in the JSON response + time.sleep(1) def test_vv_endpoint_all_genomic(client): response = client.get('/VariantValidator/variantvalidator/GRCh38/NC_000017.10:g.48275363C>A/all?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 404 # Check if the response status code is 200 OK assert "metadata" not in response.json.keys() # Check if "metadata" key is in the JSON response + time.sleep(1) def test_vv_endpoint_mane_genomic(client): response = client.get('/VariantValidator/variantvalidator/GRCh38/NC_000017.10:g.48275363C>A/mane?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + time.sleep(1) def test_vv_endpoint_mane_vcf(client): response = client.get('/VariantValidator/variantvalidator/GRCh38/17-50198002-C-A/mane?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + time.sleep(1) def test_vv_endpoint_auth_raw_vcf(client): response = client.get('/VariantValidator/variantvalidator/GRCh38/17-50198002-C-A/auth_all?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + time.sleep(1) def test_vv_endpoint_auth_all_vcf(client): response = client.get('/VariantValidator/variantvalidator/GRCh38/17-50198002-C-A/auth_raw?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response + time.sleep(1) def test_vv_g2tv2(client): response = client.get('/VariantValidator/tools/gene2transcripts_v2/AMPD1/mane/refseq/GRCh38?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert len(response.json) == 1 + time.sleep(1) def test_lovd_endpoint_all(client): response = client.get('/LOVD/lovd/GRCh37/1%3A43815009%3AG%3AT/refseq/all/False/True?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response assert "1:43815009:G:T" in response.json.keys() + time.sleep(1) def test_lovd_endpoint_raw(client): @@ -130,3 +152,4 @@ def test_lovd_endpoint_raw(client): assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response assert "1:43815009:G:T" in response.json.keys() + time.sleep(1) diff --git a/tests/test_internal_servers.py b/tests/test_internal_servers.py index af246ab..4c9131a 100644 --- a/tests/test_internal_servers.py +++ b/tests/test_internal_servers.py @@ -11,22 +11,28 @@ def setUpClass(cls): os.environ['PORT'] = '8002' # Start the WSGI server as a separate process - cls.wsgi_server_process = subprocess.Popen(['python', 'rest_VariantValidator/wsgi.py']) + cls.wsgi_server_process = subprocess.Popen(['python', 'rest_VariantValidator/wsgi.py'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) time.sleep(60) # Check if the WSGI server started correctly if cls.wsgi_server_process.poll() is not None: + stdout, stderr = cls.wsgi_server_process.communicate() + print(f"WSGI Server stdout: {stdout}") + print(f"WSGI Server stderr: {stderr}") raise RuntimeError('WSGI server failed to start') # Set the PORT environment variable for APP server os.environ['PORT'] = '5002' # Start the app server as a separate process - cls.app_server_process = subprocess.Popen(['python', 'rest_VariantValidator/app.py']) + cls.app_server_process = subprocess.Popen(['python', 'rest_VariantValidator/app.py'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) time.sleep(60) # Check if the app server started correctly if cls.app_server_process.poll() is not None: + stdout, stderr = cls.app_server_process.communicate() + print(f"App Server stdout: {stdout}") + print(f"App Server stderr: {stderr}") raise RuntimeError('App server failed to start') @classmethod @@ -45,14 +51,14 @@ def tearDownClass(cls): if wsgi_exit_code != 0: wsgi_pid = cls.wsgi_server_process.pid - # Forcefully kill all processes listening on port 8001 + # Forcefully kill all processes listening on port 8002 subprocess.run(['pkill', '-f', f':{wsgi_pid}']) assert wsgi_exit_code == 0, f"WSGI Server termination failed with exit code {wsgi_exit_code}" else: assert wsgi_exit_code == 0, f"WSGI Server termination failed with exit code {wsgi_exit_code}" if app_exit_code != 0: - app_pid = cls.app_server_process.pid # Fix this line - # Forcefully kill all processes listening on port 5001 + app_pid = cls.app_server_process.pid + # Forcefully kill all processes listening on port 5002 subprocess.run(['pkill', '-f', f':{app_pid}']) assert app_exit_code == 0, f"App Server termination failed with exit code {app_exit_code}" else: @@ -70,5 +76,4 @@ def test_wsgi_internal_server(self): def test_app_server(self): # Check the app server - self.check_server('hello', 5002) - + self.check_server('hello', 5002) \ No newline at end of file diff --git a/tests/test_limiters.py b/tests/test_limiters.py index a855626..845d487 100644 --- a/tests/test_limiters.py +++ b/tests/test_limiters.py @@ -3,6 +3,7 @@ This code relies on the /hello/limit endpoint and relies on it's 1 access per- second limit. """ + # Import necessary packages import time import pytest @@ -13,7 +14,7 @@ # Fixture to set up the test client @pytest.fixture(scope='module',name='client') def rate_limit_test_client(): - "Return a test client that works for rate limiting" + """Return a test client that works for rate limiting""" application.testing = False application.debug = False application.config['PROPAGATE_EXCEPTIONS'] = True @@ -21,12 +22,13 @@ def rate_limit_test_client(): # break on updates to the limiter or flask test client code. with application.app_context(): setattr(g, '%s_rate_limiting_complete' % limiter._key_prefix, False) - return application.test_client() # Create a test client to interact with the app + test_client = application.test_client() # Create a test client to interact with the app + yield test_client # This is where the tests will run -# test the limiter works as intended, +# Test the limiter works as intended, def test_limit_endpoint_error_immediate(client): - "Provoke a limiter error by repeated requests to a limited endpoint" + """Provoke a limiter error by repeated requests to a limited endpoint""" response = client.get('/hello/limit', headers={'Content-Type': 'application/json'}) response = client.get('/hello/limit', headers={'Content-Type': 'application/json'}) assert response.status_code == 429 @@ -44,8 +46,8 @@ def test_limit_endpoint_error_delayed(client): assert response.json["message"] == "Rate limit hit for this endpoint: See the endpoint documentation at https://rest.variantvalidator.org" def test_limit_endpoint_success(client): - "Repeat the same as above, but with a 1 second delay to exceed limit" + """Repeat the same as above, but with a 1 second delay to exceed limit""" response = client.get('/hello/limit', headers={'Content-Type': 'application/json'}) time.sleep(1) response = client.get('/hello/limit', headers={'Content-Type': 'application/json'}) - assert response.status_code == 200 + assert response.status_code == 200 \ No newline at end of file From 4e7e9885f07056eba5a3b2eae7cd23df7d9a30a6 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 12 Jun 2024 11:49:41 +0100 Subject: [PATCH 191/218] Add ensembl to VV endopint. Still need to add to the genes2transcripts --- .../endpoints/variantvalidator_endpoints.py | 14 +++++++++++--- tests/test_limiters.py | 4 +++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index d864a0d..beaafbe 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -18,7 +18,7 @@ api = Namespace('VariantValidator', description='VariantValidator API Endpoints') -@api.route("/variantvalidator///") +@api.route("/variantvalidator/////") @api.doc(description="This endpoint has a rate limit of 2 requests per second.") @api.param("select_transcripts", "***Return all possible transcripts***\n" "> all (at the latest version for each transcript)\n" @@ -55,16 +55,23 @@ "> - GRCh38\n" "> - hg19\n" "> - hg38") +@api.param("transcript_model", "***Accepted:***\n" + "> - refseq (return data for RefSeq transcript models)\n" + "> - ensembl (return data for ensembl transcript models)") class VariantValidatorClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) @auth.login_required() @limiter.limit("2/second") - def get(self, genome_build, variant_description, select_transcripts): + def get(self, genome_build, variant_description, select_transcripts, transcript_model): # Import object from vval pool vval = vval_object_pool.get_object() + # set transcript_model + if transcript_model != 'ensembl' and transcript_model == 'refseq': + transcript_model = "refseq" + # Switch off select_transcripts = all or raw for genomic variants if ("all" in select_transcripts or "raw" in select_transcripts) and "auth" not in select_transcripts: if "c." not in variant_description and "n." not in variant_description and "r." not in variant_description \ @@ -89,7 +96,8 @@ def get(self, genome_build, variant_description, select_transcripts): try: # Validate using the VariantValidator Python Library - validate = vval.validate(variant_description, genome_build, select_transcripts) + validate = vval.validate(variant_description, genome_build, select_transcripts, + transcript_set=transcript_model) content = validate.format_as_dict(with_meta=True) except Exception as e: # Handle the exception and customize the error response diff --git a/tests/test_limiters.py b/tests/test_limiters.py index 845d487..7f2c87e 100644 --- a/tests/test_limiters.py +++ b/tests/test_limiters.py @@ -26,6 +26,7 @@ def rate_limit_test_client(): yield test_client # This is where the tests will run + # Test the limiter works as intended, def test_limit_endpoint_error_immediate(client): """Provoke a limiter error by repeated requests to a limited endpoint""" @@ -50,4 +51,5 @@ def test_limit_endpoint_success(client): response = client.get('/hello/limit', headers={'Content-Type': 'application/json'}) time.sleep(1) response = client.get('/hello/limit', headers={'Content-Type': 'application/json'}) - assert response.status_code == 200 \ No newline at end of file + assert response.status_code == 200 + From d993d38296a75cd07d0f71db1402f7abfd675bde Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 12 Jun 2024 12:17:52 +0100 Subject: [PATCH 192/218] add VV ensembl endpoint --- .../endpoints/variantvalidator_endpoints.py | 104 ++++++++++++++++-- 1 file changed, 95 insertions(+), 9 deletions(-) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index beaafbe..fe49532 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -18,11 +18,9 @@ api = Namespace('VariantValidator', description='VariantValidator API Endpoints') -@api.route("/variantvalidator/////") +@api.route("/variantvalidator////") @api.doc(description="This endpoint has a rate limit of 2 requests per second.") @api.param("select_transcripts", "***Return all possible transcripts***\n" - "> all (at the latest version for each transcript)\n" - "> raw (all versions of each transcript)\n" "\n***Return only 'select' transcripts***\n" "> select\n" "> mane_select\n" @@ -55,22 +53,110 @@ "> - GRCh38\n" "> - hg19\n" "> - hg38") -@api.param("transcript_model", "***Accepted:***\n" - "> - refseq (return data for RefSeq transcript models)\n" - "> - ensembl (return data for ensembl transcript models)") class VariantValidatorClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) @auth.login_required() @limiter.limit("2/second") - def get(self, genome_build, variant_description, select_transcripts, transcript_model): + def get(self, genome_build, variant_description, select_transcripts): # Import object from vval pool vval = vval_object_pool.get_object() # set transcript_model - if transcript_model != 'ensembl' and transcript_model == 'refseq': - transcript_model = "refseq" + transcript_model = "refseq" + + # Switch off select_transcripts = all or raw for genomic variants + if ("all" in select_transcripts or "raw" in select_transcripts) and "auth" not in select_transcripts: + if "c." not in variant_description and "n." not in variant_description and "r." not in variant_description \ + and "p." not in variant_description: + return {"Not Found": "Setting select_transcripts to 'all' or 'raw' is deprecated for genomic " + "variant processing using this endpoint. Contact admin on " + "https://variantvalidator.org/help/contact/ for updated instructions and" + " fair usage information; use another option; or use the LOVD endpoint which is " + "designed for integration into pipelines"}, 404 + elif "auth_all" in select_transcripts: + select_transcripts = "all" + elif "auth_raw" in select_transcripts: + select_transcripts = "raw" + + # Convert inputs to JSON arrays + variant_description = input_formatting.format_input(variant_description) + select_transcripts = input_formatting.format_input(select_transcripts) + if select_transcripts == '["all"]': + select_transcripts = "all" + if select_transcripts == '["raw"]': + select_transcripts = "raw" + + try: + # Validate using the VariantValidator Python Library + validate = vval.validate(variant_description, genome_build, select_transcripts, + transcript_set=transcript_model) + content = validate.format_as_dict(with_meta=True) + except Exception as e: + # Handle the exception and customize the error response + return {"error": str(e)}, 500 + finally: + vval_object_pool.return_object(vval) + + # Collect Arguments + args = parser.parse_args() + + # Overrides the default response route so that the standard HTML URL can return any specified format + if args['content-type'] == 'application/json': + # example: http://127.0.0.1:5000.....bob?content-type=application/json + return representations.application_json(content, 200, None) + # example: http://127.0.0.1:5000.....?content-type=text/xml + elif args['content-type'] == 'text/xml': + return representations.xml(content, 200, None) + else: + # Return the api default output + return content + +@api.route("/variantvalidator_ensembl////") +@api.doc(description="This endpoint has a rate limit of 2 requests per second.") +@api.param("select_transcripts", "***Return all possible transcripts***\n" + "\n***Return only 'select' transcripts***\n" + "> select\n" + "> mane_select\n" + "> mane (MANE and MANE Plus Clinical)\n" + "> refseq_select\n" + "\n***Single***\n" + "> ENST00000225964.10\n" + "\n***Multiple***\n" + "> ENST00000225964.9|ENST00000225964.10") +@api.param("variant_description", "***HGVS***\n" + "> - ENST00000225964.10:c.589G>T\n" + "> - NC_000017.10:g.48275363C>A\n" + "\n***Pseudo-VCF***\n" + "> - 17-50198002-C-A\n" + "> - 17:50198002:C:A\n" + "> - GRCh38-17-50198002-C-A\n" + "> - GRCh38:17:50198002:C:A\n" + "\n***Hybrid***\n" + "> - chr17:50198002C>A\n " + "> - chr17:50198002C>A(GRCh38)\n" + "> - chr17(GRCh38):50198002C>A\n" + "> - chr17:g.50198002C>A\n" + "> - chr17:g.50198002C>A(GRCh38)\n" + "> - chr17(GRCh38):g.50198002C>A") +@api.param("genome_build", "***Accepted:***\n" + "> - GRCh37\n" + "> - GRCh38\n" + "> - hg19\n" + "> - hg38") +class VariantValidatorEnsemblClass(Resource): + # Add documentation about the parser + @api.expect(parser, validate=True) + @auth.login_required() + @limiter.limit("2/second") + def get(self, genome_build, variant_description, select_transcripts): + + # Import object from vval pool + vval = vval_object_pool.get_object() + + # set transcript_model + transcript_model = "ensembl" # Switch off select_transcripts = all or raw for genomic variants if ("all" in select_transcripts or "raw" in select_transcripts) and "auth" not in select_transcripts: From a898a2a397faee5535f48053b6e1613167c7399e Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 8 Jul 2024 12:39:41 +0100 Subject: [PATCH 193/218] Remove trailing slashes from VV endpoints --- rest_VariantValidator/endpoints/variantvalidator_endpoints.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index fe49532..b69ff4d 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -18,7 +18,7 @@ api = Namespace('VariantValidator', description='VariantValidator API Endpoints') -@api.route("/variantvalidator////") +@api.route("/variantvalidator///") @api.doc(description="This endpoint has a rate limit of 2 requests per second.") @api.param("select_transcripts", "***Return all possible transcripts***\n" "\n***Return only 'select' transcripts***\n" @@ -113,7 +113,7 @@ def get(self, genome_build, variant_description, select_transcripts): # Return the api default output return content -@api.route("/variantvalidator_ensembl////") +@api.route("/variantvalidator_ensembl///") @api.doc(description="This endpoint has a rate limit of 2 requests per second.") @api.param("select_transcripts", "***Return all possible transcripts***\n" "\n***Return only 'select' transcripts***\n" From 812e222e0f6f667b74a1a62db19c3e167b151247 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 10 Jul 2024 10:17:31 +0100 Subject: [PATCH 194/218] update docker for v3 release --- configuration/docker.ini | 6 +++--- db_dockerfiles/seqrepo/Dockerfile | 8 ++++---- db_dockerfiles/vvta/Dockerfile | 4 ++-- docker-compose-dev.yml | 4 ++-- docker-compose.yml | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/configuration/docker.ini b/configuration/docker.ini index 525c405..4dc8bd1 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -4,10 +4,10 @@ port = 3306 database = validator user = vvadmin password = var1ant -version = vvdb_2023_8 +version = vvdb_2024_5 [seqrepo] -version = VV_SR_2023_05/master +version = VV_SR_2024_01/master location = /usr/local/share/seqdata require_threading = True @@ -15,7 +15,7 @@ require_threading = True host = rv-vvta port = 5432 database = vvta -version = vvta_2023_05 +version = vvta_2024_01 user = uta_admin password = uta_admin diff --git a/db_dockerfiles/seqrepo/Dockerfile b/db_dockerfiles/seqrepo/Dockerfile index 85e1c85..c7bc18e 100644 --- a/db_dockerfiles/seqrepo/Dockerfile +++ b/db_dockerfiles/seqrepo/Dockerfile @@ -1,16 +1,16 @@ FROM ubuntu:22.04 -RUN apt-get update +RUN apt-get update RUN apt-get install -y wget RUN mkdir -p /usr/local/share/seqdata -RUN wget --output-document=/usr/local/share/seqdata/VV_SR_2024_01.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2024_01.tar +RUN wget --output-document=/usr/local/share/seqdata/VV_SR_2024_04.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2024_04.tar -RUN tar -xvf /usr/local/share/seqdata/VV_SR_2024_01.tar --directory /usr/local/share/seqdata +RUN tar -xvf /usr/local/share/seqdata/VV_SR_2024_04.tar --directory /usr/local/share/seqdata -RUN rm /usr/local/share/seqdata/VV_SR_2024_01.tar +RUN rm /usr/local/share/seqdata/VV_SR_2024_04.tar ENTRYPOINT [] diff --git a/db_dockerfiles/vvta/Dockerfile b/db_dockerfiles/vvta/Dockerfile index 136ece5..9b0900a 100644 --- a/db_dockerfiles/vvta/Dockerfile +++ b/db_dockerfiles/vvta/Dockerfile @@ -14,7 +14,7 @@ RUN apt-get update && \ RUN echo "shared_buffers = 2GB" > /docker-entrypoint-initdb.d/postgresql.conf # Step 1: Download the file -RUN wget https://www528.lamp.le.ac.uk/vvdata/vvta/vvta_2024_01_no_seq.sql.gz -O input_file.sql.gz +RUN wget https://www528.lamp.le.ac.uk/vvdata/vvta/vvta_2024_01_no_seq.ens_patch.sql.gz -O input_file.sql.gz # Step 2: Extract the gzipped file RUN gzip -dq input_file.sql.gz @@ -27,5 +27,5 @@ RUN rm input_file.sql RUN gzip modified_file.sql # Step 5: Move the modified and compressed file to the desired location -RUN mv modified_file.sql.gz /docker-entrypoint-initdb.d/vvta_2024_01_noseq.sql.gz +RUN mv modified_file.sql.gz /docker-entrypoint-initdb.d/vvta_2024_01_no_seq.ens_patch.sql.gz diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 9bdcaeb..1474300 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -14,12 +14,12 @@ services: - vv-logs:/usr/local/share/logs # Mount volume for logs - seqdata:/usr/local/share/seqdata # Mount volume for sequence data ports: - - "5000:5000" + - "5001:5000" - "5050:5050" - "8000:8000" - "9000:9000" expose: - - "5000" # Expose ports for external access + - "5001" # Expose ports for external access - "5050" - "8000" - "8080" diff --git a/docker-compose.yml b/docker-compose.yml index 927ca81..582917a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,12 +40,12 @@ services: - vv-logs:/usr/local/share/logs # Mount volume for logs - seqdata:/usr/local/share/seqdata # Mount volume for sequence data ports: - - "5000:5000" + - "5001:5000" - "5050:5050" - "8000:8000" - "9000:9000" expose: - - "5000" # Expose ports for external access + - "5001" # Expose ports for external access - "5050" - "8000" - "8080" From c179b2ea03335f3403b2caf5e666f64df0b32079 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 11 Jul 2024 11:25:53 +0100 Subject: [PATCH 195/218] update docker for v3 release --- docs/DOCKER.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 8e7dcf1..dd6f675 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -96,6 +96,9 @@ $ docker-compose up -d rv-vvta && \ ✔ Container rest_variantvalidator-dev-mode-1 Running ``` +- The first time you see all services running, wait for 30 mimutes before testing and using the API. This is because the +VVTA database needs to load and initialise. + ### Test the build ```bash # Run PyTest (all tests should pass) From 71f7df369e766b1cdcae2a239ac0a62553861664 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 15 Nov 2024 10:29:09 +0000 Subject: [PATCH 196/218] fast forward with merged changes and update VVTA and SR docker to latest versions --- db_dockerfiles/seqrepo/Dockerfile | 6 +++--- db_dockerfiles/vvta/Dockerfile | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/db_dockerfiles/seqrepo/Dockerfile b/db_dockerfiles/seqrepo/Dockerfile index c7bc18e..0742d9d 100644 --- a/db_dockerfiles/seqrepo/Dockerfile +++ b/db_dockerfiles/seqrepo/Dockerfile @@ -6,11 +6,11 @@ RUN apt-get install -y wget RUN mkdir -p /usr/local/share/seqdata -RUN wget --output-document=/usr/local/share/seqdata/VV_SR_2024_04.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2024_04.tar +RUN wget --output-document=/usr/local/share/seqdata/VV_SR_2024_09.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2024_09.tar -RUN tar -xvf /usr/local/share/seqdata/VV_SR_2024_04.tar --directory /usr/local/share/seqdata +RUN tar -xvf /usr/local/share/seqdata/VV_SR_2024_09.tar --directory /usr/local/share/seqdata -RUN rm /usr/local/share/seqdata/VV_SR_2024_04.tar +RUN rm /usr/local/share/seqdata/VV_SR_2024_09.tar ENTRYPOINT [] diff --git a/db_dockerfiles/vvta/Dockerfile b/db_dockerfiles/vvta/Dockerfile index 9b0900a..89ad74c 100644 --- a/db_dockerfiles/vvta/Dockerfile +++ b/db_dockerfiles/vvta/Dockerfile @@ -14,7 +14,7 @@ RUN apt-get update && \ RUN echo "shared_buffers = 2GB" > /docker-entrypoint-initdb.d/postgresql.conf # Step 1: Download the file -RUN wget https://www528.lamp.le.ac.uk/vvdata/vvta/vvta_2024_01_no_seq.ens_patch.sql.gz -O input_file.sql.gz +RUN wget https://www528.lamp.le.ac.uk/vvdata/vvta/VVTA_2024_09_no_seq.sql.gz -O input_file.sql.gz # Step 2: Extract the gzipped file RUN gzip -dq input_file.sql.gz @@ -27,5 +27,5 @@ RUN rm input_file.sql RUN gzip modified_file.sql # Step 5: Move the modified and compressed file to the desired location -RUN mv modified_file.sql.gz /docker-entrypoint-initdb.d/vvta_2024_01_no_seq.ens_patch.sql.gz +RUN mv modified_file.sql.gz /docker-entrypoint-initdb.d/VVTA_2024_09_no_seq.sql.gz From dcf8d93b3a1157e5807fde2037187a1b5950faca Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Tue, 11 Mar 2025 15:50:57 +0000 Subject: [PATCH 197/218] push new docker build for validator 3.0.0 --- bin/batch_validator.py | 2 +- bin/update_vdb.py | 2 +- bin/variant_validator.py | 2 +- bin/vv_configure.py | 2 +- configuration/docker.ini | 9 ++++----- db_dockerfiles/seqrepo/Dockerfile | 8 ++++---- db_dockerfiles/vdb/Dockerfile | 2 +- db_dockerfiles/vvta/Dockerfile | 6 +++--- docs/DOCKER.md | 6 +++--- locust/locustfile.py | 2 +- rest_VariantValidator/__init__.py | 2 +- rest_VariantValidator/app.py | 2 +- rest_VariantValidator/endpoints/__init__.py | 2 +- rest_VariantValidator/endpoints/hello.py | 2 +- rest_VariantValidator/endpoints/lovd_endpoints.py | 2 +- .../endpoints/variantformatter_endpoints.py | 2 +- rest_VariantValidator/utils/exceptions.py | 2 +- rest_VariantValidator/utils/input_formatting.py | 2 +- rest_VariantValidator/utils/limiter.py | 2 +- rest_VariantValidator/utils/object_pool.py | 2 +- rest_VariantValidator/utils/representations.py | 2 +- rest_VariantValidator/utils/request_parser.py | 2 +- rest_VariantValidator/utils/verify_password.py | 2 +- rest_VariantValidator/wsgi.py | 2 +- 24 files changed, 34 insertions(+), 35 deletions(-) diff --git a/bin/batch_validator.py b/bin/batch_validator.py index 3ea61b7..d81387a 100644 --- a/bin/batch_validator.py +++ b/bin/batch_validator.py @@ -84,7 +84,7 @@ outfile.close() # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/bin/update_vdb.py b/bin/update_vdb.py index df31d3b..a57f749 100644 --- a/bin/update_vdb.py +++ b/bin/update_vdb.py @@ -16,7 +16,7 @@ update_vv_db.update() # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/bin/variant_validator.py b/bin/variant_validator.py index 7803b3a..3847c04 100644 --- a/bin/variant_validator.py +++ b/bin/variant_validator.py @@ -54,7 +54,7 @@ def output_results(valoutput, outformat, with_meta): args.output.write(output_results(output, args.output_format, args.meta) + '\n') # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/bin/vv_configure.py b/bin/vv_configure.py index 26552c7..1d9d221 100644 --- a/bin/vv_configure.py +++ b/bin/vv_configure.py @@ -59,7 +59,7 @@ def read_settings(): config.write(fh) # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/configuration/docker.ini b/configuration/docker.ini index 4dc8bd1..cb7dea9 100644 --- a/configuration/docker.ini +++ b/configuration/docker.ini @@ -4,10 +4,10 @@ port = 3306 database = validator user = vvadmin password = var1ant -version = vvdb_2024_5 +version = vvdb_2025_3 [seqrepo] -version = VV_SR_2024_01/master +version = VV_SR_2025_02/master location = /usr/local/share/seqdata require_threading = True @@ -15,7 +15,7 @@ require_threading = True host = rv-vvta port = 5432 database = vvta -version = vvta_2024_01 +version = vvta_2025_02 user = uta_admin password = uta_admin @@ -24,7 +24,6 @@ password = uta_admin log = True console = WARNING file = ERROR -file_path = /usr/local/share/logs [Entrez] email = OPTIONAL @@ -32,7 +31,7 @@ api_key = OPTIONAL # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/db_dockerfiles/seqrepo/Dockerfile b/db_dockerfiles/seqrepo/Dockerfile index 0742d9d..8d1811f 100644 --- a/db_dockerfiles/seqrepo/Dockerfile +++ b/db_dockerfiles/seqrepo/Dockerfile @@ -1,16 +1,16 @@ FROM ubuntu:22.04 -RUN apt-get update +RUN apt-get update RUN apt-get install -y wget RUN mkdir -p /usr/local/share/seqdata -RUN wget --output-document=/usr/local/share/seqdata/VV_SR_2024_09.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2024_09.tar +RUN wget --output-document=/usr/local/share/seqdata/VV_SR_2025_02.tar https://www528.lamp.le.ac.uk/vvdata/vv_seqrepo/VV_SR_2025_02.tar -RUN tar -xvf /usr/local/share/seqdata/VV_SR_2024_09.tar --directory /usr/local/share/seqdata +RUN tar -xvf /usr/local/share/seqdata/VV_SR_2025_02.tar --directory /usr/local/share/seqdata -RUN rm /usr/local/share/seqdata/VV_SR_2024_09.tar +RUN rm /usr/local/share/seqdata/VV_SR_2025_02.tar ENTRYPOINT [] diff --git a/db_dockerfiles/vdb/Dockerfile b/db_dockerfiles/vdb/Dockerfile index ed7b4f4..3f16ea1 100644 --- a/db_dockerfiles/vdb/Dockerfile +++ b/db_dockerfiles/vdb/Dockerfile @@ -15,6 +15,6 @@ RUN rm -rf /var/lib/apt/lists/* RUN echo '[mysqld]' >> /etc/mysql/my.cnf && \ echo 'max_connections=250' >> /etc/mysql/my.cnf -RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2024_05.sql.gz -O /docker-entrypoint-initdb.d/validator_2024_05.sql.gz +RUN wget https://www528.lamp.le.ac.uk/vvdata/validator/validator_2025_03.sql.gz -O /docker-entrypoint-initdb.d/validator_2025_03.sql.gz CMD ["mysqld"] diff --git a/db_dockerfiles/vvta/Dockerfile b/db_dockerfiles/vvta/Dockerfile index 89ad74c..0706e8c 100644 --- a/db_dockerfiles/vvta/Dockerfile +++ b/db_dockerfiles/vvta/Dockerfile @@ -1,4 +1,4 @@ -FROM postgres:14.7 +FROM postgres:14.9 ENV POSTGRES_DB=vvta ENV POSTGRES_USER=uta_admin @@ -14,7 +14,7 @@ RUN apt-get update && \ RUN echo "shared_buffers = 2GB" > /docker-entrypoint-initdb.d/postgresql.conf # Step 1: Download the file -RUN wget https://www528.lamp.le.ac.uk/vvdata/vvta/VVTA_2024_09_no_seq.sql.gz -O input_file.sql.gz +RUN wget https://www528.lamp.le.ac.uk/vvdata/vvta/vvta_2025_02_no_seq.sql.gz -O input_file.sql.gz # Step 2: Extract the gzipped file RUN gzip -dq input_file.sql.gz @@ -27,5 +27,5 @@ RUN rm input_file.sql RUN gzip modified_file.sql # Step 5: Move the modified and compressed file to the desired location -RUN mv modified_file.sql.gz /docker-entrypoint-initdb.d/VVTA_2024_09_no_seq.sql.gz +RUN mv modified_file.sql.gz /docker-entrypoint-initdb.d/vvta_2025_02_no_seq.sql.gz diff --git a/docs/DOCKER.md b/docs/DOCKER.md index dd6f675..582254c 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -96,12 +96,12 @@ $ docker-compose up -d rv-vvta && \ ✔ Container rest_variantvalidator-dev-mode-1 Running ``` -- The first time you see all services running, wait for 30 mimutes before testing and using the API. This is because the -VVTA database needs to load and initialise. +- ***The first time you see all services running, wait for 30 minutes before testing and using the API. This is because the +VVTA database needs to load and initialise. There are no logs generated while this is happening*** ### Test the build ```bash -# Run PyTest (all tests should pass) +# Run PyTest (all tests should pass except for RunTimeError fails. These are intended for local development not dockerised versions) $ docker exec rest_variantvalidator-rest-variantvalidator-1 pytest ``` Note: Different host Operating Systems name the container using slightly different conventions e.g. underscores instead diff --git a/locust/locustfile.py b/locust/locustfile.py index 33ad805..63d22c8 100644 --- a/locust/locustfile.py +++ b/locust/locustfile.py @@ -27,7 +27,7 @@ class WebsiteUser(HttpUser): wait_time = between(0.1, 2) # seconds # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/__init__.py b/rest_VariantValidator/__init__.py index 09f6c68..663b3fc 100644 --- a/rest_VariantValidator/__init__.py +++ b/rest_VariantValidator/__init__.py @@ -13,7 +13,7 @@ __version__ = None # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index c693ecf..222f26c 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -198,7 +198,7 @@ def api_forbidden_error_handler(e): application.run(host="127.0.0.1", port=port) # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/__init__.py b/rest_VariantValidator/endpoints/__init__.py index 17c4768..bad2ffa 100644 --- a/rest_VariantValidator/endpoints/__init__.py +++ b/rest_VariantValidator/endpoints/__init__.py @@ -87,7 +87,7 @@ def specs_url(self): # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py index 8438f85..fbfdb23 100644 --- a/rest_VariantValidator/endpoints/hello.py +++ b/rest_VariantValidator/endpoints/hello.py @@ -88,7 +88,7 @@ def get(self, error_code): # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index 792c6c5..ac1db3b 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -126,7 +126,7 @@ def get(self, genome_build, variant_description, transcript_model, select_transc # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index 68c542f..de3550d 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -100,7 +100,7 @@ def get(self, genome_build, variant_description, transcript_model, select_transc return content # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/utils/exceptions.py b/rest_VariantValidator/utils/exceptions.py index dcb5e7c..029679a 100644 --- a/rest_VariantValidator/utils/exceptions.py +++ b/rest_VariantValidator/utils/exceptions.py @@ -2,7 +2,7 @@ class RemoteConnectionError(Exception): code = 504 # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/utils/input_formatting.py b/rest_VariantValidator/utils/input_formatting.py index 120f9cf..e13a191 100644 --- a/rest_VariantValidator/utils/input_formatting.py +++ b/rest_VariantValidator/utils/input_formatting.py @@ -24,7 +24,7 @@ def format_input(data_string): return json.dumps(data_list) # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/utils/limiter.py b/rest_VariantValidator/utils/limiter.py index 079afb4..7e2411c 100644 --- a/rest_VariantValidator/utils/limiter.py +++ b/rest_VariantValidator/utils/limiter.py @@ -4,7 +4,7 @@ limiter = Limiter(key_func=get_remote_address) # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/utils/object_pool.py b/rest_VariantValidator/utils/object_pool.py index 2a1c6d8..d9fa8a5 100644 --- a/rest_VariantValidator/utils/object_pool.py +++ b/rest_VariantValidator/utils/object_pool.py @@ -53,7 +53,7 @@ def return_object(self, obj): simple_variant_formatter_pool = SimpleVariantFormatterPool(initial_pool_size=8, max_pool_size=10) # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/utils/representations.py b/rest_VariantValidator/utils/representations.py index 7761b9f..30f907e 100644 --- a/rest_VariantValidator/utils/representations.py +++ b/rest_VariantValidator/utils/representations.py @@ -26,7 +26,7 @@ def application_json(data, code, headers): return resp # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/utils/request_parser.py b/rest_VariantValidator/utils/request_parser.py index 350a03d..22a6307 100644 --- a/rest_VariantValidator/utils/request_parser.py +++ b/rest_VariantValidator/utils/request_parser.py @@ -11,7 +11,7 @@ # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/utils/verify_password.py b/rest_VariantValidator/utils/verify_password.py index c86316e..1e1d317 100644 --- a/rest_VariantValidator/utils/verify_password.py +++ b/rest_VariantValidator/utils/verify_password.py @@ -15,7 +15,7 @@ def wrapper(*args, **kwargs): auth = DummyAuth() # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/wsgi.py b/rest_VariantValidator/wsgi.py index fd616c5..777ba47 100644 --- a/rest_VariantValidator/wsgi.py +++ b/rest_VariantValidator/wsgi.py @@ -22,7 +22,7 @@ app.run(host="127.0.0.1", port=port) # -# Copyright (C) 2016-2024 VariantValidator Contributors +# Copyright (C) 2016-2025 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as From d83009557f7bd1aabd62589c28a391fd3d42e95e Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 14 Mar 2025 12:44:51 +0000 Subject: [PATCH 198/218] ad bypass of exon spans and fix up an issue --- .../endpoints/lovd_endpoints.py | 4 -- .../endpoints/variantvalidator_endpoints.py | 20 +++++++--- .../utils/request_parser_g2t.py | 40 +++++++++++++++++++ 3 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 rest_VariantValidator/utils/request_parser_g2t.py diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index ac1db3b..cc4eeed 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -91,10 +91,6 @@ def get(self, genome_build, variant_description, transcript_model, select_transc # Convert inputs to JSON arrays variant_description = input_formatting.format_input(variant_description) select_transcripts = input_formatting.format_input(select_transcripts) - if select_transcripts == '["all"]': - select_transcripts = "all" - if select_transcripts == '["raw"]': - select_transcripts = "raw" try: content = simple_formatter.format(variant_description, genome_build, transcript_model, diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index b69ff4d..da9f8b8 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -1,6 +1,6 @@ # Import modules from flask_restx import Namespace, Resource -from rest_VariantValidator.utils import exceptions, request_parser, representations, input_formatting +from rest_VariantValidator.utils import exceptions, request_parser, representations, input_formatting, request_parser_g2t from rest_VariantValidator.utils.object_pool import vval_object_pool, g2t_object_pool from rest_VariantValidator.utils.limiter import limiter # get login authentication, if needed, or dummy auth if not present @@ -14,6 +14,7 @@ Create a parser object locally """ parser = request_parser.parser +parser_g2t = request_parser_g2t.parser api = Namespace('VariantValidator', description='VariantValidator API Endpoints') @@ -280,7 +281,7 @@ def get(self, gene_query): "\nall = all builds, GRCh37 = GRCh37 only, GRCh38 = GRCh38 only") class Gene2transcriptsV2Class(Resource): # Add documentation about the parser - @api.expect(parser, validate=True) + @api.expect(parser_g2t, validate=True) @auth.login_required() @limiter.limit("1/second") def get(self, gene_query, limit_transcripts, transcript_set, genome_build): @@ -288,6 +289,15 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build): # Get vval object from pool vval = g2t_object_pool.get_object() + # Collect Arguments + args = parser_g2t.parse_args() + if args['show_exon_info'] is True: + bypass_genomic_spans = False + elif args['show_exon_info'] is False: + bypass_genomic_spans = True + else: + bypass_genomic_spans = True + # Convert inputs to JSON arrays gene_query = input_formatting.format_input(gene_query) limit_transcripts = input_formatting.format_input(limit_transcripts) @@ -301,7 +311,8 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build): limit_transcripts = None content = vval.gene2transcripts(gene_query, select_transcripts=limit_transcripts, transcript_set=transcript_set, genome_build=genome_build, - batch_output=True, validator=vval) + batch_output=True, validator=vval, + bypass_genomic_spans=bypass_genomic_spans) except ConnectionError: message = "Cannot connect to rest.genenames.org, please try again later" g2t_object_pool.return_object(vval) @@ -309,9 +320,6 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build): finally: g2t_object_pool.return_object(vval) - # Collect Arguments - args = parser.parse_args() - # Overrides the default response route so that the standard HTML URL can return any specified format if args['content-type'] == 'application/json': # example: http://127.0.0.1:5000.....bob?content-type=application/json diff --git a/rest_VariantValidator/utils/request_parser_g2t.py b/rest_VariantValidator/utils/request_parser_g2t.py new file mode 100644 index 0000000..6a8637e --- /dev/null +++ b/rest_VariantValidator/utils/request_parser_g2t.py @@ -0,0 +1,40 @@ +from flask_restx import reqparse + +# Custom boolean conversion function +def str_to_bool(value): + if isinstance(value, bool): + return value + if value.lower() in ['true', '1', 't', 'yes', 'y']: + return True + elif value.lower() in ['false', '0', 'f', 'no', 'n']: + return False + else: + raise ValueError('Boolean value expected.') + +# Create a RequestParser object to identify specific content-type requests in HTTP URLs +parser = reqparse.RequestParser() +parser.add_argument('content-type', + type=str, + help='***Select the response format***', + choices=['application/json', 'text/xml']) +parser.add_argument('show_exon_info', + type=str_to_bool, + help='***Show Exon structures and alignment data***') + + +# +# Copyright (C) 2016-2025 VariantValidator Contributors +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# From f35d9d772682f27b7638398c845d7d872d951e2c Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Fri, 14 Mar 2025 12:56:38 +0000 Subject: [PATCH 199/218] quick repair to g2t parser --- rest_VariantValidator/utils/request_parser_g2t.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rest_VariantValidator/utils/request_parser_g2t.py b/rest_VariantValidator/utils/request_parser_g2t.py index 6a8637e..c1870c2 100644 --- a/rest_VariantValidator/utils/request_parser_g2t.py +++ b/rest_VariantValidator/utils/request_parser_g2t.py @@ -19,7 +19,8 @@ def str_to_bool(value): choices=['application/json', 'text/xml']) parser.add_argument('show_exon_info', type=str_to_bool, - help='***Show Exon structures and alignment data***') + help='***Show Exon structures and alignment data***', + choices=[True, False]) # From 0be6dccfb7ed83e89df5a33b011e5216b043c71d Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 27 Mar 2025 14:39:26 +0000 Subject: [PATCH 200/218] update test and docs --- Dockerfile | 1 + tests/test_endpoints.py | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index bb82ee4..c3acf3f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,6 +25,7 @@ RUN pip install --upgrade pip # Install the tool RUN pip install -e . +RUN python -m VariantValidator.bin.setup_lovd_syntax_checker # Copy the config file into the container home directory COPY configuration/docker.ini /root/.variantvalidator diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 4d3a969..2a4df33 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -1,11 +1,9 @@ - # Import necessary packages import pytest import time from rest_VariantValidator.app import application # Import your Flask app -# Fixture to set up the test client # Fixture to set up the test client @pytest.fixture(scope='module') def client(): From c44cbcc7583210df5ca86cc91154dac211b8b701 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 27 Mar 2025 14:55:32 +0000 Subject: [PATCH 201/218] switch on syntax check --- rest_VariantValidator/endpoints/variantvalidator_endpoints.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index da9f8b8..8df767d 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -92,7 +92,7 @@ def get(self, genome_build, variant_description, select_transcripts): try: # Validate using the VariantValidator Python Library validate = vval.validate(variant_description, genome_build, select_transcripts, - transcript_set=transcript_model) + transcript_set=transcript_model, lovd_syntax_check=True) content = validate.format_as_dict(with_meta=True) except Exception as e: # Handle the exception and customize the error response @@ -184,7 +184,7 @@ def get(self, genome_build, variant_description, select_transcripts): try: # Validate using the VariantValidator Python Library validate = vval.validate(variant_description, genome_build, select_transcripts, - transcript_set=transcript_model) + transcript_set=transcript_model, lovd_syntax_check=True) content = validate.format_as_dict(with_meta=True) except Exception as e: # Handle the exception and customize the error response From 1b082893c1c69e7170187a5717506e1534fda788 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 9 Apr 2025 14:47:03 +0100 Subject: [PATCH 202/218] update hello endpoint --- rest_VariantValidator/endpoints/hello.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py index fbfdb23..d47d196 100644 --- a/rest_VariantValidator/endpoints/hello.py +++ b/rest_VariantValidator/endpoints/hello.py @@ -2,9 +2,7 @@ from rest_VariantValidator.utils import request_parser, representations, exceptions from rest_VariantValidator.utils.limiter import limiter from flask import abort - -# Import VariantValidator code -import VariantValidator +from rest_VariantValidator.utils.object_pool import vval_object_pool """ Create a parser object locally @@ -31,13 +29,17 @@ class HelloClass(Resource): @api.expect(parser, validate=True) def get(self): - vval = VariantValidator.Validator() + # Import object from vval pool + vval = vval_object_pool.get_object() # Collect Arguments args = parser.parse_args() config_dict = vval.my_config() config_dict['vvseqrepo_db'] = config_dict['vvseqrepo_db'].split('/')[-2] + # Return object to vval pool + vval_object_pool.return_object(vval) + # Overrides the default response route so that the standard HTML URL can return any specified format if args['content-type'] == 'application/json': # example: http://127.0.0.1:5000/name/name/bob?content-type=application/json From fe269c778064585036019ae5d35ec58f79043d67 Mon Sep 17 00:00:00 2001 From: Clayton Wheeler Date: Mon, 14 Apr 2025 10:46:54 -0500 Subject: [PATCH 203/218] Ignore __pycache__ dirs --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index e1400c3..e8d48a8 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,4 @@ build dist rest_VariantValidator.egg-info .DS_Store - - +__pycache__ From b48b4a50bd50c270233b9dd6406418bf314c783a Mon Sep 17 00:00:00 2001 From: Clayton Wheeler Date: Thu, 10 Apr 2025 20:04:13 -0500 Subject: [PATCH 204/218] Enable Flask env var configuration --- docs/MANUAL.md | 16 +++++++++++++++- rest_VariantValidator/app.py | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/MANUAL.md b/docs/MANUAL.md index c36ebbd..7df9de2 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -56,5 +56,19 @@ CustomLog /local/apache2/log/access_log for_pound ``` +## Flask configuration + +rest_variantValidator is a Flask application. Flask can be +[configured](https://flask.palletsprojects.com/en/stable/config/) +using its +[prefixed environment variable](https://flask.palletsprojects.com/en/stable/config/#configuring-from-environment-variables) +configuration mechanism, so documented settings can be set with a prefix +of `FLASK_`. + +This mechanism is also used for Flask extensions such as the +[Flask-Limiter](https://github.com/alisaifee/flask-limiter/tree/master) +rate limiter this uses, which can be disabled by setting +`FLASK_RATELIMIT_ENABLED=`. + ## Additional resources -We are compiling a number of jupyter notebook user guides for rest_variantValidator in [rest_variantValidator_manuals](https://github.com/openvar/rest_variantValidator_manuals) \ No newline at end of file +We are compiling a number of jupyter notebook user guides for rest_variantValidator in [rest_variantValidator_manuals](https://github.com/openvar/rest_variantValidator_manuals) diff --git a/rest_VariantValidator/app.py b/rest_VariantValidator/app.py index 222f26c..b95466c 100644 --- a/rest_VariantValidator/app.py +++ b/rest_VariantValidator/app.py @@ -51,6 +51,7 @@ # Define the application as a Flask app with the name defined by __name__ application = Flask(__name__) +application.config.from_prefixed_env() # Create a limiter instance and attach it to your Flask application limiter.init_app(application) From 0e108e4dbb67448cb709f36a21b3ea696d3ead63 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 28 Apr 2025 16:26:15 +0100 Subject: [PATCH 205/218] Tweak docker workflow to deal with issue https://github.com/openvar/rest_variantValidator/issues/194 --- Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c3acf3f..4341174 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,11 +25,13 @@ RUN pip install --upgrade pip # Install the tool RUN pip install -e . -RUN python -m VariantValidator.bin.setup_lovd_syntax_checker # Copy the config file into the container home directory COPY configuration/docker.ini /root/.variantvalidator +# Install LOVD Syntax checker +RUN python -m VariantValidator.bin.setup_lovd_syntax_checker + # Define the entrypoint as an empty command ENTRYPOINT [] From e4e20d7502cf6bc8abd38fbcbe1bfdff804f68a3 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 8 May 2025 09:53:28 +0100 Subject: [PATCH 206/218] Add in LOVD to g2t --- rest_VariantValidator/endpoints/variantvalidator_endpoints.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 8df767d..600c8f7 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -230,7 +230,7 @@ def get(self, gene_query): gene_query = input_formatting.format_input(gene_query) try: - content = vval.gene2transcripts(gene_query)[0] + content = vval.gene2transcripts(gene_query, lovd_syntax_check=True)[0] except ConnectionError: message = "Cannot connect to rest.genenames.org, please try again later" g2t_object_pool.return_object(vval) @@ -312,7 +312,7 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build): content = vval.gene2transcripts(gene_query, select_transcripts=limit_transcripts, transcript_set=transcript_set, genome_build=genome_build, batch_output=True, validator=vval, - bypass_genomic_spans=bypass_genomic_spans) + bypass_genomic_spans=bypass_genomic_spans, lovd_syntax_check=True) except ConnectionError: message = "Cannot connect to rest.genenames.org, please try again later" g2t_object_pool.return_object(vval) From f3dc012f2d391aa1ac775ee8b294b621a333d556 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 9 Jun 2025 10:00:24 +0100 Subject: [PATCH 207/218] update to python 3.12 --- Dockerfile | 5 +++-- environment.yml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4341174..53dedad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11 +FROM python:3.12.11 # Set the working directory to /app WORKDIR /app @@ -15,7 +15,8 @@ RUN apt update # Install apt managed sofware RUN apt -y install git \ postgresql-client \ - sqlite3 + sqlite3 \ + php # Manage git buffer RUN git config http.postBuffer 500000000 diff --git a/environment.yml b/environment.yml index a04aa4e..3a4438a 100644 --- a/environment.yml +++ b/environment.yml @@ -3,6 +3,6 @@ channels: - conda-forge - bioconda dependencies: - - python==3.11 + - python==3.12.11 - pip From 12c07ee4fff118775aec949e52a611d3e64fdcc0 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 9 Jun 2025 12:47:35 +0100 Subject: [PATCH 208/218] correct mane tx selection guidance in LOVD swagger --- rest_VariantValidator/endpoints/lovd_endpoints.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index cc4eeed..00e8cd3 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -46,8 +46,8 @@ def ordereddict_to_dict(value): "> None or all (all transcripts at the latest versions)\n" "> raw (all transcripts all version)\n" "> select (select transcripts)\n" - "> mane (MANE Select transcripts)\n" - "> mane_select (MANE Select and MANE Plus Clinical transcripts)\n" + "> mane (MANE Select and MANE Plus Clinical transcripts)\n" + "> mane_select (MANE Select transcripts)\n" "\n***Single***\n" "> NM_000093.4\n" "\n***Multiple***\n" From 79cbd22a7d199b958ffc09cea120d82c453acda8 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 17 Jul 2025 14:15:38 +0100 Subject: [PATCH 209/218] Tweaks to new env setup --- pyproject.toml | 109 +++++++++++++++++++--------------------- tests/test_endpoints.py | 2 +- 2 files changed, 52 insertions(+), 59 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b4074d0..1702912 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,52 +1,30 @@ # Project metadata [project] name = "rest_VariantValidator" -dynamic = ["version"] # Use dynamic version based on the setuptools_scm plugin +dynamic = ["version"] # Use dynamic version from setuptools_scm description = "REST API interface for VariantValidator" -license = {file = "LICENSE.txt"} # Specify the license file -authors = [{name = "VariantValidator Contributors", email = "admin@variantvalidator.org"}] # Author information -readme = "README.md" # Specify the README file -keywords = ["bioinformatics", - "computational biology", - "genome variants", - "genome variation", - "genomic variants", - "genomic variation", - "genomics", - "hgvs", - "HGVS", - "sequencevariants"] # Keywords associated with the project - -requires-python = ">=3.6" # Minimum required Python version - -# List of project dependencies -dependencies = [ - # Dependencies that will be installed via PyPi - "httplib2>=0.9.0", - "configparser>=3.5.0", - "dicttoxml==1.7.16", - "gunicorn==21.2.0", - "flask-restx==1.1.0", - "Flask==2.3.2", - "Jinja2==3.1.2", - "Werkzeug==2.3.7", - "MarkupSafe==2.1.3", - "flask-cors==4.0.0", - "flask_httpauth==4.8.0", - "flask_limiter==3.5.1", - - # Dependencies from other repositories, specified with their repository URLs and package names - "vvhgvs@git+https://github.com/openvar/vv_hgvs@master", - "VariantFormatter@git+https://github.com/openvar/variantFormatter@master", - "VariantValidator@git+https://github.com/openvar/variantValidator@master" +license = "AGPL-3.0-only" +license-files = ["LICENSE.txt"] +authors = [{name = "VariantValidator Contributors", email = "admin@variantvalidator.org"}] +readme = "README.md" +keywords = [ + "bioinformatics", + "computational biology", + "genome variants", + "genome variation", + "genomic variants", + "genomic variation", + "genomics", + "hgvs", + "HGVS", + "sequencevariants" ] +requires-python = ">=3.6" -# Project classifiers classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: Software Development :: Build Tools", - "License :: OSI Approved :: AGPL-3.0", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", @@ -55,45 +33,60 @@ classifiers = [ "Programming Language :: Python :: 3.11" ] -# URLs associated with the project +# List of project dependencies +dependencies = [ + # Dependencies that will be installed via PyPi + "httplib2", + "configparser", + "dicttoxml", + "gunicorn", + "flask-restx", + "Flask", + "Jinja2", + "Werkzeug", + "MarkupSafe", + "flask-cors", + "flask_httpauth", + "flask_limiter", + + # Dependencies from other repositories, specified with their repository URLs and package names + "vvhgvs@git+https://github.com/openvar/vv_hgvs@master", + "VariantFormatter@git+https://github.com/openvar/variantFormatter@master", + "VariantValidator@git+https://github.com/openvar/variantValidator@master" +] + +# URLs related to the project [project.urls] -"Homepage" = "https://variantvalidator.org/" -"Source" = "https://github.com/openvar/rest_variantValidator" +Homepage = "https://variantvalidator.org/" +Source = "https://github.com/openvar/rest_variantValidator" "Bug Reports" = "https://github.com/openvar/variantValidator/issues" "Say Thanks!" = "https://www.buymeacoffee.com/VariantValidatr" -# Scripts and data files +# Console scripts exposed by the package [scripts] -# Define scripts that will be available after installation, with their entry points -# After install, these scripts will be accessible for the user to run update_vdb = "bin/update_vdb:main" variant_validator = "bin/variant_validator:main" vv_configure = "bin/vv_configure:main" -# Specify additional data files included in the distribution +# Additional data files to include in the package data = [ { include = "configuration", glob = "configuration/empty_vv_db.sql" } ] -# Setuptools configuration -[tool.setuptools] -# Specify the main Python modules to be included in the distribution -py-modules = ["rest_VariantValidator"] - -# Setuptools SCM configuration +# setuptools SCM for version management [tool.setuptools_scm] -# Setuptools package discovery configuration +# Package discovery configuration [tool.setuptools.packages.find] -# Define where to look for packages (root directory) where = ["."] -# Specify patterns to include and exclude for package discovery include = ["rest_VariantValidator*"] exclude = ["batch", "locust"] # Build system configuration [build-system] -# Specify required dependencies for the build system -requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"] -# Specify the build backend to use +requires = [ + "setuptools>=45", + "setuptools_scm[toml]>=6.2", + "wheel" +] build-backend = "setuptools.build_meta" diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 2a4df33..9d12d45 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -65,7 +65,7 @@ def test_vv_endpoint_multi(client): def test_g2t_endpoint(client): response = client.get('/VariantValidator/tools/gene2transcripts/COL1A1?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK - assert len(response.json) == 6 + assert len(response.json) == 8 time.sleep(1) From 45b347f7c402d994397fec79e7b5e5f6169f928e Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 6 Aug 2025 15:19:35 +0100 Subject: [PATCH 210/218] update version capture --- rest_VariantValidator/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/rest_VariantValidator/__init__.py b/rest_VariantValidator/__init__.py index 663b3fc..09841b4 100644 --- a/rest_VariantValidator/__init__.py +++ b/rest_VariantValidator/__init__.py @@ -1,17 +1,18 @@ -import pkg_resources +import importlib.metadata import re import warnings # Pull in use_scm_version=True enabled version number _is_released_version = False try: - __version__ = pkg_resources.get_distribution("rest_VariantValidator").version + __version__ = importlib.metadata.version("rest_VariantValidator") if re.match(r"^\d+\.\d+\.\d+$", __version__) is not None: _is_released_version = True -except pkg_resources.DistributionNotFound as e: - warnings.warn("can't get __version__ because %s package isn't installed" % __package__, Warning) +except importlib.metadata.PackageNotFoundError: + warnings.warn("can't get __version__ because VariantValidator package isn't installed", Warning) __version__ = None + # # Copyright (C) 2016-2025 VariantValidator Contributors # From 74bcfc91a58c96c582781b72a4fc5309525e45fc Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 10 Sep 2025 16:22:10 +0100 Subject: [PATCH 211/218] Add tests for curl requests into swarms --- locust/__pycache__/test_set.cpython-36.pyc | Bin 7675 -> 0 bytes {locust => swarms}/__init__.py | 0 swarms/locust/__init__.py | 0 {locust => swarms/locust}/locustfile.py | 2 +- {locust => swarms/locust}/test_set.py | 2 +- swarms/tests/__init__.py | 0 .../tests/test_curl_restvv_lovd_endpoint.py | 149 ++++++++++++++ .../test_curl_terracipher_vfv2_endpoint.py | 181 ++++++++++++++++++ 8 files changed, 332 insertions(+), 2 deletions(-) delete mode 100644 locust/__pycache__/test_set.cpython-36.pyc rename {locust => swarms}/__init__.py (100%) create mode 100644 swarms/locust/__init__.py rename {locust => swarms/locust}/locustfile.py (96%) rename {locust => swarms/locust}/test_set.py (99%) create mode 100644 swarms/tests/__init__.py create mode 100644 swarms/tests/test_curl_restvv_lovd_endpoint.py create mode 100644 swarms/tests/test_curl_terracipher_vfv2_endpoint.py diff --git a/locust/__pycache__/test_set.cpython-36.pyc b/locust/__pycache__/test_set.cpython-36.pyc deleted file mode 100644 index dfd094c29f208e81f88de72f4af16989298a74af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7675 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 ayH1~>a|?6(ICV4`H9R!@MD2;%J^uwX_a^ZG diff --git a/locust/__init__.py b/swarms/__init__.py similarity index 100% rename from locust/__init__.py rename to swarms/__init__.py diff --git a/swarms/locust/__init__.py b/swarms/locust/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/locust/locustfile.py b/swarms/locust/locustfile.py similarity index 96% rename from locust/locustfile.py rename to swarms/locust/locustfile.py index 63d22c8..7a4eeef 100644 --- a/locust/locustfile.py +++ b/swarms/locust/locustfile.py @@ -1,4 +1,4 @@ -from locust import HttpUser, TaskSet, task, between +from swarms import HttpUser, TaskSet, task, between import test_set diff --git a/locust/test_set.py b/swarms/locust/test_set.py similarity index 99% rename from locust/test_set.py rename to swarms/locust/test_set.py index f4ad6db..f6c1499 100644 --- a/locust/test_set.py +++ b/swarms/locust/test_set.py @@ -370,6 +370,6 @@ def vf_list(): "NC_000005.9:g.35058667_35058668AG=", "NC_000005.9:g.35058667_35058668AG=" ] - random_number = random.randint(0, 319) + random_number = random.randint(0, len(tests) - 1) odd_task = tests[random_number] return odd_task diff --git a/swarms/tests/__init__.py b/swarms/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/swarms/tests/test_curl_restvv_lovd_endpoint.py b/swarms/tests/test_curl_restvv_lovd_endpoint.py new file mode 100644 index 0000000..cd84d98 --- /dev/null +++ b/swarms/tests/test_curl_restvv_lovd_endpoint.py @@ -0,0 +1,149 @@ +import json +import subprocess +import time + +BASE_URL = "https://www183.lamp.le.ac.uk/LOVD/lovd" +THROTTLE_SECONDS = 0.3 # ~3 requests/sec (API allows 4/sec) + +def run_curl( + variant, + genome_build="GRCh38", + transcript_model="refseq", + select_transcripts="None", + liftover="False", + checkonly="False"): + """ + Run curl against the LOVD VariantValidator API and return parsed JSON. + """ + time.sleep(THROTTLE_SECONDS) # avoid rate limiting + url = ( + f"{BASE_URL}/{genome_build}/{variant}/" + f"{transcript_model}/{select_transcripts}/" + f"{liftover}/{checkonly}" + f"?content-type=application%2Fjson" + ) + cmd = ["curl", "-s", "-X", "GET", url, "-H", "accept: application/json"] + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return json.loads(result.stdout) + + +# ----------------------------- +# Variant Inputs +# ----------------------------- +class TestVariantInputs: + def test_hybrid_syntax_1(self): + data = run_curl("chr17:50198002C>A") + entry = data["chr17:50198002C>A"]["NC_000017.11:g.50198002C>A"] + assert entry["g_hgvs"] == "NC_000017.11:g.50198002C>A" + + def test_hybrid_syntax_2(self): + data = run_curl("17:50198002C>A") + entry = data["17:50198002C>A"]["NC_000017.11:g.50198002C>A"] + assert entry["g_hgvs"] == "NC_000017.11:g.50198002C>A" + + +# ----------------------------- +# Transcript Selection +# ----------------------------- +class TestTranscriptSelection: + def test_transcript_selection_raw(self): + data = run_curl("NC_000005.10:g.140114829del", select_transcripts="raw") + hgvs_t_and_p = data["NC_000005.10:g.140114829del"]["NC_000005.10:g.140114829del"]["hgvs_t_and_p"] + assert "NM_005859.3" in hgvs_t_and_p + assert "NM_005859.4" in hgvs_t_and_p + assert "NM_005859.5" in hgvs_t_and_p + + def test_transcript_selection_all(self): + data = run_curl("NC_000005.10:g.140114829del", select_transcripts="all") + hgvs_t_and_p = data["NC_000005.10:g.140114829del"]["NC_000005.10:g.140114829del"]["hgvs_t_and_p"] + assert "NM_005859.5" in hgvs_t_and_p + assert "NM_005859.3" not in hgvs_t_and_p + assert "NM_005859.4" not in hgvs_t_and_p + + def test_transcript_selection_mane_select(self): + data = run_curl("NC_000005.10:g.140114829del", select_transcripts="mane_select") + hgvs_t_and_p = data["NC_000005.10:g.140114829del"]["NC_000005.10:g.140114829del"]["hgvs_t_and_p"] + assert "NM_005859.5" in hgvs_t_and_p + + def test_transcript_selection_select(self): + data = run_curl("NC_000005.10:g.140114829del", select_transcripts="select") + hgvs_t_and_p = data["NC_000005.10:g.140114829del"]["NC_000005.10:g.140114829del"]["hgvs_t_and_p"] + assert "NM_005859.4" in hgvs_t_and_p + assert "NM_005859.5" in hgvs_t_and_p + assert "NM_005859.3" not in hgvs_t_and_p + + def test_transcript_selection_nm(self): + data = run_curl("NC_000005.10:g.140114829del", select_transcripts="NM_005859.4") + hgvs_t_and_p = data["NC_000005.10:g.140114829del"]["NC_000005.10:g.140114829del"]["hgvs_t_and_p"] + assert "NM_005859.4" in hgvs_t_and_p + assert "NM_005859.3" not in hgvs_t_and_p + assert "NM_005859.5" not in hgvs_t_and_p + + def test_transcript_selection_mane(self): + data = run_curl("NC_000007.14:g.140924703T>C", select_transcripts="mane") + hgvs_t_and_p = data["NC_000007.14:g.140924703T>C"]["NC_000007.14:g.140924703T>C"]["hgvs_t_and_p"] + assert "NM_004333.6" in hgvs_t_and_p + assert "NM_001374258.1" in hgvs_t_and_p + assert "NM_001354609.1" not in hgvs_t_and_p + + +# ----------------------------- +# Auto/Edge Case Variants +# ----------------------------- +class TestVariantAutoCases: + def test_variant1_bad_build(self): + v = "NC_000019.10:g.50378563_50378564insTAC" + data = run_curl(v, genome_build="GRCh37") + entry = data[v][v] + assert "chromosome ID NC_000019.10 is not associated" in entry["genomic_variant_error"] + + def test_variant2_mismatched_reference(self): + v = "11-5248232-A-T" + data = run_curl(v, genome_build="GRCh37") + entry = data[v][v] + assert "does not agree with reference sequence" in entry["genomic_variant_error"] + + def test_variant3_ref_mismatch(self): + v = "NC_000012.11:g.122064777A>C" + data = run_curl(v, genome_build="GRCh37") + entry = data[v][v] + assert "does not agree with reference sequence" in entry["genomic_variant_error"] + + def test_variant4_synonymous(self): + v = "NC_000002.11:g.73613030C>T" + data = run_curl(v, genome_build="GRCh37", select_transcripts="all") + entry = data[v][v] + assert entry["genomic_variant_error"] is None + assert "NM_015120.4" in entry["hgvs_t_and_p"] + + def test_variant5_x_chromosome(self): + v = "NC_000023.10:g.33229673A>T" + data = run_curl(v, genome_build="GRCh37", select_transcripts="raw") + entry = data[v][v] + assert entry["genomic_variant_error"] is None + assert "NM_000109.3" in entry["hgvs_t_and_p"] + + def test_variant6_intergenic(self): + v = "NC_000017.10:g.48279242G>T" + data = run_curl(v, genome_build="GRCh37", select_transcripts="all") + entry = data[v][v] + assert entry["hgvs_t_and_p"] == {"intergenic": {"alt_genomic_loci": None}} + + def test_variant7_identity(self): + v = "NC_000017.10:g.48261457_48261463TTATGTT=" + data = run_curl(v, genome_build="GRCh37", select_transcripts="raw") + entry = data[v][v] + assert "NM_000088.3" in entry["hgvs_t_and_p"] + + def test_variant8_missense(self): + v = "NC_000017.10:g.48275363C>A" + data = run_curl(v, genome_build="GRCh37", select_transcripts="raw") + entry = data[v][v] + assert "NM_000088.3" in entry["hgvs_t_and_p"] + + def test_variant9_roundtrip_consistency(self): + v = "11-5248232-T-A" + data = run_curl(v, genome_build="GRCh37") + entry = data[v][v] + assert entry["p_vcf"] == v + assert entry["genomic_variant_error"] is None diff --git a/swarms/tests/test_curl_terracipher_vfv2_endpoint.py b/swarms/tests/test_curl_terracipher_vfv2_endpoint.py new file mode 100644 index 0000000..31acd54 --- /dev/null +++ b/swarms/tests/test_curl_terracipher_vfv2_endpoint.py @@ -0,0 +1,181 @@ +import json +import subprocess +import time +import os + +# ----------------------------- +# API Base and Throttling +# ----------------------------- +BASE_URL = "https://shaipup.com/market/shaip?owner=uominnovationfactory&shaip=variantvalidatorapi" +THROTTLE_SECONDS = 0 # ~3 requests/sec (API allows 4/sec) + +# Tokens from environment +BEARER_TOKEN = os.getenv("SHAIP_BEARER_TOKEN") +COOKIE_TOKEN = os.getenv("SHAIP_COOKIE_TOKEN") + +if not BEARER_TOKEN: + raise RuntimeError("Please set SHAIP_BEARER_TOKEN in your environment") +if not COOKIE_TOKEN: + raise RuntimeError("Please set SHAIP_COOKIE_TOKEN in your environment") + + +def run_curl( + variant, + genome_build="GRCh38", + transcript_model="refseq", + select_transcripts="None", + liftover="False", + checkonly="False", +): + """ + Run curl against the TerraCipher VariantValidator API and return parsed JSON. + Uses POST with JSON body. + """ + time.sleep(THROTTLE_SECONDS) # avoid rate limiting + + # Build JSON payload + payload = [ + { + "select_transcripts": [select_transcripts], + "liftover": liftover.lower() == "true", + "checkonly": checkonly.lower() == "true", + "transcript_model": transcript_model, + "genome_build": genome_build, + "variant_description": [variant], + } + ] + + cmd = [ + "curl", + "--location", + "--request", "POST", BASE_URL, + "--header", f"Authorization: Bearer {BEARER_TOKEN}", + "--header", "Content-Type: application/json", + "--header", f"Cookie: {COOKIE_TOKEN}", + "--data-raw", json.dumps(payload), + "-s", # silent + ] + + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return json.loads(result.stdout) + +# ----------------------------- +# Variant Inputs +# ----------------------------- +class TestVariantInputs: + def test_hybrid_syntax_1(self): + data = run_curl("chr17:50198002C>A") + entry = data["chr17:50198002C>A"]["NC_000017.11:g.50198002C>A"] + assert entry["g_hgvs"] == "NC_000017.11:g.50198002C>A" + + def test_hybrid_syntax_2(self): + data = run_curl("17:50198002C>A") + entry = data["17:50198002C>A"]["NC_000017.11:g.50198002C>A"] + assert entry["g_hgvs"] == "NC_000017.11:g.50198002C>A" + + +# ----------------------------- +# Transcript Selection +# ----------------------------- +class TestTranscriptSelection: + def test_transcript_selection_raw(self): + data = run_curl("NC_000005.10:g.140114829del", select_transcripts="raw") + hgvs_t_and_p = data["NC_000005.10:g.140114829del"]["NC_000005.10:g.140114829del"]["hgvs_t_and_p"] + assert "NM_005859.3" in hgvs_t_and_p + assert "NM_005859.4" in hgvs_t_and_p + assert "NM_005859.5" in hgvs_t_and_p + + def test_transcript_selection_all(self): + data = run_curl("NC_000005.10:g.140114829del", select_transcripts="all") + hgvs_t_and_p = data["NC_000005.10:g.140114829del"]["NC_000005.10:g.140114829del"]["hgvs_t_and_p"] + assert "NM_005859.5" in hgvs_t_and_p + assert "NM_005859.3" not in hgvs_t_and_p + assert "NM_005859.4" not in hgvs_t_and_p + + def test_transcript_selection_mane_select(self): + data = run_curl("NC_000005.10:g.140114829del", select_transcripts="mane_select") + hgvs_t_and_p = data["NC_000005.10:g.140114829del"]["NC_000005.10:g.140114829del"]["hgvs_t_and_p"] + assert "NM_005859.5" in hgvs_t_and_p + + def test_transcript_selection_select(self): + data = run_curl("NC_000005.10:g.140114829del", select_transcripts="select") + hgvs_t_and_p = data["NC_000005.10:g.140114829del"]["NC_000005.10:g.140114829del"]["hgvs_t_and_p"] + assert "NM_005859.4" in hgvs_t_and_p + assert "NM_005859.5" in hgvs_t_and_p + assert "NM_005859.3" not in hgvs_t_and_p + + def test_transcript_selection_nm(self): + data = run_curl("NC_000005.10:g.140114829del", select_transcripts="NM_005859.4") + hgvs_t_and_p = data["NC_000005.10:g.140114829del"]["NC_000005.10:g.140114829del"]["hgvs_t_and_p"] + assert "NM_005859.4" in hgvs_t_and_p + assert "NM_005859.3" not in hgvs_t_and_p + assert "NM_005859.5" not in hgvs_t_and_p + + def test_transcript_selection_mane(self): + data = run_curl("NC_000007.14:g.140924703T>C", select_transcripts="mane") + hgvs_t_and_p = data["NC_000007.14:g.140924703T>C"]["NC_000007.14:g.140924703T>C"]["hgvs_t_and_p"] + assert "NM_004333.6" in hgvs_t_and_p + assert "NM_001374258.1" in hgvs_t_and_p + assert "NM_001354609.1" not in hgvs_t_and_p + + +# ----------------------------- +# Auto/Edge Case Variants +# ----------------------------- +class TestVariantAutoCases: + def test_variant1_bad_build(self): + v = "NC_000019.10:g.50378563_50378564insTAC" + data = run_curl(v, genome_build="GRCh37") + entry = data[v][v] + assert "chromosome ID NC_000019.10 is not associated" in entry["genomic_variant_error"] + + def test_variant2_mismatched_reference(self): + v = "11-5248232-A-T" + data = run_curl(v, genome_build="GRCh37") + entry = data[v][v] + assert "does not agree with reference sequence" in entry["genomic_variant_error"] + + def test_variant3_ref_mismatch(self): + v = "NC_000012.11:g.122064777A>C" + data = run_curl(v, genome_build="GRCh37") + entry = data[v][v] + assert "does not agree with reference sequence" in entry["genomic_variant_error"] + + def test_variant4_synonymous(self): + v = "NC_000002.11:g.73613030C>T" + data = run_curl(v, genome_build="GRCh37", select_transcripts="all") + entry = data[v][v] + assert entry["genomic_variant_error"] is None + assert "NM_015120.4" in entry["hgvs_t_and_p"] + + def test_variant5_x_chromosome(self): + v = "NC_000023.10:g.33229673A>T" + data = run_curl(v, genome_build="GRCh37", select_transcripts="raw") + entry = data[v][v] + assert entry["genomic_variant_error"] is None + assert "NM_000109.3" in entry["hgvs_t_and_p"] + + def test_variant6_intergenic(self): + v = "NC_000017.10:g.48279242G>T" + data = run_curl(v, genome_build="GRCh37", select_transcripts="all") + entry = data[v][v] + assert entry["hgvs_t_and_p"] == {"intergenic": {"alt_genomic_loci": None}} + + def test_variant7_identity(self): + v = "NC_000017.10:g.48261457_48261463TTATGTT=" + data = run_curl(v, genome_build="GRCh37", select_transcripts="raw") + entry = data[v][v] + assert "NM_000088.3" in entry["hgvs_t_and_p"] + + def test_variant8_missense(self): + v = "NC_000017.10:g.48275363C>A" + data = run_curl(v, genome_build="GRCh37", select_transcripts="raw") + entry = data[v][v] + assert "NM_000088.3" in entry["hgvs_t_and_p"] + + def test_variant9_roundtrip_consistency(self): + v = "11-5248232-T-A" + data = run_curl(v, genome_build="GRCh37") + entry = data[v][v] + assert entry["p_vcf"] == v + assert entry["genomic_variant_error"] is None From ddec20e963ba38a02b0782aaee86e1c5cee8b8c4 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Wed, 8 Oct 2025 16:38:31 +0100 Subject: [PATCH 212/218] update ini --- rest_VariantValidator/endpoints/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rest_VariantValidator/endpoints/__init__.py b/rest_VariantValidator/endpoints/__init__.py index bad2ffa..49c3025 100644 --- a/rest_VariantValidator/endpoints/__init__.py +++ b/rest_VariantValidator/endpoints/__init__.py @@ -84,6 +84,9 @@ def specs_url(self): api.add_namespace(ns_vf) api.add_namespace(ns_lovd) api.add_namespace(ns_hello) +if ns_auth is not None: + api.add_namespace(ns_auth, path='/auth') # Mount auth endpoints under /auth + # From d844dcaefd62f0d82130e2dad50ca1b5b1d5cbb5 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Tue, 14 Oct 2025 13:14:56 +0100 Subject: [PATCH 213/218] Add additional params for DBauth --- .../endpoints/lovd_endpoints.py | 2 +- .../endpoints/variantformatter_endpoints.py | 2 +- .../endpoints/variantvalidator_endpoints.py | 10 +++--- .../tests/test_curl_restvv_lovd_endpoint.py | 34 +++++++++++++------ 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index 00e8cd3..ed57af6 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -71,7 +71,7 @@ class LOVDClass(Resource): @api.expect(parser, validate=True) @auth.login_required() @limiter.limit("4/second") - def get(self, genome_build, variant_description, transcript_model, select_transcripts, checkonly, liftover): + def get(self, genome_build, variant_description, transcript_model, select_transcripts, checkonly, liftover, user_id=None): if transcript_model == 'None' or transcript_model == 'none': transcript_model = None if select_transcripts == 'None' or select_transcripts == 'none': diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index de3550d..f9c4fe3 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -55,7 +55,7 @@ class VariantFormatterClass(Resource): @api.expect(parser, validate=True) @auth.login_required() @limiter.limit("4/second") - def get(self, genome_build, variant_description, transcript_model, select_transcripts, checkonly): + def get(self, genome_build, variant_description, transcript_model, select_transcripts, checkonly, user_id=None): if transcript_model == 'None' or transcript_model == 'none': transcript_model = None if select_transcripts == 'None' or select_transcripts == 'none': diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index 600c8f7..fc10751 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -59,7 +59,7 @@ class VariantValidatorClass(Resource): @api.expect(parser, validate=True) @auth.login_required() @limiter.limit("2/second") - def get(self, genome_build, variant_description, select_transcripts): + def get(self, genome_build, variant_description, select_transcripts, user_id=None): # Import object from vval pool vval = vval_object_pool.get_object() @@ -151,7 +151,7 @@ class VariantValidatorEnsemblClass(Resource): @api.expect(parser, validate=True) @auth.login_required() @limiter.limit("2/second") - def get(self, genome_build, variant_description, select_transcripts): + def get(self, genome_build, variant_description, select_transcripts, user_id=None): # Import object from vval pool vval = vval_object_pool.get_object() @@ -221,7 +221,7 @@ class Gene2transcriptsClass(Resource): @api.expect(parser, validate=True) @auth.login_required() @limiter.limit("1/second") - def get(self, gene_query): + def get(self, gene_query, user_id=None): # Get vvval object from pool vval = g2t_object_pool.get_object() @@ -284,7 +284,7 @@ class Gene2transcriptsV2Class(Resource): @api.expect(parser_g2t, validate=True) @auth.login_required() @limiter.limit("1/second") - def get(self, gene_query, limit_transcripts, transcript_set, genome_build): + def get(self, gene_query, limit_transcripts, transcript_set, genome_build, user_id=None): # Get vval object from pool vval = g2t_object_pool.get_object() @@ -342,7 +342,7 @@ class Hgvs2referenceClass(Resource): @api.expect(parser, validate=True) @auth.login_required() @limiter.limit("4/second") - def get(self, hgvs_description): + def get(self, hgvs_description, user_id=None): # Get vval object from pool vval = vval_object_pool.get_object() diff --git a/swarms/tests/test_curl_restvv_lovd_endpoint.py b/swarms/tests/test_curl_restvv_lovd_endpoint.py index cd84d98..2993908 100644 --- a/swarms/tests/test_curl_restvv_lovd_endpoint.py +++ b/swarms/tests/test_curl_restvv_lovd_endpoint.py @@ -1,7 +1,9 @@ import json import subprocess import time +import os +# Updated base URL BASE_URL = "https://www183.lamp.le.ac.uk/LOVD/lovd" THROTTLE_SECONDS = 0.3 # ~3 requests/sec (API allows 4/sec) @@ -14,22 +16,40 @@ def run_curl( checkonly="False"): """ Run curl against the LOVD VariantValidator API and return parsed JSON. + Automatically includes Authorization header if RESTVV_BEARER_TOKEN is set. """ time.sleep(THROTTLE_SECONDS) # avoid rate limiting + url = ( f"{BASE_URL}/{genome_build}/{variant}/" f"{transcript_model}/{select_transcripts}/" f"{liftover}/{checkonly}" f"?content-type=application%2Fjson" ) - cmd = ["curl", "-s", "-X", "GET", url, "-H", "accept: application/json"] + + # Get bearer token from environment (set by your token script) + bearer_token = os.getenv("RESTVV_BEARER_TOKEN") + + # Build curl command + cmd = [ + "curl", "-s", "-X", "GET", url, + "-H", "accept: application/json", + "-H", "Content-Type: application/json" + ] + + # Add Authorization header if token exists + if bearer_token: + cmd += ["-H", f"Authorization: Bearer {bearer_token}"] + + # Run curl and parse output result = subprocess.run(cmd, capture_output=True, text=True, check=True) return json.loads(result.stdout) -# ----------------------------- -# Variant Inputs -# ----------------------------- +# ------------------------------------------------------------------------- +# All your existing test classes stay exactly as they were +# ------------------------------------------------------------------------- + class TestVariantInputs: def test_hybrid_syntax_1(self): data = run_curl("chr17:50198002C>A") @@ -42,9 +62,6 @@ def test_hybrid_syntax_2(self): assert entry["g_hgvs"] == "NC_000017.11:g.50198002C>A" -# ----------------------------- -# Transcript Selection -# ----------------------------- class TestTranscriptSelection: def test_transcript_selection_raw(self): data = run_curl("NC_000005.10:g.140114829del", select_transcripts="raw") @@ -87,9 +104,6 @@ def test_transcript_selection_mane(self): assert "NM_001354609.1" not in hgvs_t_and_p -# ----------------------------- -# Auto/Edge Case Variants -# ----------------------------- class TestVariantAutoCases: def test_variant1_bad_build(self): v = "NC_000019.10:g.50378563_50378564insTAC" From 32cdc0c7ca3a57ebd5331cd713f6bc8e6ebc07e7 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Tue, 14 Oct 2025 14:50:22 +0100 Subject: [PATCH 214/218] Draft the auth user manual --- docs/USER_MANUAL.md | 204 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 docs/USER_MANUAL.md diff --git a/docs/USER_MANUAL.md b/docs/USER_MANUAL.md new file mode 100644 index 0000000..bbbdcb1 --- /dev/null +++ b/docs/USER_MANUAL.md @@ -0,0 +1,204 @@ +## 1. Request a User Account via Swagger API + +The VariantValidator API provides a Swagger interface to submit account requests interactively or via JSON requests. + +1. Navigate to [https://www183.lamp.le.ac.uk/](https://www183.lamp.le.ac.uk/) +2. Locate the **`POST /auth/request_account`** endpoint. +3. Click **"Try it out"**. +4. Fill in the fields in the form: + +- **Username** – your desired username (3–20 characters: letters, numbers, underscores, dots, or dashes). +- **Primary Email** – your main email address. If this is not a workplace email, you must provide additional information in the optional fields to help verify your identity. +- **Professional Email** – optional, use if you have a work email. +- **ORCID** – optional ORCID identifier. +- **Link to Company Profile** – optional link to a public company profile or other identifying information. + +**Important:** Users must provide sufficient identifying information, particularly if the primary email is not a workplace email. Insufficient information may result in the account request being rejected. + +5. Click **"Execute"** to submit the request. + +Once submitted, your account request will be reviewed by an administrator. You will receive an email notification once the account is approved or rejected. + +--- + +## 2. Manage Your Account + +Once your account has been approved, you can reset your password, generate new access tokens, and view your account details. The API supports several authentication methods for scripts and CLI: + +- **Basic Auth** – username and password in the HTTP header (standard user/password authentication). +- **Bearer Token Auth** – using the token returned from `/auth/new_token`. +- **JSON body** – for endpoints that accept credentials in the body (e.g., `/auth/reset_password`). +- **Query parameters** – for Swagger-generated curl commands. + +--- + +### 2.1 Set a New Password + +Endpoint: `/auth/reset_password` + +```bash +curl -X POST https://www183.lamp.le.ac.uk/auth/reset_password \ + -H "Content-Type: application/json" \ + -d '{ + "username": "your_username", + "current_password": "current_password_here", + "new_password": "new_secure_password_here" + }' +``` + +- You must provide your current password to set a new one. +- Passwords should be strong (letters, numbers, symbols). + +--- + +### 2.2 Generate a New Token + +Endpoint: `/auth/new_token` + +Authenticate using Basic Auth to generate a Bearer token: + +```bash +curl -X POST https://www183.lamp.le.ac.uk/auth/new_token \ + -u 'your_username:your_password' +``` + +Response: + +```json +{ + "your_token": "", + "token_type": "Bearer" +} +``` + +You can now use this token for endpoints that require authentication. + +--- + +### 2.3 View Your Account Details + +Endpoint: `/auth/myaccount` + +#### a) Standard Basic Auth (user/password) + +```bash +curl -v -X POST https://www183.lamp.le.ac.uk/auth/myaccount \ + -u 'your_username:your_password' +``` + +#### b) Bearer Token + +```bash +curl -v -X POST https://www183.lamp.le.ac.uk/auth/myaccount \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer " +``` + +#### c) JSON body (for endpoints that accept it) + +```bash +curl -X POST https://www183.lamp.le.ac.uk/auth/myaccount \ + -H "Content-Type: application/json" \ + -d '{ + "username": "your_username", + "current_password": "your_password" + }' +``` + +#### d) Query parameters (for Swagger cURL) + +```bash +curl -X POST "https://www183.lamp.le.ac.uk/auth/myaccount?username=your_username¤t_password=your_password" +``` + +Response: + +```json +{ + "access_token": "", + "token_type": "Bearer", + "expires_in_days": 365 +} +``` + +--- + +**Notes:** + +- The API checks authentication methods in the following order: Bearer token → Basic Auth → JSON body → Query params. +- If you lose your token, reset it via `/auth/reset_password`. +- Keep your tokens and passwords secure. Tokens are valid until they expire, as indicated in `expires_in_days`. + +--- + +## 3 Workflow Endpoint Examples + +### 3.1 Curl Examples + +- Using Bearer token for a workflow endpoint: + +```bash +curl -X POST https://www183.lamp.le.ac.uk/workflow/run_analysis \ + -H "Authorization: Bearer " \ + -d '{"sample_id": "12345"}' +``` + +- Using standard username/password Basic Auth for a workflow endpoint: + +```bash +curl -X POST https://www183.lamp.le.ac.uk/workflow/run_analysis \ + -u 'your_username:your_password' \ + -d '{"sample_id": "12345"}' +``` + +### 3.2 Python `requests` Examples + +You can also call the workflow endpoints from Python using the `requests` library. + +- **Using Bearer Token** + +```python +import requests + +url = "https://www183.lamp.le.ac.uk/workflow/run_analysis" +headers = { + "Authorization": "Bearer ", + "Content-Type": "application/json" +} +data = { + "sample_id": "12345" +} + +response = requests.post(url, headers=headers, json=data) +print(response.status_code) +print(response.json()) +``` + +- **Using Basic Auth (username/password)** + +```python +import requests +from requests.auth import HTTPBasicAuth + +url = "https://www183.lamp.le.ac.uk/workflow/run_analysis" +data = { + "sample_id": "12345" +} + +response = requests.post(url, auth=HTTPBasicAuth('your_username', 'your_password'), json=data) +print(response.status_code) +print(response.json()) +``` + +**Notes:** + +- Make sure to replace `` or `'your_username'` / `'your_password'` with your actual credentials. +- `requests` automatically handles HTTPS connections. +- For large workflows or multiple requests, consider using a session to reuse connections: + +```python +with requests.Session() as session: + session.auth = HTTPBasicAuth('your_username', 'your_password') + response = session.post(url, json=data) + print(response.json()) +``` From 254c5b9cc3156a64d6261c438d871566c152428b Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 16 Oct 2025 14:06:20 +0100 Subject: [PATCH 215/218] add test path --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 1702912..fb3666b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,3 +90,7 @@ requires = [ "wheel" ] build-backend = "setuptools.build_meta" + +# Pytest configuration +[tool.pytest.ini_options] +testpaths = ["tests"] \ No newline at end of file From 24d38f8e3dd45522bf48dcda55665874d095d7f6 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Mon, 20 Oct 2025 11:27:40 +0100 Subject: [PATCH 216/218] Make API call trailing slash agnostic --- rest_VariantValidator/endpoints/hello.py | 2 +- rest_VariantValidator/endpoints/lovd_endpoints.py | 2 +- .../endpoints/variantformatter_endpoints.py | 2 +- .../endpoints/variantvalidator_endpoints.py | 12 +++++++----- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/rest_VariantValidator/endpoints/hello.py b/rest_VariantValidator/endpoints/hello.py index d47d196..ea81e7b 100644 --- a/rest_VariantValidator/endpoints/hello.py +++ b/rest_VariantValidator/endpoints/hello.py @@ -22,7 +22,7 @@ """ -@api.route("/") +@api.route("/", strict_slashes=False) class HelloClass(Resource): # Add documentation about the parser diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index ed57af6..778cb8f 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -27,7 +27,7 @@ def ordereddict_to_dict(value): @api.route("/lovd////" - "//") + "//", strict_slashes=False) @api.doc(description="This endpoint has a rate limit of 4 requests per second.") @api.param("variant_description", "***Genomic HGVS***\n" "> - NC_000017.10:g.48275363C>A\n" diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index f9c4fe3..a4a0332 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -18,7 +18,7 @@ @api.route("/variantformatter////" - "/") + "/", strict_slashes=False) @api.doc(description="This endpoint has a rate limit of 4 requests per second.") @api.param("variant_description", "***Genomic HGVS***\n" "> - NC_000017.10:g.48275363C>A\n" diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index fc10751..ebe28af 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -19,7 +19,8 @@ api = Namespace('VariantValidator', description='VariantValidator API Endpoints') -@api.route("/variantvalidator///") +@api.route("/variantvalidator///", + strict_slashes=False) @api.doc(description="This endpoint has a rate limit of 2 requests per second.") @api.param("select_transcripts", "***Return all possible transcripts***\n" "\n***Return only 'select' transcripts***\n" @@ -114,7 +115,8 @@ def get(self, genome_build, variant_description, select_transcripts, user_id=Non # Return the api default output return content -@api.route("/variantvalidator_ensembl///") +@api.route("/variantvalidator_ensembl///", + strict_slashes=False) @api.doc(description="This endpoint has a rate limit of 2 requests per second.") @api.param("select_transcripts", "***Return all possible transcripts***\n" "\n***Return only 'select' transcripts***\n" @@ -207,7 +209,7 @@ def get(self, genome_build, variant_description, select_transcripts, user_id=Non return content -@api.route("/tools/gene2transcripts/") +@api.route("/tools/gene2transcripts/", strict_slashes=False) @api.doc(description="This endpoint has a rate limit of 1 request per second.") @api.param("gene_query", "***HGNC gene symbol, HGNC ID, or transcript ID***\n" "\nCurrent supported transcript IDs" @@ -254,7 +256,7 @@ def get(self, gene_query, user_id=None): @api.route("/tools/gene2transcripts_v2////" - "") + "", strict_slashes=False) @api.doc(description="This endpoint has a rate limit of 1 request per second.") @api.param("gene_query", "***HGNC gene symbol, HGNC ID, or transcript ID***\n" "\nCurrent supported transcript IDs" @@ -332,7 +334,7 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build, user_ return content -@api.route("/tools/hgvs2reference/") +@api.route("/tools/hgvs2reference/", strict_slashes=False) @api.param("hgvs_description", "***hgvs_description***\n" "\nSequence variation description in the HGVS format\n" "\n *Intronic descriptions in the context of transcript reference sequences are currently " From 3c5afc54df376793a2b7cd373c67aaf5cbc01439 Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Thu, 23 Oct 2025 09:20:17 +0100 Subject: [PATCH 217/218] Develop new code for dynamic limiing to improve performance --- .../endpoints/lovd_endpoints.py | 45 ++--- .../endpoints/variantformatter_endpoints.py | 27 ++- .../endpoints/variantvalidator_endpoints.py | 183 +++--------------- rest_VariantValidator/utils/limiter.py | 52 +++-- tests/test_endpoints.py | 38 ++-- 5 files changed, 112 insertions(+), 233 deletions(-) diff --git a/rest_VariantValidator/endpoints/lovd_endpoints.py b/rest_VariantValidator/endpoints/lovd_endpoints.py index 778cb8f..58f2bc1 100644 --- a/rest_VariantValidator/endpoints/lovd_endpoints.py +++ b/rest_VariantValidator/endpoints/lovd_endpoints.py @@ -3,7 +3,7 @@ from flask_restx import Namespace, Resource from rest_VariantValidator.utils import request_parser, representations, input_formatting from rest_VariantValidator.utils.object_pool import simple_variant_formatter_pool -from rest_VariantValidator.utils.limiter import limiter +from rest_VariantValidator.utils.limiter import limiter, formatter_pool_limit # get login authentication, if needed, or dummy auth if not present try: from VariantValidator_APIs.db_auth.verify_password import auth @@ -28,7 +28,7 @@ def ordereddict_to_dict(value): @api.route("/lovd////" "//", strict_slashes=False) -@api.doc(description="This endpoint has a rate limit of 4 requests per second.") +@api.doc(description="This endpoint uses a dynamic rate limit based on pool availability.") @api.param("variant_description", "***Genomic HGVS***\n" "> - NC_000017.10:g.48275363C>A\n" "\n***Pseudo-VCF***\n" @@ -67,57 +67,58 @@ def ordereddict_to_dict(value): "> - primary - (lift to primary assembly only)\n" "> - False") class LOVDClass(Resource): - # Add documentation about the parser @api.expect(parser, validate=True) @auth.login_required() - @limiter.limit("4/second") + @limiter.limit(formatter_pool_limit) # <-- dynamic limiter def get(self, genome_build, variant_description, transcript_model, select_transcripts, checkonly, liftover, user_id=None): - if transcript_model == 'None' or transcript_model == 'none': + # Normalize input values + if transcript_model in ('None', 'none'): transcript_model = None - if select_transcripts == 'None' or select_transcripts == 'none': + if select_transcripts in ('None', 'none'): select_transcripts = None - if checkonly == 'False' or checkonly == 'false': + if checkonly in ('False', 'false'): checkonly = False - if checkonly == 'True' or checkonly == 'true': + if checkonly in ('True', 'true'): checkonly = True - if liftover == 'True' or liftover == 'true': + if liftover in ('True', 'true'): liftover = True - if liftover == 'False' or liftover == 'false': + if liftover in ('False', 'false'): liftover = False - # Import formatter object from pool + # Get a formatter from the pool simple_formatter = simple_variant_formatter_pool.get() # Convert inputs to JSON arrays variant_description = input_formatting.format_input(variant_description) select_transcripts = input_formatting.format_input(select_transcripts) + if select_transcripts == '["all"]': + select_transcripts = "all" + if select_transcripts == '["raw"]': + select_transcripts = "raw" try: - content = simple_formatter.format(variant_description, genome_build, transcript_model, - select_transcripts, checkonly, liftover) + content = simple_formatter.format( + variant_description, genome_build, transcript_model, + select_transcripts, checkonly, liftover + ) except Exception as e: - # Handle the exception and customize the error response return {"error": str(e)}, 500 finally: simple_variant_formatter_pool.return_object(simple_formatter) + # Convert OrderedDict to normal dict to_dict = ordereddict_to_dict(content) - content = str(to_dict) - content = content.replace("'", '"') - content = ast.literal_eval(content) + content = ast.literal_eval(str(to_dict).replace("'", '"')) - # Collect Arguments + # Parse query arguments args = parser.parse_args() - # Overrides the default response route so that the standard HTML URL can return any specified format + # Return content in requested format if args['content-type'] == 'application/json': - # example: http://127.0.0.1:5000.....bob?content-type=application/json return representations.application_json(content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=text/xml elif args['content-type'] == 'text/xml': return representations.xml(str(content), 200, None) else: - # Return the api default output return content diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index a4a0332..9c9ba01 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -2,7 +2,7 @@ from flask_restx import Namespace, Resource from rest_VariantValidator.utils import request_parser, representations, input_formatting from rest_VariantValidator.utils.object_pool import simple_variant_formatter_pool -from rest_VariantValidator.utils.limiter import limiter +from rest_VariantValidator.utils.limiter import limiter, formatter_pool_limit # get login authentication, if needed, or dummy auth if not present try: from VariantValidator_APIs.db_auth.verify_password import auth @@ -19,7 +19,7 @@ @api.route("/variantformatter////" "/", strict_slashes=False) -@api.doc(description="This endpoint has a rate limit of 4 requests per second.") +@api.doc(description="This endpoint uses a dynamic rate limit based on pool availability.") @api.param("variant_description", "***Genomic HGVS***\n" "> - NC_000017.10:g.48275363C>A\n" "\n***Pseudo-VCF***\n" @@ -54,18 +54,19 @@ class VariantFormatterClass(Resource): # Add documentation about the parser @api.expect(parser, validate=True) @auth.login_required() - @limiter.limit("4/second") + @limiter.limit(formatter_pool_limit) # <-- dynamic limiter def get(self, genome_build, variant_description, transcript_model, select_transcripts, checkonly, user_id=None): - if transcript_model == 'None' or transcript_model == 'none': + # Normalize input values + if transcript_model in ('None', 'none'): transcript_model = None - if select_transcripts == 'None' or select_transcripts == 'none': + if select_transcripts in ('None', 'none'): select_transcripts = None - if checkonly == 'False' or checkonly == 'false': + if checkonly in ('False', 'false'): checkonly = False - if checkonly == 'True' or checkonly == 'true': + if checkonly in ('True', 'true'): checkonly = True - # Import formatter from pool + # Get a formatter from the pool simple_formatter = simple_variant_formatter_pool.get() # Convert inputs to JSON arrays @@ -80,27 +81,23 @@ def get(self, genome_build, variant_description, transcript_model, select_transc content = simple_formatter.format(variant_description, genome_build, transcript_model, select_transcripts, checkonly) except Exception as e: - # Handle the exception and customize the error response return {"error": str(e)}, 500 finally: simple_variant_formatter_pool.return_object(simple_formatter) - # Collect Arguments + # Parse query arguments args = parser.parse_args() - # Overrides the default response route so that the standard HTML URL can return any specified format + # Return content in requested format if args['content-type'] == 'application/json': - # example: http://127.0.0.1:5000.....bob?content-type=application/json return representations.application_json(content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=text/xml elif args['content-type'] == 'text/xml': return representations.xml(content, 200, None) else: - # Return the api default output return content # -# Copyright (C) 2016-2025 VariantValidator Contributors +# Copyright (C) 2016-2021 VariantValidator Contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index ebe28af..115dccf 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -2,17 +2,14 @@ from flask_restx import Namespace, Resource from rest_VariantValidator.utils import exceptions, request_parser, representations, input_formatting, request_parser_g2t from rest_VariantValidator.utils.object_pool import vval_object_pool, g2t_object_pool -from rest_VariantValidator.utils.limiter import limiter +from rest_VariantValidator.utils.limiter import limiter, vval_pool_limit, g2t_pool_limit # get login authentication, if needed, or dummy auth if not present try: from VariantValidator_APIs.db_auth.verify_password import auth except ModuleNotFoundError: from rest_VariantValidator.utils.verify_password import auth - -""" -Create a parser object locally -""" +# Create parser objects locally parser = request_parser.parser parser_g2t = request_parser_g2t.parser @@ -21,7 +18,7 @@ @api.route("/variantvalidator///", strict_slashes=False) -@api.doc(description="This endpoint has a rate limit of 2 requests per second.") +@api.doc(description="This endpoint uses a dynamic rate limit based on pool availability.") @api.param("select_transcripts", "***Return all possible transcripts***\n" "\n***Return only 'select' transcripts***\n" "> select\n" @@ -56,22 +53,19 @@ "> - hg19\n" "> - hg38") class VariantValidatorClass(Resource): - # Add documentation about the parser @api.expect(parser, validate=True) @auth.login_required() - @limiter.limit("2/second") + @limiter.limit(vval_pool_limit) def get(self, genome_build, variant_description, select_transcripts, user_id=None): - # Import object from vval pool + # Get object from vval pool vval = vval_object_pool.get_object() - # set transcript_model transcript_model = "refseq" - # Switch off select_transcripts = all or raw for genomic variants + # Deprecated check for 'all' or 'raw' for genomic variants if ("all" in select_transcripts or "raw" in select_transcripts) and "auth" not in select_transcripts: - if "c." not in variant_description and "n." not in variant_description and "r." not in variant_description \ - and "p." not in variant_description: + if not any(x in variant_description for x in ("c.", "n.", "r.", "p.")): return {"Not Found": "Setting select_transcripts to 'all' or 'raw' is deprecated for genomic " "variant processing using this endpoint. Contact admin on " "https://variantvalidator.org/help/contact/ for updated instructions and" @@ -82,89 +76,41 @@ def get(self, genome_build, variant_description, select_transcripts, user_id=Non elif "auth_raw" in select_transcripts: select_transcripts = "raw" - # Convert inputs to JSON arrays variant_description = input_formatting.format_input(variant_description) select_transcripts = input_formatting.format_input(select_transcripts) - if select_transcripts == '["all"]': - select_transcripts = "all" - if select_transcripts == '["raw"]': - select_transcripts = "raw" + if select_transcripts == '["all"]': select_transcripts = "all" + if select_transcripts == '["raw"]': select_transcripts = "raw" try: - # Validate using the VariantValidator Python Library validate = vval.validate(variant_description, genome_build, select_transcripts, transcript_set=transcript_model, lovd_syntax_check=True) content = validate.format_as_dict(with_meta=True) except Exception as e: - # Handle the exception and customize the error response return {"error": str(e)}, 500 finally: vval_object_pool.return_object(vval) - # Collect Arguments args = parser.parse_args() - - # Overrides the default response route so that the standard HTML URL can return any specified format if args['content-type'] == 'application/json': - # example: http://127.0.0.1:5000.....bob?content-type=application/json return representations.application_json(content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=text/xml elif args['content-type'] == 'text/xml': return representations.xml(content, 200, None) else: - # Return the api default output return content + @api.route("/variantvalidator_ensembl///", strict_slashes=False) -@api.doc(description="This endpoint has a rate limit of 2 requests per second.") -@api.param("select_transcripts", "***Return all possible transcripts***\n" - "\n***Return only 'select' transcripts***\n" - "> select\n" - "> mane_select\n" - "> mane (MANE and MANE Plus Clinical)\n" - "> refseq_select\n" - "\n***Single***\n" - "> ENST00000225964.10\n" - "\n***Multiple***\n" - "> ENST00000225964.9|ENST00000225964.10") -@api.param("variant_description", "***HGVS***\n" - "> - ENST00000225964.10:c.589G>T\n" - "> - NC_000017.10:g.48275363C>A\n" - "\n***Pseudo-VCF***\n" - "> - 17-50198002-C-A\n" - "> - 17:50198002:C:A\n" - "> - GRCh38-17-50198002-C-A\n" - "> - GRCh38:17:50198002:C:A\n" - "\n***Hybrid***\n" - "> - chr17:50198002C>A\n " - "> - chr17:50198002C>A(GRCh38)\n" - "> - chr17(GRCh38):50198002C>A\n" - "> - chr17:g.50198002C>A\n" - "> - chr17:g.50198002C>A(GRCh38)\n" - "> - chr17(GRCh38):g.50198002C>A") -@api.param("genome_build", "***Accepted:***\n" - "> - GRCh37\n" - "> - GRCh38\n" - "> - hg19\n" - "> - hg38") class VariantValidatorEnsemblClass(Resource): - # Add documentation about the parser @api.expect(parser, validate=True) @auth.login_required() - @limiter.limit("2/second") + @limiter.limit(vval_pool_limit) def get(self, genome_build, variant_description, select_transcripts, user_id=None): - - # Import object from vval pool vval = vval_object_pool.get_object() - - # set transcript_model transcript_model = "ensembl" - # Switch off select_transcripts = all or raw for genomic variants if ("all" in select_transcripts or "raw" in select_transcripts) and "auth" not in select_transcripts: - if "c." not in variant_description and "n." not in variant_description and "r." not in variant_description \ - and "p." not in variant_description: + if not any(x in variant_description for x in ("c.", "n.", "r.", "p.")): return {"Not Found": "Setting select_transcripts to 'all' or 'raw' is deprecated for genomic " "variant processing using this endpoint. Contact admin on " "https://variantvalidator.org/help/contact/ for updated instructions and" @@ -175,132 +121,66 @@ def get(self, genome_build, variant_description, select_transcripts, user_id=Non elif "auth_raw" in select_transcripts: select_transcripts = "raw" - # Convert inputs to JSON arrays variant_description = input_formatting.format_input(variant_description) select_transcripts = input_formatting.format_input(select_transcripts) - if select_transcripts == '["all"]': - select_transcripts = "all" - if select_transcripts == '["raw"]': - select_transcripts = "raw" + if select_transcripts == '["all"]': select_transcripts = "all" + if select_transcripts == '["raw"]': select_transcripts = "raw" try: - # Validate using the VariantValidator Python Library validate = vval.validate(variant_description, genome_build, select_transcripts, transcript_set=transcript_model, lovd_syntax_check=True) content = validate.format_as_dict(with_meta=True) except Exception as e: - # Handle the exception and customize the error response return {"error": str(e)}, 500 finally: vval_object_pool.return_object(vval) - # Collect Arguments args = parser.parse_args() - - # Overrides the default response route so that the standard HTML URL can return any specified format if args['content-type'] == 'application/json': - # example: http://127.0.0.1:5000.....bob?content-type=application/json return representations.application_json(content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=text/xml elif args['content-type'] == 'text/xml': return representations.xml(content, 200, None) else: - # Return the api default output return content @api.route("/tools/gene2transcripts/", strict_slashes=False) -@api.doc(description="This endpoint has a rate limit of 1 request per second.") -@api.param("gene_query", "***HGNC gene symbol, HGNC ID, or transcript ID***\n" - "\nCurrent supported transcript IDs" - "\n- RefSeq\n" - "\n***Single***\n" - "> COL1A1\n" - "> HGNC:2197\n" - "> NM_000088.4\n") class Gene2transcriptsClass(Resource): - # Add documentation about the parser @api.expect(parser, validate=True) @auth.login_required() - @limiter.limit("1/second") + @limiter.limit(g2t_pool_limit) def get(self, gene_query, user_id=None): - - # Get vvval object from pool vval = g2t_object_pool.get_object() - - # Convert inputs to JSON arrays gene_query = input_formatting.format_input(gene_query) - try: content = vval.gene2transcripts(gene_query, lovd_syntax_check=True)[0] except ConnectionError: - message = "Cannot connect to rest.genenames.org, please try again later" g2t_object_pool.return_object(vval) - raise exceptions.RemoteConnectionError(message) + raise exceptions.RemoteConnectionError("Cannot connect to rest.genenames.org, please try again later") finally: g2t_object_pool.return_object(vval) - # Collect Arguments args = parser.parse_args() - - # Overrides the default response route so that the standard HTML URL can return any specified format if args['content-type'] == 'application/json': - # example: http://127.0.0.1:5000.....bob?content-type=application/json return representations.application_json(content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=text/xml elif args['content-type'] == 'text/xml': return representations.xml(content, 200, None) else: - # Return the api default output return content @api.route("/tools/gene2transcripts_v2////" "", strict_slashes=False) -@api.doc(description="This endpoint has a rate limit of 1 request per second.") -@api.param("gene_query", "***HGNC gene symbol, HGNC ID, or transcript ID***\n" - "\nCurrent supported transcript IDs" - "\n- RefSeq or Ensembl\n" - "\n***Single***\n" - "> COL1A1\n" - "> HGNC:2197\n" - "> NM_000088.4\n" - "\n***Multiple***\n" - "> COL1A1|COL1A2|COL5A1|HGNC:2197\n") -@api.param("limit_transcripts", "***Return all possible transcripts***\n" - "> False\n" - "\n***Single***\n" - "> NM_000088.4\n" - "\n***Multiple***\n" - "> NM_000088.4|NM_000088.3\n" - "\n***Limit to select transcripts***\n" - "> mane_select = MANE Select transcript only\n" - "> mane = Mane Select and MANE Plus Clinical\n" - "> select = All transcripts that have been classified as canonical") -@api.param("transcript_set", "***RefSeq or Ensembl***\n" - "\nall = all transcripts, refseq = RefSeq only, ensembl = Ensembl only") -@api.param("genome_build", "***GRCh37 or GRCh38***\n" - "\nall = all builds, GRCh37 = GRCh37 only, GRCh38 = GRCh38 only") class Gene2transcriptsV2Class(Resource): - # Add documentation about the parser @api.expect(parser_g2t, validate=True) @auth.login_required() - @limiter.limit("1/second") + @limiter.limit(g2t_pool_limit) def get(self, gene_query, limit_transcripts, transcript_set, genome_build, user_id=None): - - # Get vval object from pool vval = g2t_object_pool.get_object() - # Collect Arguments args = parser_g2t.parse_args() - if args['show_exon_info'] is True: - bypass_genomic_spans = False - elif args['show_exon_info'] is False: - bypass_genomic_spans = True - else: - bypass_genomic_spans = True + bypass_genomic_spans = not bool(args['show_exon_info']) - # Convert inputs to JSON arrays gene_query = input_formatting.format_input(gene_query) limit_transcripts = input_formatting.format_input(limit_transcripts) if len(limit_transcripts) == 1: @@ -309,69 +189,50 @@ def get(self, gene_query, limit_transcripts, transcript_set, genome_build, user_ try: if genome_build not in ["GRCh37", "GRCh38"]: genome_build = None - if "False" in limit_transcripts or "false" in limit_transcripts or limit_transcripts is False: + if limit_transcripts in ["False", "false", False]: limit_transcripts = None content = vval.gene2transcripts(gene_query, select_transcripts=limit_transcripts, transcript_set=transcript_set, genome_build=genome_build, batch_output=True, validator=vval, bypass_genomic_spans=bypass_genomic_spans, lovd_syntax_check=True) except ConnectionError: - message = "Cannot connect to rest.genenames.org, please try again later" g2t_object_pool.return_object(vval) - raise exceptions.RemoteConnectionError(message) + raise exceptions.RemoteConnectionError("Cannot connect to rest.genenames.org, please try again later") finally: g2t_object_pool.return_object(vval) - # Overrides the default response route so that the standard HTML URL can return any specified format if args['content-type'] == 'application/json': - # example: http://127.0.0.1:5000.....bob?content-type=application/json return representations.application_json(content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=text/xml elif args['content-type'] == 'text/xml': return representations.xml(content, 200, None) else: - # Return the api default output return content @api.route("/tools/hgvs2reference/", strict_slashes=False) -@api.param("hgvs_description", "***hgvs_description***\n" - "\nSequence variation description in the HGVS format\n" - "\n *Intronic descriptions in the context of transcript reference sequences are currently " - "unsupported*") class Hgvs2referenceClass(Resource): - # Add documentation about the parser @api.expect(parser, validate=True) @auth.login_required() - @limiter.limit("4/second") + @limiter.limit(vval_pool_limit) def get(self, hgvs_description, user_id=None): - - # Get vval object from pool vval = vval_object_pool.get_object() - try: content = vval.hgvs2ref(hgvs_description) except Exception as e: - # Handle the exception and customize the error response return {"error": str(e)}, 500 finally: vval_object_pool.return_object(vval) - # Collect Arguments args = parser.parse_args() - - # Overrides the default response route so that the standard HTML URL can return any specified format if args['content-type'] == 'application/json': - # example: http://127.0.0.1:5000.....bob?content-type=application/json return representations.application_json(content, 200, None) - # example: http://127.0.0.1:5000.....?content-type=text/xml elif args['content-type'] == 'text/xml': return representations.xml(content, 200, None) else: - # Return the api default output return content + # # Copyright (C) 2016-2021 VariantValidator Contributors # diff --git a/rest_VariantValidator/utils/limiter.py b/rest_VariantValidator/utils/limiter.py index 7e2411c..6f043ba 100644 --- a/rest_VariantValidator/utils/limiter.py +++ b/rest_VariantValidator/utils/limiter.py @@ -1,21 +1,41 @@ from flask_limiter import Limiter from flask_limiter.util import get_remote_address +from rest_VariantValidator.utils.object_pool import ( + vval_object_pool, + g2t_object_pool, + simple_variant_formatter_pool +) +# Initialize Flask-Limiter limiter = Limiter(key_func=get_remote_address) -# -# Copyright (C) 2016-2025 VariantValidator Contributors -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program 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 Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# +# -------------------------- +# Dynamic pool-based rate limiter +# -------------------------- +def pool_dynamic_limit(pool, max_rate, min_rate=1): + """ + Returns a dynamic Flask-Limiter rate string based on the number of available objects in a pool. + """ + with pool.lock: + if hasattr(pool, "objects"): # ObjectPool (Validator) + available = len(pool.objects) + elif hasattr(pool, "pool"): # SimpleVariantFormatterPool + available = len(pool.pool) + else: + available = max_rate + + # Clamp the rate between min_rate and max_rate + rate = max(min_rate, min(available, max_rate)) + return f"{rate}/second" + +# -------------------------- +# Convenience functions for each pool with custom max_rate +# -------------------------- +def vval_pool_limit(): + return pool_dynamic_limit(vval_object_pool, max_rate=8) + +def g2t_pool_limit(): + return pool_dynamic_limit(g2t_object_pool, max_rate=6) + +def formatter_pool_limit(): + return pool_dynamic_limit(simple_variant_formatter_pool, max_rate=8) diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py index 9d12d45..05a14f4 100644 --- a/tests/test_endpoints.py +++ b/tests/test_endpoints.py @@ -18,7 +18,7 @@ def test_hello_endpoint(client): response = client.get('/hello/') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response - time.sleep(1) + # time.sleep(1) @@ -27,7 +27,7 @@ def test_lovd_endpoint(client): assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response assert "17-50198002-C-A" in response.json.keys() - time.sleep(1) + # time.sleep(1) def test_lovd_endpoint_multi(client): response = client.get('/LOVD/lovd/GRCh38/17-50198002-C-A|17-50198002-C-T/all/mane/True/False?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint @@ -35,14 +35,14 @@ def test_lovd_endpoint_multi(client): assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response assert "17-50198002-C-A" in response.json.keys() assert "17-50198002-C-T" in response.json.keys() - time.sleep(1) + # time.sleep(1) def test_vf_endpoint(client): response = client.get('/VariantFormatter/variantformatter/GRCh38/NC_000017.10%3Ag.48275363C%3EA/all/mane/True?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response - time.sleep(1) + # time.sleep(1) def test_vv_endpoint(client): @@ -50,7 +50,7 @@ def test_vv_endpoint(client): assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response assert "NM_000088.3:c.589G>T" in response.json.keys() - time.sleep(1) + # time.sleep(1) def test_vv_endpoint_multi(client): @@ -59,90 +59,90 @@ def test_vv_endpoint_multi(client): assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response assert "NM_000088.3:c.589G>T" in response.json.keys() assert "NM_000088.3:c.589G>A" in response.json.keys() - time.sleep(1) + # time.sleep(1) def test_g2t_endpoint(client): response = client.get('/VariantValidator/tools/gene2transcripts/COL1A1?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert len(response.json) == 8 - time.sleep(1) + # time.sleep(1) def test_g2t2_endpoint(client): response = client.get('/VariantValidator/tools/gene2transcripts_v2/COL1A1%7CCOL1A2%7CCOL5A1/mane/all/GRCh38?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert len(response.json) == 3 - time.sleep(1) + # time.sleep(1) def test_h2ref_endpoint(client): response = client.get('/VariantValidator/tools/hgvs2reference/NM_000088.3%3Ac.589G%3ET?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK - time.sleep(1) + # time.sleep(1) def test_vv_endpoint_all_tx(client): response = client.get('/VariantValidator/variantvalidator/GRCh38/NM_000088.3%3Ac.589G%3ET/all?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response - time.sleep(1) + # time.sleep(1) def test_vv_endpoint_all_vcf(client): response = client.get('/VariantValidator/variantvalidator/GRCh38/17-50198002-C-A/all?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 404 # Check if the response status code is 200 OK assert "metadata" not in response.json.keys() # Check if "metadata" key is in the JSON response - time.sleep(1) + # time.sleep(1) def test_vv_endpoint_all_genomic(client): response = client.get('/VariantValidator/variantvalidator/GRCh38/NC_000017.10:g.48275363C>A/all?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 404 # Check if the response status code is 200 OK assert "metadata" not in response.json.keys() # Check if "metadata" key is in the JSON response - time.sleep(1) + # time.sleep(1) def test_vv_endpoint_mane_genomic(client): response = client.get('/VariantValidator/variantvalidator/GRCh38/NC_000017.10:g.48275363C>A/mane?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response - time.sleep(1) + # time.sleep(1) def test_vv_endpoint_mane_vcf(client): response = client.get('/VariantValidator/variantvalidator/GRCh38/17-50198002-C-A/mane?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response - time.sleep(1) + # time.sleep(1) def test_vv_endpoint_auth_raw_vcf(client): response = client.get('/VariantValidator/variantvalidator/GRCh38/17-50198002-C-A/auth_all?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response - time.sleep(1) + # time.sleep(1) def test_vv_endpoint_auth_all_vcf(client): response = client.get('/VariantValidator/variantvalidator/GRCh38/17-50198002-C-A/auth_raw?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response - time.sleep(1) + # time.sleep(1) def test_vv_g2tv2(client): response = client.get('/VariantValidator/tools/gene2transcripts_v2/AMPD1/mane/refseq/GRCh38?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert len(response.json) == 1 - time.sleep(1) + # time.sleep(1) def test_lovd_endpoint_all(client): response = client.get('/LOVD/lovd/GRCh37/1%3A43815009%3AG%3AT/refseq/all/False/True?content-type=application%2Fjson') # Send a GET request to the /hello/ endpoint assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response assert "1:43815009:G:T" in response.json.keys() - time.sleep(1) + # time.sleep(1) def test_lovd_endpoint_raw(client): @@ -150,4 +150,4 @@ def test_lovd_endpoint_raw(client): assert response.status_code == 200 # Check if the response status code is 200 OK assert "metadata" in response.json.keys() # Check if "metadata" key is in the JSON response assert "1:43815009:G:T" in response.json.keys() - time.sleep(1) + # time.sleep(1) From 92d3dc2c8ee5caedac7674af1f436f4f868ce39f Mon Sep 17 00:00:00 2001 From: Peter-J-Freeman Date: Tue, 4 Nov 2025 15:42:38 +0000 Subject: [PATCH 218/218] select transcript handling gor VV endpoint --- .../endpoints/variantformatter_endpoints.py | 4 ++++ .../endpoints/variantvalidator_endpoints.py | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/rest_VariantValidator/endpoints/variantformatter_endpoints.py b/rest_VariantValidator/endpoints/variantformatter_endpoints.py index a4a0332..477b247 100644 --- a/rest_VariantValidator/endpoints/variantformatter_endpoints.py +++ b/rest_VariantValidator/endpoints/variantformatter_endpoints.py @@ -75,6 +75,10 @@ def get(self, genome_build, variant_description, transcript_model, select_transc select_transcripts = "all" if select_transcripts == '["raw"]': select_transcripts = "raw" + if select_transcripts == '["mane"]': + select_transcripts = "mane" + if select_transcripts == '["mane_select"]': + select_transcripts = "mane_select" try: content = simple_formatter.format(variant_description, genome_build, transcript_model, diff --git a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py index ebe28af..5829635 100644 --- a/rest_VariantValidator/endpoints/variantvalidator_endpoints.py +++ b/rest_VariantValidator/endpoints/variantvalidator_endpoints.py @@ -89,6 +89,10 @@ def get(self, genome_build, variant_description, select_transcripts, user_id=Non select_transcripts = "all" if select_transcripts == '["raw"]': select_transcripts = "raw" + if select_transcripts == '["mane_select"]': + select_transcripts = "mane_select" + if select_transcripts == '["mane"]': + select_transcripts = "mane" try: # Validate using the VariantValidator Python Library @@ -182,6 +186,10 @@ def get(self, genome_build, variant_description, select_transcripts, user_id=Non select_transcripts = "all" if select_transcripts == '["raw"]': select_transcripts = "raw" + if select_transcripts == '["mane_select"]': + select_transcripts = "mane_select" + if select_transcripts == '["mane"]': + select_transcripts = "mane" try: # Validate using the VariantValidator Python Library