Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
2c5240f
Fix captcha validation
brucosper Nov 25, 2021
f4774f0
Merge branch 'BioAnalyticResource:dev' into dev
brucosper Nov 25, 2021
7915b5d
Merge pull request #102 from brucosper/dev
asherpasha Nov 25, 2021
ced5482
Documentation initial commit
asherpasha Nov 27, 2021
3914634
Merge pull request #103 from asherpasha/dev
asherpasha Nov 27, 2021
07fedda
Updated documentation.
asherpasha Nov 27, 2021
20ed8ff
Merge pull request #104 from asherpasha/dev
asherpasha Nov 27, 2021
0504f57
Updated documentation.
asherpasha Nov 28, 2021
ad8d180
Merge pull request #105 from asherpasha/dev
asherpasha Nov 28, 2021
83ba58f
Minor bug fix
asherpasha Nov 28, 2021
0465a9a
Merge pull request #106 from asherpasha/dev
asherpasha Nov 28, 2021
e9fa9b1
added unit tests, standardized response JSON for interactions and loc…
VinLau Dec 1, 2021
0643c23
merged docs upstream
VinLau Dec 1, 2021
500ef45
Fix API manager keys
brucosper Dec 1, 2021
cd09316
Merge branch 'dev' of github.com:brucosper/BAR_API into dev
brucosper Dec 1, 2021
a152ff8
Merge branch 'BioAnalyticResource:dev' into dev
brucosper Dec 1, 2021
c92e0d6
Change DATA_FOLDER
brucosper Dec 2, 2021
d89008f
Merge pull request #107 from VinLau/dev
asherpasha Dec 3, 2021
fdbce62
Merge pull request #108 from brucosper/dev
asherpasha Dec 3, 2021
72c2bc1
Fix requests not being removed, create table on user approval
brucosper Dec 3, 2021
8e00e44
Merge branch 'BioAnalyticResource:dev' into dev
brucosper Dec 3, 2021
bd29d92
Merge pull request #109 from brucosper/dev
asherpasha Dec 3, 2021
e71be8b
Working on documentation.
asherpasha Dec 3, 2021
1209a7e
Merge pull request #110 from asherpasha/dev
asherpasha Dec 3, 2021
fd20ec6
eFP image service initial commit.
asherpasha Dec 5, 2021
a212561
Merge branch 'BioAnalyticResource:dev' into dev
asherpasha Dec 5, 2021
142fb5e
Minor bug.
asherpasha Dec 5, 2021
4af9ac8
Working on eFP Images.
asherpasha Dec 6, 2021
34f54a1
Working on eFP Image service.
asherpasha Dec 6, 2021
b65f0af
Adding output
asherpasha Dec 6, 2021
356724f
Added output directory.
asherpasha Dec 6, 2021
0719a5f
Merge pull request #111 from asherpasha/dev
asherpasha Dec 6, 2021
955304f
Added unit tests for eFP Image service.
asherpasha Dec 8, 2021
0d6d515
Send email notification on request
brucosper Dec 10, 2021
ccae489
Merge branch 'BioAnalyticResource:dev' into dev
brucosper Dec 10, 2021
923743b
Load env vars in CI
brucosper Dec 10, 2021
fec8e14
Ignore email in CI
brucosper Dec 10, 2021
6f869f9
Merge pull request #113 from brucosper/dev
asherpasha Dec 10, 2021
60a96b8
Merge branch 'BioAnalyticResource:dev' into dev
asherpasha Dec 11, 2021
563903a
eFP Image end point is now cached.
asherpasha Dec 11, 2021
f1f3769
Merge pull request #112 from asherpasha/dev
asherpasha Dec 11, 2021
622e41e
Fixing read the docs.
asherpasha Dec 11, 2021
61fb44c
Merge pull request #114 from asherpasha/dev
asherpasha Dec 11, 2021
e2529e2
Adding eFP Cannabis for faster tests.
asherpasha Dec 12, 2021
1cb5872
Removing eFP compare test to speed up testing.
asherpasha Dec 12, 2021
4cccd50
eFP Arachis image service is added.
asherpasha Dec 15, 2021
874c7f7
Validate captcha within /request
brucosper Dec 16, 2021
5d36229
Validate Captcha inside /request
brucosper Dec 16, 2021
07766d7
Merge branch 'BioAnalyticResource:dev' into dev
brucosper Dec 16, 2021
e3841c8
Don't try to validate captcha in CI
brucosper Dec 17, 2021
f85b5c2
Merge branch 'dev' of github.com:brucosper/BAR_API into dev
brucosper Dec 17, 2021
d8a1f22
Merge pull request #116 from brucosper/dev
asherpasha Dec 17, 2021
6cc7f69
Added eFP Soybean.
asherpasha Dec 17, 2021
3e06b2d
Merge branch 'BioAnalyticResource:dev' into dev
asherpasha Dec 30, 2021
7553f4f
eFP Maize is added. Output directory cleanup is also added.
asherpasha Dec 30, 2021
5e3ddbc
Merge pull request #115 from asherpasha/dev
asherpasha Dec 30, 2021
f6ee0c3
Fix CSV/TSV saving to use temp directory
brucosper Jan 13, 2022
b58c1f0
Merge pull request #117 from brucosper/dev
asherpasha Jan 14, 2022
ddd89e9
Requirements update.
asherpasha Jan 14, 2022
ea098a4
Fixed numpy
asherpasha Jan 14, 2022
633c738
Merge pull request #118 from asherpasha/dev
asherpasha Jan 14, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ exclude =
venv,
env,
.venv,
./venv-docs,
docs,
.env
5 changes: 2 additions & 3 deletions .github/workflows/bar-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: Ubuntu-20.04
strategy:
matrix:
python-version: [3.7, 3.8, 3.9, 3.10.0]
python-version: [3.7, 3.8, 3.9, 3.10.1]

