Skip to content

Commit a7efff8

Browse files
authored
Merge pull request #168 from pbashyal-nmdp/add_validate_endpoint
Add `validate` endpoint
2 parents 34e2cba + 6a33ae2 commit a7efff8

File tree

13 files changed

+278
-29228
lines changed

13 files changed

+278
-29228
lines changed

.dockerignore

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
.github
2+
.git
3+
.direnv
4+
.idea
5+
.eggs/
6+
build
7+
dist
8+
*.egg
9+
*.egg-info
10+
tests
11+
venv
12+
13+
__pycache__
14+
*.pyo
15+
*.pyd
16+
*.output
17+
.coverage
18+
.coverage.*
19+
.cache
20+
htmlcov/
21+
.pytest_cache
22+
allure_report
23+
CHANGELOG.md

CHANGELOG.md

Lines changed: 0 additions & 29218 deletions
Large diffs are not rendered by default.

Dockerfile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
FROM python:3.10-slim-buster
2+
3+
LABEL MAINTAINER="Pradeep Bashyal"
4+
5+
WORKDIR /app
6+
7+
COPY requirements.txt /app
8+
RUN pip install --no-cache-dir --upgrade pip && \
9+
pip install --no-cache-dir -r requirements.txt
10+
11+
COPY requirements-deploy.txt /app
12+
RUN pip install --no-cache-dir -r requirements-deploy.txt
13+
14+
COPY app.py /app/
15+
COPY api.py /app/
16+
COPY api-spec.yaml /app/
17+
COPY pyard /app/pyard
18+
19+
CMD ["gunicorn", "--bind", "0.0.0.0:8080", "--worker-tmp-dir", "/dev/shm", "--timeout", "30", "app:app"]

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,18 @@ dist: clean ## builds source and wheel package
9595
ls -l dist
9696

9797
docker-build: ## build a docker image for the service
98-
docker build -t my-project-template-service:0.0.1 .
98+
docker build -t pyard-service:latest .
9999

100100
docker: docker-build ## build a docker image and run the service
101-
docker run --name my-project-template -p 8080:8080 my-project-template-service:0.0.1
101+
docker run --name pyard-service -p 8080:8080 pyard-service:latest
102102

103103
install: clean ## install the package to the active Python's site-packages
104104
pip install --upgrade pip
105-
python setup.py install
106105
pip install -r requirements.txt
107106
pip install -r requirements-tests.txt
108107
pip install -r requirements-dev.txt
109108
pip install -r requirements-deploy.txt
109+
python setup.py install
110110
pre-commit install
111111

112112
venv: ## creates a Python3 virtualenv environment in venv

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ ard.redux_gl('B14', 'lg')
119119
| `lgx` | Reduce to 2 field ARD level |
120120
| `W` | Reduce/Expand to 3 field WHO nomenclature level |
121121
| `exon` | Reduce/Expand to exon level |
122+
| `U2` | Reduce to 2 field unambiguous level |
122123

123124
# Command Line Tools
124125

@@ -186,3 +187,19 @@ A*01:01/A*01:02/A*01:03/A*01:06/A*01:07/A*01:08/A*01:09/A*01:10/A*01:12/ ...
186187
### Batch Reduce a CSV file
187188