services:
redis:
Expand Down Expand Up @@ -58,5 +58,4 @@ jobs:
- name: "Upload coverage to Codecov"
uses: codecov/codecov-action@v2
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
fail_ci_if_error: false
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ __pycache__/
build/
develop-eggs/
dist/
docs/build/
downloads/
eggs/
.eggs/
Expand Down Expand Up @@ -106,6 +107,7 @@ celerybeat.pid
.venv
env/
venv/
venv-docs/
ENV/
env.bak/
venv.bak/
Expand Down Expand Up @@ -136,3 +138,6 @@ dmypy.json
**/.DS_Store

.vscode/
output/*
!output
!output/placeholder.txt
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.10.0-bullseye
FROM python:3.10.1-bullseye

WORKDIR /usr/src/app

Expand Down
73 changes: 2 additions & 71 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,75 +3,6 @@
**master**: [![Build Status](https://github.com/BioAnalyticResource/BAR_API/workflows/BAR-API/badge.svg?branch=master)](https://github.com/BioAnalyticResource/BAR_API/actions?query=branch%3Amaster) [![codecov](https://codecov.io/gh/BioAnalyticResource/BAR_API/branch/master/graph/badge.svg?token=QSYUWTRYEV)](https://codecov.io/gh/BioAnalyticResource/BAR_API) **dev**: [![Build Status](https://github.com/BioAnalyticResource/BAR_API/workflows/BAR-API/badge.svg?branch=dev)](https://github.com/BioAnalyticResource/BAR_API/actions?query=branch%3Adev) [![codecov](https://codecov.io/gh/BioAnalyticResource/BAR_API/branch/dev/graph/badge.svg?token=QSYUWTRYEV)](https://codecov.io/gh/BioAnalyticResource/BAR_API)

[![Website Status](https://img.shields.io/website?url=http%3A%2F%2Fbar.utoronto.ca%2Fapi%2F)](http://bar.utoronto.ca/api/) ![GitHub repo size](https://img.shields.io/github/repo-size/BioAnalyticResource/BAR_API) [![LGTM Alerts](https://img.shields.io/lgtm/alerts/github/BioAnalyticResource/BAR_API)](https://lgtm.com/projects/g/BioAnalyticResource/BAR_API/?mode=list) [![LGTM
Grade](https://img.shields.io/lgtm/grade/python/github/BioAnalyticResource/BAR_API)](https://lgtm.com/projects/g/BioAnalyticResource/BAR_API/latest/files/?sort=name&dir=ASC&mode=heatmap) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
Grade](https://img.shields.io/lgtm/grade/python/github/BioAnalyticResource/BAR_API)](https://lgtm.com/projects/g/BioAnalyticResource/BAR_API/latest/files/?sort=name&dir=ASC&mode=heatmap) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Documentation Status](https://readthedocs.org/projects/bar-api/badge/?version=latest)](https://bar-api.readthedocs.io/en/latest/?badge=latest)

This is the official repository for the Bio-Analytic Resource API.

## Status

Apart from Travis CI and testing on the live BAR, we frequently test the API on systems that are not BAR for cross-platform compatibility. The most recent test is on the following systems:

* OpenBSD 6.7-CURRENT (Maria DB 10.4.12v1, Python 3.8.3 and Redis 6.0.5)
* FreeBSD 12.1-RELEASE-p6 (MySQL 8.0.20, Python 3.8.3 and Redis 5.0.9)

## Run on your own computer with Docker

1. Install [Docker](https://docs.docker.com/get-docker/)
2. Install [Docker Compose](https://docs.docker.com/compose/install/)
3. Install [Git](https://git-scm.com/downloads)
4. Clone this repository and change directory to ```BAR_API```
5. Build docker images
```
docker-compose build
```
6. Run docker containers (-d is detached)
```
docker-compose up -d
```
7. Load ```http://localhost:5000/``` in a web browser. Enjoy :)

## Run on your own computer without Docker

1. Install [MySQL](https://www.mysql.com/products/community/) or [Maria DB](https://mariadb.com/downloads/).
2. Install [Redis](https://redis.io/download).
3. Install [Python](https://www.python.org/downloads/) or [Pypy](https://www.pypy.org/download.html). Note: Python 2 is not supported.
4. Install [Git](https://git-scm.com/downloads)
5. (Optional) On Debian based systems, you may also need to install ```libmysqlclient-dev``` and ```python3-dev```. On FreeBSD, you may need to install ```py38-sqlite3```. We will update this step as we come across more OS dependencies.
6. Clone this repository and change directory to ```BAR_API```
7. Set up a virtual environment
```
python3 -m venv venv
```
8. Activate the virtual environment. Bash/Zsh:
```
source venv/bin/activate
```
csh/tcsh:
```
source venv/bin/activate.csh
```
9. Install requirements
```
pip install --upgrade pip setuptools wheel
pip install -r requirements.txt
```
10. Copy ```config/BAR_API.cgi``` to your preferred directory, for example:
```
cp config/BAR_API.cgi ~/.config/
```
Add, update, and modify passwords and environment variables as needed.

11. Copy ```./config/init.sh``` to BAR_API directory:
```
cp config/init.sh .
```
Change passwords in ```./init.sh``` and run this script to load the databases:
```
./init.sh
```
Then delete ```./init.sh```

12. Edit ```./api/__init__.py``` and update the location of your BAR_API.cfg file if you have changed it.
13. Run ```pytest```. Tests should pass if the system is set up correctly.
14. Run ```python app.py``` to start.
15. Load ```http://localhost:5000/``` in a web browser. Enjoy :)
This is the official repository for the Bio-Analytic Resource API. The API documentation can be found [here](https://bar-api.readthedocs.io/en/latest/).
26 changes: 24 additions & 2 deletions api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,28 @@ def create_app():
print("We are now loading configuration.")
bar_app.config.from_pyfile(os.getcwd() + "/config/BAR_API.cfg", silent=True)
if bar_app.config.get("ADMIN_ENCRYPT_KEY"):
os.environ["ADMIN_ENCRYPT_KEY"] = bar_app.config.get("ADMIN_ENCRYPT_KEY")
os.environ["ADMIN_ENCRYPT_KEY"] = bar_app.config.get(
"TEST_ADMIN_ENCRYPT_KEY"
)
if bar_app.config.get("ADMIN_PASSWORD_FILE"):
os.environ["ADMIN_PASSWORD_FILE"] = bar_app.config.get(
"ADMIN_PASSWORD_FILE"
"TEST_ADMIN_PASSWORD_FILE"
)
if bar_app.config.get("ADMIN_EMAIL"):
os.environ["ADMIN_EMAIL"] = bar_app.config.get("ADMIN_EMAIL")
if bar_app.config.get("EMAIL_PASS_KEY"):
os.environ["EMAIL_PASS_KEY"] = bar_app.config.get("EMAIL_PASS_KEY")
if bar_app.config.get("EMAIL_PASS_FILE"):
os.environ["EMAIL_PASS_FILE"] = bar_app.config.get("EMAIL_PASS_FILE")
elif os.environ.get("BAR"):
# The BAR
bar_app.config.from_pyfile(os.environ.get("BAR_API_PATH"), silent=True)
if bar_app.config.get("ADMIN_EMAIL"):
os.environ["ADMIN_EMAIL"] = bar_app.config.get("ADMIN_EMAIL")
if bar_app.config.get("EMAIL_PASS_KEY"):
os.environ["EMAIL_PASS_KEY"] = bar_app.config.get("EMAIL_PASS_KEY")
if bar_app.config.get("EMAIL_PASS_FILE"):
os.environ["EMAIL_PASS_FILE"] = bar_app.config.get("EMAIL_PASS_FILE")
if bar_app.config.get("ADMIN_ENCRYPT_KEY"):
os.environ["ADMIN_ENCRYPT_KEY"] = bar_app.config.get("ADMIN_ENCRYPT_KEY")
if bar_app.config.get("ADMIN_PASSWORD_FILE"):
Expand All @@ -51,6 +65,12 @@ def create_app():
os.environ["ADMIN_PASSWORD_FILE"] = bar_app.config.get(
"ADMIN_PASSWORD_FILE"
)
if bar_app.config.get("ADMIN_EMAIL"):
os.environ["ADMIN_EMAIL"] = bar_app.config.get("ADMIN_EMAIL")
if bar_app.config.get("EMAIL_PASS_KEY"):
os.environ["EMAIL_PASS_KEY"] = bar_app.config.get("EMAIL_PASS_KEY")
if bar_app.config.get("EMAIL_PASS_FILE"):
os.environ["EMAIL_PASS_FILE"] = bar_app.config.get("EMAIL_PASS_FILE")
if bar_app.config.get("DRIVE_LIST_KEY"):
os.environ["DRIVE_LIST_KEY"] = bar_app.config.get("DRIVE_LIST_KEY")
if bar_app.config.get("DRIVE_LIST_FILE"):
Expand Down Expand Up @@ -103,6 +123,7 @@ def create_app():
from api.resources.gene_annotation import gene_annotation
from api.resources.interactions import itrns
from api.resources.gene_localizations import loc
from api.resources.efp_image import efp_image

bar_api.add_namespace(gene_information)
bar_api.add_namespace(rnaseq_gene_expression)
Expand All @@ -115,6 +136,7 @@ def create_app():
bar_api.add_namespace(gene_annotation)
bar_api.add_namespace(itrns)
bar_api.add_namespace(loc)
bar_api.add_namespace(efp_image)
bar_api.init_app(bar_app)
return bar_app

Expand Down
127 changes: 92 additions & 35 deletions api/resources/api_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
from flask_restx import Namespace, Resource
from datetime import datetime
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.types import String, Float
import os
import uuid
import requests
import pandas
from cryptography.fernet import Fernet
from smtplib import SMTP_SSL
from ssl import create_default_context
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

CAPTCHA_KEY_FILE = "/home/bpereira/data/bar.summarization/key"

Expand All @@ -32,6 +37,56 @@ def check_admin_pass(password):
else:
return False

@staticmethod
def validate_captcha(value):
"""Validates a reCaptcha value using our secret token"""
if os.environ.get("BAR"):
with open(CAPTCHA_KEY_FILE, "rb") as f:
for line in f:
key = line
if key:
ret = requests.post(
"https://www.google.com/recaptcha/api/siteverify",
data={"secret": key, "response": value},
)
return ret.json()["success"]
else:
return False
else:
return True

@staticmethod
def send_email_notification():
if os.environ.get("BAR"):
with open(os.environ.get("ADMIN_EMAIL"), "r") as f:
for line in f:
recipient = line
port = 465
key = os.environ.get("EMAIL_PASS_KEY")
cipher_suite = Fernet(key)
with open(os.environ.get("EMAIL_PASS_FILE"), "rb") as f:
for line in f:
encrypted_key = line
uncipher_text = cipher_suite.decrypt(encrypted_key)
password = bytes(uncipher_text).decode("utf-8")
context = create_default_context()
smtp_server = "smtp.gmail.com"
sender_email = "bar.summarization@gmail.com"
subject = "[Bio-Analytic Resource] New API key request"
text = """\
There is a new API key request.
You can approve or reject it at http://bar.utoronto.ca/~bpereira/webservices/bar-request-manager/build/index.html
"""
m_text = MIMEText(text, _subtype="plain", _charset="UTF-8")
msg = MIMEMultipart()
msg["From"] = sender_email
msg["To"] = recipient
msg["Subject"] = subject
msg.attach(m_text)
with SMTP_SSL(smtp_server, port, context=context) as server:
server.login("bar.summarization@gmail.com", password)
server.sendmail(sender_email, recipient, msg.as_string())


@api_manager.route("/validate_admin_password", methods=["POST"], doc=False)
class ApiManagerValidate(Resource):
Expand Down Expand Up @@ -72,22 +127,27 @@ def post(self):
class ApiManagerRequest(Resource):
def post(self):
if request.method == "POST":
response_json = request.get_json()
df = pandas.DataFrame.from_records([response_json])
con = db.get_engine(bind="summarization")
try:
reqs = Requests()
users = Users()
row_req = reqs.query.filter_by(email=df.email[0]).first()
row_users = users.query.filter_by(email=df.email[0]).first()

if row_req is None and row_users is None:
df.to_sql("requests", con, if_exists="append", index=False)
return BARUtils.success_exit("Data added")
else:
return BARUtils.error_exit("E-mail already in use"), 409
except SQLAlchemyError:
return BARUtils.error_exit("Internal server error"), 500
captchaVal = request.headers.get("captchaVal")
if ApiManagerUtils.validate_captcha(captchaVal):
response_json = request.get_json()
df = pandas.DataFrame.from_records([response_json])
con = db.get_engine(bind="summarization")
try:
reqs = Requests()
users = Users()
row_req = reqs.query.filter_by(email=df.email[0]).first()
row_users = users.query.filter_by(email=df.email[0]).first()

if row_req is None and row_users is None:
df.to_sql("requests", con, if_exists="append", index=False)
ApiManagerUtils.send_email_notification()
return BARUtils.success_exit("Data added")
else:
return BARUtils.error_exit("E-mail already in use"), 409
except SQLAlchemyError:
return BARUtils.error_exit("Internal server error"), 500
else:
return BARUtils.error_exit("Failed Captcha verification")


@api_manager.route("/get_pending_requests", methods=["POST"], doc=False)
Expand Down Expand Up @@ -167,37 +227,34 @@ def post(self):
"date_added": datetime.now(),
"status": "user",
"api_key": key,
"uses_left": 25,
"uses_left": 100,
}
)
for row in rows
]
df = pandas.DataFrame.from_records([values[0]])
values_df = pandas.DataFrame(columns=["Gene", "Sample", "Value"])
con = db.get_engine(bind="summarization")
try:
df.to_sql("users", con, if_exists="append", index=False)
values_df.to_sql(
key,
con,
index_label="index",
dtype={
values_df.index.name: String(42),
"Gene": String(32),
"Sample": String(32),
"Value": Float,
},
if_exists="append",
index=True,
)
el = table.query.filter_by(email=email).one()
db.session.delete(el)
db.session.commit()
except SQLAlchemyError:
return BARUtils.error_exit("Internal server error"), 500
return BARUtils.success_exit(key)
else:
return BARUtils.error_exit("Forbidden"), 403


@api_manager.route("/validate_captcha", methods=["POST"], doc=False)
class ApiManagerCaptchaValidate(Resource):
def post(self):
"""Validates a reCaptcha value using our secret token"""
if request.method == "POST":
json = request.get_json()
value = json["response"]
key = os.environ.get("CAPTCHA_KEY_FILE")
if key:
ret = requests.post(
"https://www.google.com/recaptcha/api/siteverify",
data={"secret": key, "response": value},
)
return BARUtils.success_exit(ret.text)
else:
return BARUtils.error_exit("Forbidden: CAPTCHA key is not found"), 403
Loading