188189
`pyard-csv-reduce` can be used to batch process a CSV file with HLA typings. See [documentation](extras/README.md) for instructions on how to configure and run.
190+
191+
## py-ard REST Service
192+
193+
Run `py-ard` as a service so that it can be accessed as a REST service endpoint.
194+
195+
Build the docker image:
196+
```shell
197+
make docker-build
198+
```
199+
200+
Build the docker and run it with:
201+
```shell
202+
make docker
203+
```
204+
205+
The endpoint should then be available at [localhost:8080](http://0.0.0.0:8080)

api-spec.yaml

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
openapi: 3.0.3
2+
info:
3+
title: ARD Reduction
4+
description: Reduce to ARD Level
5+
version: "0.8.0"
6+
servers:
7+
- url: 'http://localhost:8080'
8+
tags:
9+
- name: ARD Reduction
10+
description: Reduce GL String to ARD
11+
paths:
12+
/redux:
13+
post:
14+
tags:
15+
- ARD Reduction
16+
operationId: api.redux_controller
17+
summary: Reduce to ARD
18+
description: |
19+
Given a GL String and a reduction method perform ARD Reduction
20+
requestBody:
21+
content:
22+
application/json:
23+
schema:
24+
properties:
25+
gl_string:
26+
description: GL String
27+
type: string
28+
example: "A*01:01+A*01:01^B*08:ASXJP+B*07:02^C*02:02+C*07:HTGM^DPB1*28:01:01G+DPB1*296:01"
29+
reduction_method:
30+
description: Reduction Method
31+
type: string
32+
enum:
33+
- G
34+
- lg
35+
- lgx
36+
- W
37+
- exon
38+
- U2
39+
example: "lgx"
40+
responses:
41+
200:
42+
description: Reduction Result
43+
content:
44+
application/json:
45+
schema:
46+
type: object
47+
properties:
48+
ard:
49+
description: ARD reduced version of GL String
50+
type: string
51+
example: "A*01:01+A*01:01^B*07:02+B*08:01^C*02:02/C*02:10+C*07:01/C*07:150Q^DPB1*28:01+DPB1*28:01"
52+
400:
53+
description: Invalid GL String Form
54+
content:
55+
application/json:
56+
schema:
57+
type: object
58+
properties:
59+
message:
60+
description: Describes what went wrong
61+
type: string
62+
example: "Invalid HLA locus"
63+
/validate:
64+
post:
65+
tags:
66+
- ARD Reduction
67+
operationId: api.validate_controller
68+
summary: Validate GL String
69+
description: |
70+
Given a GL String report whether it is valid or not
71+
requestBody:
72+
content:
73+
application/json:
74+
schema:
75+
properties:
76+
gl_string:
77+
description: GL String
78+
type: string
79+
example: "A*01:01+A*01:01^B*08:ASXJP+B*07:02^C*02:02+C*07:HTGM^DPB1*28:01:01G+DPB1*296:01"
80+
responses:
81+
200:
82+
description: Validation Result
83+
content:
84+
application/json:
85+
schema:
86+
type: object
87+
properties:
88+
valid:
89+
description: Is GL String valid
90+
type: boolean
91+
example: true
92+
404:
93+
description: GL String didn't validate
94+
content:
95+
application/json:
96+
schema:
97+
type: object
98+
properties:
99+
valid:
100+
description: Is GL String valid
101+
type: boolean
102+
example: false
103+
message:
104+
description: Describes what went wrong
105+
type: string
106+
example: "Provided GL String is invalid: HLA-A*01:BLAH"
107+
cause:
108+
description: Explanation of why the GL String is not valid
109+
type: string
110+
example: "HLA-A*01:BLAH is not a valid GL Allele"
111+
400:
112+
description: Invalid GL String Form
113+
content:
114+
application/json:
115+
schema:
116+
type: object
117+
properties:
118+
message:
119+
description: Describes what went wrong
120+
type: string
121+
example: "Invalid HLA locus"

api.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from flask import request
2+
3+
from pyard import ARD
4+
from pyard.exceptions import PyArdError, InvalidAlleleError
5+
6+
# Globally accessible for all endpoints
7+
ard = ARD()
8+
9+
10+
def validate_controller():
11+
if request.json:
12+
# Check the request has required inputs
13+
try:
14+
gl_string = request.json["gl_string"]
15+
except KeyError:
16+
return {"message": "gl_string not provided"}, 404
17+
# Validate
18+
try:
19+
ard.isvalid_gl(gl_string)
20+
return {"valid": True}, 200
21+
except InvalidAlleleError as e:
22+
return {
23+
"valid": False,
24+
"message": f"Provided GL String is invalid: {gl_string}",
25+
"cause": e.message,
26+
}, 404
27+
except PyArdError as e:
28+
return {"message": e.message}, 400
29+
30+
31+
def redux_controller():
32+
if request.json:
33+
# Check the request has required inputs
34+
try:
35+
gl_string = request.json["gl_string"]
36+
reduction_method = request.json["reduction_method"]
37+
except KeyError:
38+
return {"message": "gl_string and reduction_method not provided"}, 404
39+
# Perform redux
40+
try:
41+
redux_gl_string = ard.redux_gl(gl_string, reduction_method)
42+
return {"ard": redux_gl_string}, 200
43+
except PyArdError as e:
44+
return {"message": e.message}, 400
45+
46+
# if no data is sent
47+
return {"message": "No input data provided"}, 404

app.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env python3
2+
import connexion
3+
4+
from flask import redirect
5+
6+
app = connexion.App(__name__)
7+
application = app.app
8+
api = app.add_api("api-spec.yaml")
9+
10+
11+
@app.route("/")
12+
def index():
13+
return redirect(api.base_path + "/ui")
14+
15+
16+
if __name__ == "__main__":
17+
app.run(port=8080, debug=True)

pyard/db.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,11 @@ def create_db_connection(data_dir, imgt_version, ro=False):
5757
if ro:
5858
# Open the database in read-only mode
5959
file_uri = f"file:{db_filename}?mode=ro"
60-
else:
61-
# Open the database in read-only mode
62-
file_uri = f"file:{db_filename}"
60+
# Multiple threads can access the same connection since it's only ro
61+
return sqlite3.connect(file_uri, check_same_thread=False, uri=True)
6362

63+
# Open the database for read/write
64+
file_uri = f"file:{db_filename}"
6465
return sqlite3.connect(file_uri, uri=True)
6566

6667

pyard/exceptions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ def __str__(self) -> str:
2323

2424

2525
class InvalidTypingError(PyArdError):
26-
def __init__(self, message: str) -> None:
26+
def __init__(self, message: str, cause=None) -> None:
2727
self.message = message
28+
self.cause = cause
2829

2930
def __str__(self) -> str:
3031
return f"Invalid HLA Typing: {self.message}"

0 commit comments

Comments
 (0)