Compare commits
11 Commits
803d1202f6
...
4c66b0e2af
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c66b0e2af | |||
| 00a6a4da74 | |||
| 867fd0c437 | |||
| b20dc5acab | |||
| 16cc7adb8e | |||
| 2b8707be81 | |||
| 0a7bf8f174 | |||
| e0232aa02d | |||
| e2bc602264 | |||
| ecef739fdd | |||
| 1753230e27 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
virtualenv
|
virtualenv
|
||||||
|
version.sh
|
||||||
|
|||||||
173
Makefile
173
Makefile
@@ -1,90 +1,85 @@
|
|||||||
MAJOR:=$(shell bash -c 'source version.sh ; echo $$MAJOR')
|
MAJOR:=$(shell bash -c 'source version.sh ; echo $$MAJOR')
|
||||||
BUILD:=$(shell bash -c 'source version.sh ; echo $$BUILD')
|
BUILD:=$(shell bash -c 'source version.sh ; echo $$BUILD')
|
||||||
OS_NAME:=$(shell bash -c 'source version.sh ; echo $$OS_NAME')
|
OS_NAME:=$(shell bash -c 'source version.sh ; echo $$OS_NAME')
|
||||||
ifeq "$(OS_NAME)" "win"
|
PIP=$(shell pwd)/virtualenv/bin/pip
|
||||||
PIP=$(shell pwd)/virtualenv/Scripts/pip
|
VIRTUALENV_PKGS_DIR=$(shell pwd)/virtualenv/lib/python2.7/site-packages
|
||||||
VIRTUALENV_PKGS_DIR=$(shell pwd)/virtualenv/Lib/site-packages
|
VIRTUALENV=$(shell which virtualenv)
|
||||||
else
|
PYTHON=$(shell which python)
|
||||||
PIP=$(shell pwd)/virtualenv/bin/pip
|
|
||||||
VIRTUALENV_PKGS_DIR=$(shell pwd)/virtualenv/lib/site-packages
|
PYTHON_FILES=setup.py mercy/version.py $(shell find mercy -iname "*py")
|
||||||
endif
|
PYTHON_SDIST=./dist/mercy-$(MAJOR)-$(BUILD).tar.gz
|
||||||
VIRTUALENV=$(shell which virtualenv)
|
|
||||||
PYTHON=$(shell which python)
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
PYTHON_FILES=setup.py mercy/version.py $(shell find mercy -iname "*py")
|
rm -fr dist/*
|
||||||
PYTHON_SDIST=./dist/mercy-$(MAJOR)-$(BUILD).tar.gz
|
find mercy -iname "*pyc" -exec rm -vf \{\} \;
|
||||||
|
cd puppet && make clean
|
||||||
.PHONY: clean
|
|
||||||
clean:
|
############## Targets for puppet module
|
||||||
rm -fr dist/*
|
|
||||||
find mercy -iname "*pyc" -exec rm -vf \{\} \;
|
puppet/version.sh: version.sh
|
||||||
cd puppet && make clean
|
cp version.sh $@
|
||||||
|
|
||||||
############## Targets for puppet module
|
.PHONY: puppet
|
||||||
|
puppet: puppet_dist
|
||||||
puppet/version.sh: version.sh
|
|
||||||
cp version.sh $@
|
.PHONY: puppet_dist
|
||||||
|
puppet_dist: puppet/version.sh
|
||||||
.PHONY: puppet
|
cd puppet && make dist
|
||||||
puppet: puppet_dist
|
|
||||||
|
.PHONY: puppet_install
|
||||||
.PHONY: puppet_dist
|
puppet_install: puppet/version.sh
|
||||||
puppet_dist: puppet/version.sh
|
cd puppet && make install
|
||||||
cd puppet && make dist
|
|
||||||
|
.PHONY: puppet_uninstall
|
||||||
.PHONY: puppet_install
|
puppet_uninstall: puppet/version.sh
|
||||||
puppet_install: puppet/version.sh
|
cd puppet && make uninstall
|
||||||
cd puppet && make install
|
|
||||||
|
################ /puppet module
|
||||||
.PHONY: puppet_uninstall
|
|
||||||
puppet_uninstall: puppet/version.sh
|
############### Targets for python app
|
||||||
cd puppet && make uninstall
|
|
||||||
|
dist: $(PYTHON_SDIST) puppet
|
||||||
################ /puppet module
|
|
||||||
|
sdist: $(PYTHON_SDIST)
|
||||||
############### Targets for python app
|
|
||||||
|
mercy/version.py: version.sh
|
||||||
dist: $(PYTHON_SDIST) puppet
|
bash -c 'source version.sh && echo "VERSION=\"$${MAJOR}-$${BUILD}\"" > $@'
|
||||||
|
|
||||||
sdist: $(PYTHON_SDIST)
|
$(PYTHON_SDIST): $(PYTHON_FILES)
|
||||||
|
$(PYTHON) setup.py sdist --formats=gztar
|
||||||
mercy/version.py: version.sh
|
|
||||||
bash -c 'source version.sh && echo "VERSION=\"$${MAJOR}-$${BUILD}\"" > $@'
|
uninstall:
|
||||||
|
rm -fr $(VIRTUALENV_PKGS_DIR)/mercy-* || echo 'not installed'
|
||||||
$(PYTHON_SDIST): $(PYTHON_FILES)
|
|
||||||
$(PYTHON) setup.py sdist --formats=gztar
|
install: $(PYTHON_SDIST) virtualenv
|
||||||
|
$(PIP) install $(PYTHON_SDIST) --upgrade
|
||||||
uninstall:
|
|
||||||
rm -fr $(VIRTUALENV_PKGS_DIR)/mercy-* || echo 'not installed'
|
.PHONY: virtualenv
|
||||||
|
virtualenv:
|
||||||
install: $(PYTHON_SDIST) virtualenv
|
if [ ! -e $(PIP) ]; then \
|
||||||
$(PIP) install $(PYTHON_SDIST) --upgrade
|
$(VIRTUALENV) --no-site-packages --distribute virtualenv ; \
|
||||||
|
fi
|
||||||
.PHONY: virtualenv
|
|
||||||
virtualenv:
|
################## /python app
|
||||||
if [ ! -e $(PIP) ]; then \
|
|
||||||
$(VIRTUALENV) --no-site-packages --distribute virtualenv ; \
|
################## Targets for supporting development work
|
||||||
fi
|
|
||||||
|
databases: databases/fda_ndc.zip databases/drugbank.xml.zip
|
||||||
################## /python app
|
|
||||||
|
.PHONY: databases/fda_ndc.zip
|
||||||
################## Targets for supporting development work
|
databases/fda_ndc.zip:
|
||||||
|
rm -fr databases/fda_ndc*
|
||||||
databases: databases/fda_ndc.zip databases/drugbank.xml.zip
|
mkdir -p databases/fda_ndc
|
||||||
|
rm -f fda_ndc.zip
|
||||||
.PHONY: databases/fda_ndc.zip
|
DBPAGE=$$(wget -O - http://www.fda.gov/Drugs/InformationOnDrugs/default.htm --quiet | grep -Eo 'National Drug Code Directory Search(</strong>)*(</a>)*(<br />)*<a href="[a-zA-Z0-9\:/\.]+">More information about the database</a>' | cut -d \" -f 2); \
|
||||||
databases/fda_ndc.zip:
|
LINK=$$(wget -O - http://www.fda.gov/$${DBPAGE} --quiet | grep ">NDC Database File" | cut -d \" -f 2) ; \
|
||||||
rm -fr databases/fda_ndc*
|
wget -O $@ http://www.fda.gov/$${LINK}
|
||||||
mkdir -p databases/fda_ndc
|
cd databases/fda_ndc && unzip -e ../fda_ndc.zip
|
||||||
rm -f fda_ndc.zip
|
|
||||||
DBPAGE=$$(wget -O - http://www.fda.gov/Drugs/InformationOnDrugs/default.htm --quiet | grep -Eo 'National Drug Code Directory Search(</strong>)*(</a>)*(<br />)*<a href="[a-zA-Z0-9\:/\.]+">More information about the database</a>' | cut -d \" -f 2); \
|
.PHONY: databases/drugbank.xml.zip
|
||||||
LINK=$$(wget -O - http://www.fda.gov/$${DBPAGE} --quiet | grep ">NDC Database File" | cut -d \" -f 2) ; \
|
databases/drugbank.xml.zip:
|
||||||
wget -O $@ http://www.fda.gov/$${LINK}
|
rm -fr databases/drugbank*
|
||||||
cd databases/fda_ndc && unzip -e ../fda_ndc.zip
|
mkdir -p databases/drugbank
|
||||||
|
wget -O $@ http://www.drugbank.ca/system/downloads/current/drugbank.xml.zip
|
||||||
.PHONY: databases/drugbank.xml.zip
|
|
||||||
databases/drugbank.xml.zip:
|
|
||||||
rm -fr databases/drugbank*
|
|
||||||
mkdir -p databases/drugbank
|
|
||||||
wget -O $@ http://www.drugbank.ca/system/downloads/current/drugbank.xml.zip
|
|
||||||
cd databases/drugbank && unzip -e ../drugbank.xml.zip
|
cd databases/drugbank && unzip -e ../drugbank.xml.zip
|
||||||
59
README.md
59
README.md
@@ -1,28 +1,31 @@
|
|||||||
mercy
|
mercy
|
||||||
=====
|
=====
|
||||||
|
|
||||||
Mercy is a web application designed to facilitate payments between people who need prescriptions but can't afford them, and charitable people who can afford them
|
This project comprises the entirety of The Mercy Project; everything needed to deploy and serve The Mercy Project currently lives in this repository.
|
||||||
|
|
||||||
The Idea
|
If you came here looking for more information about what (in general) The Mercy Project is, please see http://www.mercyproject.us for more information
|
||||||
========
|
|
||||||
|
Python Application
|
||||||
* Patient: "Here's my prescription, Mr Pharmacist"
|
===================
|
||||||
* Pharmacist: "Sure thing. That'll be $150 USD."
|
|
||||||
* Patient: "I can't afford that! Can you submit it to Mercy for me?"
|
The mercy/ directory contains all of the python code used to serve the Mercy website and perform all necessary functions.
|
||||||
* Pharmacist: "Sure thing. We'll call you when it gets filled."
|
|
||||||
|
Alembic ORM database upgrade scripts
|
||||||
... Meanwhile, back at stately Charitable Person Manor ...
|
====================================
|
||||||
|
|
||||||
* Charitable Person's Phone: *DING*
|
The alembic/ directory contains scripts used by the alembic flask extension to automatically upgrade and downgrade the postgres database used by Mercy.
|
||||||
* Charitable Person: "Huh? Oh! Mercy is telling me there's a new prescription I can pay for. I love helping people!"
|
|
||||||
* Charitable person clicks some buttons
|
Puppet Modules for Deployment
|
||||||
* Charitable Person: "*CLICK* Paid! Enjoy some good health, stranger. Now to post it on my facebook wall and collect the new achievement for buying my first Leukemia drug..."
|
==============================
|
||||||
|
|
||||||
... Back at the pharmacy ...
|
The puppet code necessary to deploy Mercy onto Linux servers lives in the puppet/ directory.
|
||||||
|
|
||||||
* Pharmacist : "Hello, Patient? I just wanted to let you know that your prescription is filled and has been paid in full."
|
Nagios Checks for Monitoring the Mercy Platform
|
||||||
* Patient: "That's so great! I'll come pick it up right away. Now I won't die because my medication is too *!#&$# expensive!"
|
===============================================
|
||||||
|
|
||||||
Then everyone rides dinosaurs into the sunset, happy and healthy. It's pretty awesome.
|
These live in the nagios/ directory.
|
||||||
|
|
||||||
(That's the idea anyway. WE'll see if reality follows.)
|
Automated Test Suites
|
||||||
|
=====================
|
||||||
|
|
||||||
|
tests/ currently holds any and all automated testing (Python Nosetests, currently) for all of the code listed above.
|
||||||
|
|||||||
98
alembic.ini
98
alembic.ini
@@ -1,49 +1,49 @@
|
|||||||
# A generic, single database configuration.
|
# A generic, single database configuration.
|
||||||
|
|
||||||
[alembic]
|
[alembic]
|
||||||
# path to migration scripts
|
# path to migration scripts
|
||||||
script_location = alembic
|
script_location = alembic
|
||||||
|
|
||||||
# template used to generate migration files
|
# template used to generate migration files
|
||||||
# file_template = %%(rev)s_%%(slug)s
|
# file_template = %%(rev)s_%%(slug)s
|
||||||
|
|
||||||
# set to 'true' to run the environment during
|
# set to 'true' to run the environment during
|
||||||
# the 'revision' command, regardless of autogenerate
|
# the 'revision' command, regardless of autogenerate
|
||||||
# revision_environment = false
|
# revision_environment = false
|
||||||
|
|
||||||
sqlalchemy.url = postgresql://mercy:mercy@postgresql.aklabs.net/mercy
|
sqlalchemy.url = postgresql://mercy:mercy@postgresql.aklabs.net/mercy
|
||||||
|
|
||||||
# Logging configuration
|
# Logging configuration
|
||||||
[loggers]
|
[loggers]
|
||||||
keys = root,sqlalchemy,alembic
|
keys = root,sqlalchemy,alembic
|
||||||
|
|
||||||
[handlers]
|
[handlers]
|
||||||
keys = console
|
keys = console
|
||||||
|
|
||||||
[formatters]
|
[formatters]
|
||||||
keys = generic
|
keys = generic
|
||||||
|
|
||||||
[logger_root]
|
[logger_root]
|
||||||
level = WARN
|
level = WARN
|
||||||
handlers = console
|
handlers = console
|
||||||
qualname =
|
qualname =
|
||||||
|
|
||||||
[logger_sqlalchemy]
|
[logger_sqlalchemy]
|
||||||
level = WARN
|
level = WARN
|
||||||
handlers =
|
handlers =
|
||||||
qualname = sqlalchemy.engine
|
qualname = sqlalchemy.engine
|
||||||
|
|
||||||
[logger_alembic]
|
[logger_alembic]
|
||||||
level = INFO
|
level = INFO
|
||||||
handlers =
|
handlers =
|
||||||
qualname = alembic
|
qualname = alembic
|
||||||
|
|
||||||
[handler_console]
|
[handler_console]
|
||||||
class = StreamHandler
|
class = StreamHandler
|
||||||
args = (sys.stderr,)
|
args = (sys.stderr,)
|
||||||
level = NOTSET
|
level = NOTSET
|
||||||
formatter = generic
|
formatter = generic
|
||||||
|
|
||||||
[formatter_generic]
|
[formatter_generic]
|
||||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||||
datefmt = %H:%M:%S
|
datefmt = %H:%M:%S
|
||||||
|
|||||||
150
alembic/env.py
150
alembic/env.py
@@ -1,71 +1,79 @@
|
|||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
from alembic import context
|
from alembic import context
|
||||||
from sqlalchemy import engine_from_config, pool
|
from sqlalchemy import engine_from_config, pool
|
||||||
from logging.config import fileConfig
|
from logging.config import fileConfig
|
||||||
|
import mercy.MercyApplication
|
||||||
# this is the Alembic Config object, which provides
|
import mercy.models
|
||||||
# access to the values within the .ini file in use.
|
import mercy.config
|
||||||
config = context.config
|
|
||||||
|
db = mercy.MercyApplication.get_db()
|
||||||
# Interpret the config file for Python logging.
|
# this is the Alembic Config object, which provides
|
||||||
# This line sets up loggers basically.
|
# access to the values within the .ini file in use.
|
||||||
fileConfig(config.config_file_name)
|
config = context.config
|
||||||
|
|
||||||
# add your model's MetaData object here
|
# Interpret the config file for Python logging.
|
||||||
# for 'autogenerate' support
|
# This line sets up loggers basically.
|
||||||
# from myapp import mymodel
|
fileConfig(config.config_file_name)
|
||||||
# target_metadata = mymodel.Base.metadata
|
|
||||||
target_metadata = None
|
# add your model's MetaData object here
|
||||||
|
# for 'autogenerate' support
|
||||||
# other values from the config, defined by the needs of env.py,
|
# from myapp import mymodel
|
||||||
# can be acquired:
|
# target_metadata = mymodel.Base.metadata
|
||||||
# my_important_option = config.get_main_option("my_important_option")
|
target_metadata = db.Model.metadata
|
||||||
# ... etc.
|
|
||||||
|
# other values from the config, defined by the needs of env.py,
|
||||||
def run_migrations_offline():
|
# can be acquired:
|
||||||
"""Run migrations in 'offline' mode.
|
# my_important_option = config.get_main_option("my_important_option")
|
||||||
|
# ... etc.
|
||||||
This configures the context with just a URL
|
|
||||||
and not an Engine, though an Engine is acceptable
|
def run_migrations_offline():
|
||||||
here as well. By skipping the Engine creation
|
"""Run migrations in 'offline' mode.
|
||||||
we don't even need a DBAPI to be available.
|
|
||||||
|
This configures the context with just a URL
|
||||||
Calls to context.execute() here emit the given string to the
|
and not an Engine, though an Engine is acceptable
|
||||||
script output.
|
here as well. By skipping the Engine creation
|
||||||
|
we don't even need a DBAPI to be available.
|
||||||
"""
|
|
||||||
url = config.get_main_option("sqlalchemy.url")
|
Calls to context.execute() here emit the given string to the
|
||||||
context.configure(url=url)
|
script output.
|
||||||
|
|
||||||
with context.begin_transaction():
|
"""
|
||||||
context.run_migrations()
|
url = config.get_main_option("sqlalchemy.url")
|
||||||
|
context.configure(url=url)
|
||||||
def run_migrations_online():
|
|
||||||
"""Run migrations in 'online' mode.
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
In this scenario we need to create an Engine
|
|
||||||
and associate a connection with the context.
|
def run_migrations_online():
|
||||||
|
"""Run migrations in 'online' mode.
|
||||||
"""
|
|
||||||
engine = engine_from_config(
|
In this scenario we need to create an Engine
|
||||||
config.get_section(config.config_ini_section),
|
and associate a connection with the context.
|
||||||
prefix='sqlalchemy.',
|
|
||||||
poolclass=pool.NullPool)
|
"""
|
||||||
|
|
||||||
connection = engine.connect()
|
alembic_config = config.get_section(config.config_ini_section)
|
||||||
context.configure(
|
alembic_config['sqlalchemy.url'] = mercy.config.SQLALCHEMY_URI
|
||||||
connection=connection,
|
|
||||||
target_metadata=target_metadata
|
engine = engine_from_config(
|
||||||
)
|
config.get_section(config.config_ini_section),
|
||||||
|
prefix='sqlalchemy.',
|
||||||
try:
|
poolclass=pool.NullPool)
|
||||||
with context.begin_transaction():
|
|
||||||
context.run_migrations()
|
connection = engine.connect()
|
||||||
finally:
|
context.configure(
|
||||||
connection.close()
|
connection=connection,
|
||||||
|
target_metadata=target_metadata
|
||||||
if context.is_offline_mode():
|
)
|
||||||
run_migrations_offline()
|
|
||||||
else:
|
try:
|
||||||
run_migrations_online()
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
finally:
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
if context.is_offline_mode():
|
||||||
|
run_migrations_offline()
|
||||||
|
else:
|
||||||
|
run_migrations_online()
|
||||||
|
|
||||||
|
|||||||
BIN
alembic/env.pyc
BIN
alembic/env.pyc
Binary file not shown.
158
alembic/versions/2b64ad923738_initial_schema.py
Normal file
158
alembic/versions/2b64ad923738_initial_schema.py
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
"""Initial schema
|
||||||
|
|
||||||
|
Revision ID: 2b64ad923738
|
||||||
|
Revises: None
|
||||||
|
Create Date: 2013-10-27 11:46:11.475707
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '2b64ad923738'
|
||||||
|
down_revision = None
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('fda_products',
|
||||||
|
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False),
|
||||||
|
sa.Column('productid', sa.String(), index=True, unique=True, nullable=False),
|
||||||
|
sa.Column('ndc', sa.String(), index=True, nullable=False),
|
||||||
|
sa.Column('type', sa.String(), nullable=False),
|
||||||
|
sa.Column('proprietaryName', sa.String(), nullable=False),
|
||||||
|
sa.Column('proprietaryNameSuffix', sa.String(), nullable=True),
|
||||||
|
sa.Column('genericName', sa.String(), nullable=False),
|
||||||
|
sa.Column('marketingCategoryName', sa.String(), nullable=False),
|
||||||
|
sa.Column('labelerName', sa.String(), nullable=False),
|
||||||
|
sa.Column('deaSchedule', sa.String(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_table('drugbank_packagers',
|
||||||
|
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=False),
|
||||||
|
sa.Column('url', sa.String(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('name')
|
||||||
|
)
|
||||||
|
op.create_table('drugbank_manufacturers',
|
||||||
|
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('name')
|
||||||
|
)
|
||||||
|
op.create_table('fda_pharma_classes',
|
||||||
|
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('name')
|
||||||
|
)
|
||||||
|
op.create_table('fda_product_substances',
|
||||||
|
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_table('drugbank_categories',
|
||||||
|
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('name')
|
||||||
|
)
|
||||||
|
op.create_table('fda_pharma_class_maps',
|
||||||
|
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False),
|
||||||
|
sa.Column('product_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('pharma_id', sa.Integer(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['pharma_id'], ['fda_pharma_classes.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['product_id'], ['fda_products.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id', 'pharma_id')
|
||||||
|
)
|
||||||
|
op.create_table('drugbank_drugs',
|
||||||
|
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False),
|
||||||
|
sa.Column('dbid', sa.String(), unique=True, nullable=True),
|
||||||
|
sa.Column('name', sa.String(), nullable=False),
|
||||||
|
sa.Column('indication', sa.String(), nullable=False),
|
||||||
|
sa.Column('fda_product_id', sa.String(), nullable=True),
|
||||||
|
sa.Column('wikipedia', sa.String(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['fda_product_id'], ['fda_products.productid'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_table('fda_product_substance_map',
|
||||||
|
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False),
|
||||||
|
sa.Column('product_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('substance_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('quantity', sa.Float(), nullable=False),
|
||||||
|
sa.Column('units', sa.String(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['product_id'], ['fda_products.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['substance_id'], ['fda_product_substances.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
op.create_table('drugbank_synonyms',
|
||||||
|
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False),
|
||||||
|
sa.Column('drug_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['drug_id'], ['drugbank_drugs.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('drug_id')
|
||||||
|
)
|
||||||
|
op.create_table('drugbank_packager_maps',
|
||||||
|
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False),
|
||||||
|
sa.Column('drug_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('packager_id', sa.Integer(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['drug_id'], ['drugbank_drugs.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['packager_id'], ['drugbank_packagers.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('drug_id')
|
||||||
|
)
|
||||||
|
op.create_table('drugbank_genericnames',
|
||||||
|
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False),
|
||||||
|
sa.Column('drug_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['drug_id'], ['drugbank_drugs.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('drug_id')
|
||||||
|
)
|
||||||
|
op.create_table('drugbank_prices',
|
||||||
|
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False),
|
||||||
|
sa.Column('drug_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('description', sa.String(), nullable=False),
|
||||||
|
sa.Column('currency', sa.String(), nullable=False),
|
||||||
|
sa.Column('cost', sa.Float(), nullable=False),
|
||||||
|
sa.Column('unit', sa.String(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['drug_id'], ['drugbank_drugs.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('drug_id')
|
||||||
|
)
|
||||||
|
op.create_table('drugbank_manufacturer_maps',
|
||||||
|
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False),
|
||||||
|
sa.Column('drug_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('manufacturer_id', sa.Integer(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['drug_id'], ['drugbank_drugs.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['manufacturer_id'], ['drugbank_manufacturers.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('drug_id')
|
||||||
|
)
|
||||||
|
op.create_table('drugbank_category_maps',
|
||||||
|
sa.Column('id', sa.Integer(), primary_key=True, autoincrement=True, nullable=False),
|
||||||
|
sa.Column('drug_id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('category_id', sa.Integer(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['category_id'], ['drugbank_categories.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['drug_id'], ['drugbank_drugs.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('drug_id')
|
||||||
|
)
|
||||||
|
### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table('drugbank_category_maps')
|
||||||
|
op.drop_table('drugbank_manufacturer_maps')
|
||||||
|
op.drop_table('drugbank_prices')
|
||||||
|
op.drop_table('drugbank_genericnames')
|
||||||
|
op.drop_table('drugbank_packager_maps')
|
||||||
|
op.drop_table('drugbank_synonyms')
|
||||||
|
op.drop_table('fda_product_substance_map')
|
||||||
|
op.drop_table('drugbank_drugs')
|
||||||
|
op.drop_table('fda_pharma_class_maps')
|
||||||
|
op.drop_table('drugbank_categories')
|
||||||
|
op.drop_table('fda_product_substances')
|
||||||
|
op.drop_table('fda_pharma_classes')
|
||||||
|
op.drop_table('fda_products')
|
||||||
|
op.drop_table('drugbank_manufacturers')
|
||||||
|
op.drop_table('drugbank_packagers')
|
||||||
|
### end Alembic commands ###
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
"""create fda_product table
|
|
||||||
|
|
||||||
Revision ID: 58f6a99bd6ec
|
|
||||||
Revises: None
|
|
||||||
Create Date: 2013-10-19 21:21:03.977000
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision = '58f6a99bd6ec'
|
|
||||||
down_revision = None
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
|
||||||
op.create_table(
|
|
||||||
'fda_products',
|
|
||||||
sa.Column('id', sa.String, primary_key=True),
|
|
||||||
sa.Column('ndc', sa.String, nullable=False),
|
|
||||||
sa.Column('type', sa.String, nullable=False),
|
|
||||||
sa.Column('proprietaryName', sa.String, nullable=False, index=True),
|
|
||||||
sa.Column('proprietaryNameSuffix', sa.String),
|
|
||||||
sa.Column('genericName', sa.String, nullable=False),
|
|
||||||
sa.Column('marketingCategoryName', sa.String, nullable=False),
|
|
||||||
sa.Column('labelerName', sa.String, nullable=False),
|
|
||||||
sa.Column('deaSchedule', sa.String, nullable=False)
|
|
||||||
)
|
|
||||||
op.create_table(
|
|
||||||
'fda_product_substances',
|
|
||||||
sa.Column('fda_product_id',
|
|
||||||
sa.String,
|
|
||||||
sa.ForeignKey('fda_products.id'),
|
|
||||||
nullable=False),
|
|
||||||
sa.Column('substanceName', sa.String, nullable=False),
|
|
||||||
sa.Column('strengthNumber', sa.String, nullable=False),
|
|
||||||
sa.Column('strengthUnit', sa.String, nullable=False),
|
|
||||||
sa.Column('pharmaClasses', sa.String, nullable=False)
|
|
||||||
)
|
|
||||||
op.create_table(
|
|
||||||
'drugbank_drugs',
|
|
||||||
sa.Column('id', sa.String, primary_key=True, unique=True),
|
|
||||||
sa.Column('name', sa.String, nullable=False, index=True),
|
|
||||||
sa.Column('indication', sa.String, nullable=False),
|
|
||||||
sa.Column('ndc_id', sa.String, sa.ForeignKey('fda_products.id'), nullable=True),
|
|
||||||
sa.Column('wikipedia', sa.String, nullable=True)
|
|
||||||
)
|
|
||||||
op.create_table(
|
|
||||||
'drugbank_prices',
|
|
||||||
sa.Column('id', sa.String, sa.ForeignKey('drugbank_drugs.id'), nullable=False),
|
|
||||||
sa.Column('description', sa.String, nullable=False),
|
|
||||||
sa.Column('currency', sa.String, nullable=False),
|
|
||||||
sa.Column('cost', sa.Float, nullable=False, index=True),
|
|
||||||
sa.Column('unit', sa.String, nullable=False)
|
|
||||||
)
|
|
||||||
op.create_table(
|
|
||||||
'drugbank_categories',
|
|
||||||
sa.Column('id', sa.Integer, primary_key=True, autoincrement=True),
|
|
||||||
sa.Column('name', sa.String, nullable=False)
|
|
||||||
)
|
|
||||||
op.create_table(
|
|
||||||
'drugbank_drug_categories',
|
|
||||||
sa.Column('id', sa.String, sa.ForeignKey('drugbank_drugs.id'), nullable=False),
|
|
||||||
sa.Column('category_id', sa.Integer, sa.ForeignKey('drugbank_categories.id'), nullable=False)
|
|
||||||
)
|
|
||||||
op.create_table(
|
|
||||||
'drugbank_packagers',
|
|
||||||
sa.Column('id', sa.String, sa.ForeignKey('drugbank_drugs.id'), nullable=False),
|
|
||||||
sa.Column('name', sa.String, nullable=False),
|
|
||||||
sa.Column('url', sa.String, nullable=False)
|
|
||||||
)
|
|
||||||
op.create_table(
|
|
||||||
'drugbank_manufacturers',
|
|
||||||
sa.Column('id', sa.String, sa.ForeignKey('drugbank_drugs.id'), nullable=False),
|
|
||||||
sa.Column('name', sa.String, nullable=False),
|
|
||||||
sa.Column('generic', sa.Boolean, nullable=False)
|
|
||||||
)
|
|
||||||
op.create_table(
|
|
||||||
'drugbank_genericnames',
|
|
||||||
sa.Column('id', sa.String, sa.ForeignKey('drugbank_drugs.id'), nullable=False),
|
|
||||||
sa.Column('name', sa.String, index=True, nullable=False)
|
|
||||||
)
|
|
||||||
op.create_table(
|
|
||||||
'drugbank_synonyms',
|
|
||||||
sa.Column('id', sa.String, sa.ForeignKey('drugbank_drugs.id'), nullable=False),
|
|
||||||
sa.Column('name', sa.String, index=True, nullable=False)
|
|
||||||
)
|
|
||||||
|
|
||||||
def downgrade():
|
|
||||||
op.drop_table("drugbank_synonyms")
|
|
||||||
op.drop_table("drugbank_genericnames")
|
|
||||||
op.drop_table("drugbank_manufacturers")
|
|
||||||
op.drop_table("drugbank_packagers")
|
|
||||||
op.drop_table("drugbank_drug_categories")
|
|
||||||
op.drop_table("drugbank_categories")
|
|
||||||
op.drop_table("drugbank_prices")
|
|
||||||
op.drop_table("drugbank_drugs")
|
|
||||||
op.drop_table("fda_product_substances")
|
|
||||||
op.drop_table("fda_products")
|
|
||||||
@@ -1,4 +1,22 @@
|
|||||||
import flask
|
import flask
|
||||||
|
import mercy.config
|
||||||
class MercyApplication(flask.Flask):
|
from flask.ext.sqlalchemy import SQLAlchemy
|
||||||
pass
|
|
||||||
|
class MercyApplication(flask.Flask):
|
||||||
|
pass
|
||||||
|
|
||||||
|
app = None
|
||||||
|
db = None
|
||||||
|
|
||||||
|
def get_db():
|
||||||
|
global db
|
||||||
|
if not db:
|
||||||
|
db = SQLAlchemy(get_app())
|
||||||
|
return db
|
||||||
|
|
||||||
|
def get_app():
|
||||||
|
global app
|
||||||
|
if not app:
|
||||||
|
app = MercyApplication("mercy")
|
||||||
|
app.config['SQLALCHEMY_DATABASE_URI'] = mercy.config.SQLALCHEMY_URI
|
||||||
|
return app
|
||||||
|
|||||||
1
mercy/config.py
Normal file
1
mercy/config.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
SQLALCHEMY_URI = 'postgresql://mercy:mercy@postgresql.aklabs.net/mercy'
|
||||||
3
mercy/exceptions.py
Normal file
3
mercy/exceptions.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
class CorruptTarError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
0
mercy/importers/__init__.py
Normal file
0
mercy/importers/__init__.py
Normal file
31
mercy/importers/drugbank.py
Normal file
31
mercy/importers/drugbank.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import mercy.MercyApplication
|
||||||
|
import xml.dom.pulldom
|
||||||
|
from mercy.models.drugbank import *
|
||||||
|
import sqlalchemy.exc
|
||||||
|
import xpath
|
||||||
|
|
||||||
|
class DrugBankImporter:
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.__db = mercy.MercyApplication.get_db()
|
||||||
|
|
||||||
|
def _saveobj(self, obj):
|
||||||
|
self.__db.session.add(obj)
|
||||||
|
self.__db.session.commit()
|
||||||
|
|
||||||
|
def read(self, fname):
|
||||||
|
events = xml.dom.pulldom.parse(fname)
|
||||||
|
for event, node in events:
|
||||||
|
if event == xml.dom.pulldom.START_ELEMENT and node.tagName == 'drug':
|
||||||
|
events.expandNode(node)
|
||||||
|
self.__convert_drug(node)
|
||||||
|
|
||||||
|
def __convert_drug(self, node):
|
||||||
|
drug = Drug()
|
||||||
|
drug.name = xpath.findvalue('name', node)
|
||||||
|
drug.indication = xpath.findvalue('indication', node)
|
||||||
|
drug.fda_product_id = xpath.findvalue('external-identifiers/external-identifier[starts-with(resource, "National Drug Code Directory")]/identifier', node)
|
||||||
|
drug.wikipedia = xpath.findvalue('external-links/external-link[starts-with(resource, "Wikipedia")]/url', node)
|
||||||
|
if not drug.fda_product_id:
|
||||||
|
return
|
||||||
|
print str(drug)
|
||||||
|
#self._saveobj(drug)
|
||||||
107
mercy/importers/fda.py
Normal file
107
mercy/importers/fda.py
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import mercy.MercyApplication
|
||||||
|
import xml.etree.cElementTree as ET
|
||||||
|
from mercy.models.fda import Product
|
||||||
|
from mercy.models.fda import ProductSubstance
|
||||||
|
from mercy.models.fda import ProductSubstanceMap
|
||||||
|
from mercy.models.fda import PharmaceuticalClass
|
||||||
|
from mercy.models.fda import PharmaceuticalClassMap
|
||||||
|
import sqlalchemy.exc
|
||||||
|
import csv
|
||||||
|
|
||||||
|
class FDAImporter:
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.__db = mercy.MercyApplication.get_db()
|
||||||
|
|
||||||
|
def read(self, fname, startIdx=0):
|
||||||
|
with open(fname, "r") as ifile:
|
||||||
|
reader = csv.DictReader(ifile, delimiter="\t")
|
||||||
|
idx = 0
|
||||||
|
for row in reader:
|
||||||
|
if idx < startIdx:
|
||||||
|
print "Skipping from {} to {}".format(idx, startIdx)
|
||||||
|
idx += 1
|
||||||
|
continue
|
||||||
|
retries = 0
|
||||||
|
print "{} : {}".format(idx, row)
|
||||||
|
while retries < 3 :
|
||||||
|
try:
|
||||||
|
self._convert_row(row)
|
||||||
|
retries = 3
|
||||||
|
except sqlalchemy.exc.DatabaseError, e:
|
||||||
|
retries += 1
|
||||||
|
if retries == 3:
|
||||||
|
raise e
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
idx += 1
|
||||||
|
|
||||||
|
|
||||||
|
def _saveobj(self, obj):
|
||||||
|
self.__db.session.add(obj)
|
||||||
|
self.__db.session.commit()
|
||||||
|
|
||||||
|
def _convert_row(self, row):
|
||||||
|
# The FDA CSV is HIGHLY irregular. This function is needlessly large because of that/
|
||||||
|
product = Product.query.filter_by(productid=row['PRODUCTID']).first()
|
||||||
|
if not product:
|
||||||
|
product = Product()
|
||||||
|
product.productid = row['PRODUCTID']
|
||||||
|
product.ndc = row['PRODUCTNDC']
|
||||||
|
product.type = row['PRODUCTTYPENAME']
|
||||||
|
product.proprietaryName = row['PROPRIETARYNAME']
|
||||||
|
product.proprietaryNameSuffix = row['PROPRIETARYNAMESUFFIX']
|
||||||
|
product.genericName = row['NONPROPRIETARYNAME']
|
||||||
|
product.marketingCategoryName = row['MARKETINGCATEGORYNAME']
|
||||||
|
product.labelerName = row['LABELERNAME']
|
||||||
|
product.deaSchedule = (row['DEASCHEDULE'] if row['DEASCHEDULE'] else '')
|
||||||
|
self._saveobj(product)
|
||||||
|
|
||||||
|
# Strip the substances off of the product and make objects for them
|
||||||
|
substanceNames = [x.strip().lstrip() for x in row['SUBSTANCENAME'].split(';')]
|
||||||
|
tmpQtys = [x.strip().lstrip() for x in row['ACTIVE_NUMERATOR_STRENGTH'].split(';')]
|
||||||
|
# The list addition here is to make sure we have an equal number of values in
|
||||||
|
# quantities as we do in substance names, because the FDA CSV does not ensure
|
||||||
|
# that all substance names have a quantity or unit listed. So unknown quantities
|
||||||
|
# get entered as '0', unknown units of measure become '?'.
|
||||||
|
substanceQtys = [(float(x) if x else 0.0) for x in tmpQtys] + ([0] * (len(substanceNames) - len(tmpQtys)))
|
||||||
|
substanceUnits = [x.strip().lstrip() for x in row['ACTIVE_INGRED_UNIT'].split(';')]
|
||||||
|
substanceUnits += (['?'] * (len(substanceNames) - len(substanceUnits)))
|
||||||
|
|
||||||
|
for idx in range(0, len(substanceNames)):
|
||||||
|
substance = None
|
||||||
|
substanceName = substanceNames[idx]
|
||||||
|
|
||||||
|
substance = ProductSubstance.query.filter_by(name=substanceName).first()
|
||||||
|
if not substance:
|
||||||
|
substance = ProductSubstance()
|
||||||
|
substance.name = substanceName
|
||||||
|
self._saveobj(substance)
|
||||||
|
substanceMap = ProductSubstanceMap.query.filter_by(product_id=product.id,
|
||||||
|
substance_id=substance.id,
|
||||||
|
quantity=substanceQtys[idx],
|
||||||
|
units=substanceUnits[idx]).first()
|
||||||
|
if not substanceMap:
|
||||||
|
substanceMap = ProductSubstanceMap()
|
||||||
|
substanceMap.product_id = product.id
|
||||||
|
substanceMap.substance_id = substance.id
|
||||||
|
substanceMap.quantity = substanceQtys[idx]
|
||||||
|
substanceMap.units = substanceUnits[idx]
|
||||||
|
self._saveobj(substanceMap)
|
||||||
|
|
||||||
|
pharmaClassList = row.get('PHARM_CLASSES')
|
||||||
|
pharmaClasses = [x.strip().lstrip() for x in (pharmaClassList if pharmaClassList else '').split(',')]
|
||||||
|
for pharmaClass in pharmaClasses:
|
||||||
|
pharmaObj = PharmaceuticalClass.query.filter_by(name=pharmaClass).first()
|
||||||
|
if not pharmaObj:
|
||||||
|
pharmaObj = PharmaceuticalClass()
|
||||||
|
pharmaObj.name = pharmaClass
|
||||||
|
self._saveobj(pharmaObj)
|
||||||
|
mapObj = PharmaceuticalClassMap.query.filter_by(
|
||||||
|
product_id=product.id,
|
||||||
|
pharma_id=pharmaObj.id).first()
|
||||||
|
if not mapObj:
|
||||||
|
mapObj = PharmaceuticalClassMap()
|
||||||
|
mapObj.product_id = product.id
|
||||||
|
mapObj.pharma_id = pharmaObj.id
|
||||||
|
self._saveobj(mapObj)
|
||||||
|
return
|
||||||
3
mercy/models/__init__.py
Normal file
3
mercy/models/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import simplemodel
|
||||||
|
import fda
|
||||||
|
import drugbank
|
||||||
78
mercy/models/drugbank.py
Normal file
78
mercy/models/drugbank.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import sqlalchemy as sa
|
||||||
|
from mercy.models.simplemodel import SimpleModel
|
||||||
|
import mercy.MercyApplication
|
||||||
|
from mercy.models.fda import Product
|
||||||
|
import sqlalchemy.dialects.postgresql as pgdialect
|
||||||
|
|
||||||
|
db = mercy.MercyApplication.get_db()
|
||||||
|
|
||||||
|
class Drug(SimpleModel, db.Model):
|
||||||
|
__tablename__ = "drugbank_drugs"
|
||||||
|
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||||
|
dbid = sa.Column(sa.String, index=True, unique=True)
|
||||||
|
name = sa.Column(sa.String, nullable=False, index=True)
|
||||||
|
indication = sa.Column(sa.String, nullable=False)
|
||||||
|
fda_product_id = sa.Column(sa.String, sa.ForeignKey(Product.productid), nullable=True)
|
||||||
|
wikipedia = sa.Column(sa.String, nullable=True)
|
||||||
|
|
||||||
|
__repr_keys__ = { 'id': basestring,
|
||||||
|
'name': basestring,
|
||||||
|
'fda_product_id': basestring,
|
||||||
|
'wikipedia': basestring
|
||||||
|
}
|
||||||
|
|
||||||
|
class Price(SimpleModel, db.Model):
|
||||||
|
__tablename__ = "drugbank_prices"
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||||
|
drug_id = sa.Column(sa.Integer, sa.ForeignKey(Drug.id), nullable=False)
|
||||||
|
description = sa.Column(sa.String, nullable=False)
|
||||||
|
currency = sa.Column(sa.String, nullable=False)
|
||||||
|
cost = sa.Column(sa.Float, nullable=False, index=True)
|
||||||
|
unit = sa.Column(sa.String, nullable=False)
|
||||||
|
|
||||||
|
class CategoryName(SimpleModel, db.Model):
|
||||||
|
__tablename__ = "drugbank_categories"
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||||
|
name = sa.Column(sa.String, nullable=False, unique=True)
|
||||||
|
|
||||||
|
class CategoryMap(SimpleModel, db.Model):
|
||||||
|
__tablename__ = "drugbank_category_maps"
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||||
|
drug_id = sa.Column(sa.Integer, sa.ForeignKey(Drug.id), nullable=False)
|
||||||
|
category_id = sa.Column(sa.Integer, sa.ForeignKey(CategoryName.id), nullable=False)
|
||||||
|
|
||||||
|
class Packager(SimpleModel, db.Model):
|
||||||
|
__tablename__ = "drugbank_packagers"
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||||
|
name = sa.Column(sa.String, nullable=False, unique=True)
|
||||||
|
url = sa.Column(sa.String, nullable=True)
|
||||||
|
|
||||||
|
class PackagerMap(SimpleModel, db.Model):
|
||||||
|
__tablename__ = "drugbank_packager_maps"
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||||
|
drug_id = sa.Column(sa.String, sa.ForeignKey(Drug.id), nullable=False)
|
||||||
|
packager_id = sa.Column(sa.Integer, sa.ForeignKey(Packager.id), nullable=False)
|
||||||
|
|
||||||
|
class Manufacturer(SimpleModel, db.Model):
|
||||||
|
__tablename__ = "drugbank_manufacturers"
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||||
|
name = sa.Column(sa.String, nullable=False, unique=True)
|
||||||
|
|
||||||
|
class ManufacturerMap(SimpleModel, db.Model):
|
||||||
|
__tablename__ = "drugbank_manufacturer_maps"
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||||
|
drug_id = sa.Column(sa.Integer, sa.ForeignKey(Drug.id), nullable=False)
|
||||||
|
manufacturer_id = sa.Column(sa.Integer, sa.ForeignKey(Manufacturer.id), nullable=False)
|
||||||
|
|
||||||
|
class GenericName(SimpleModel, db.Model):
|
||||||
|
__tablename__ = "drugbank_genericnames"
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||||
|
drug_id = sa.Column(sa.Integer, sa.ForeignKey(Drug.id), nullable=False)
|
||||||
|
name = sa.Column(sa.String, nullable=False)
|
||||||
|
|
||||||
|
class Synonym(SimpleModel, db.Model):
|
||||||
|
__tablename__ = "drugbank_synonyms"
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||||
|
drug_id = sa.Column(sa.Integer, sa.ForeignKey(Drug.id), nullable=False)
|
||||||
|
name = sa.Column(sa.String, nullable=False)
|
||||||
63
mercy/models/fda.py
Normal file
63
mercy/models/fda.py
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import sqlalchemy as sa
|
||||||
|
from mercy.models.simplemodel import SimpleModel
|
||||||
|
import mercy.MercyApplication
|
||||||
|
import sqlalchemy.dialects.postgresql as pgdialect
|
||||||
|
|
||||||
|
db = mercy.MercyApplication.get_db()
|
||||||
|
|
||||||
|
class Product(SimpleModel, db.Model):
|
||||||
|
__tablename__ = 'fda_products'
|
||||||
|
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||||
|
productid = sa.Column(sa.String, index=True, unique=True, nullable=False)
|
||||||
|
ndc = sa.Column(sa.String, index=True, nullable=False)
|
||||||
|
type = sa.Column(sa.String, nullable=False)
|
||||||
|
proprietaryName = sa.Column(sa.String, nullable=False, index=True)
|
||||||
|
proprietaryNameSuffix = sa.Column(sa.String)
|
||||||
|
genericName = sa.Column(sa.String, nullable=False, index=True)
|
||||||
|
marketingCategoryName = sa.Column(sa.String, nullable=False)
|
||||||
|
labelerName = sa.Column(sa.String, nullable=False)
|
||||||
|
deaSchedule = sa.Column(sa.String, nullable=False)
|
||||||
|
|
||||||
|
__repr_keys__ = { 'id': basestring,
|
||||||
|
'ndc': basestring,
|
||||||
|
'genericName': basestring,
|
||||||
|
'proprietaryName': basestring,
|
||||||
|
'proprietaryNameSuffix': basestring}
|
||||||
|
|
||||||
|
class ProductSubstance(SimpleModel, db.Model):
|
||||||
|
__tablename__ = 'fda_product_substances'
|
||||||
|
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||||
|
name = sa.Column(sa.String, nullable=False)
|
||||||
|
|
||||||
|
class ProductSubstanceMap(SimpleModel, db.Model):
|
||||||
|
__tablename__ = 'fda_product_substance_map'
|
||||||
|
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||||
|
product_id = sa.Column(sa.Integer,
|
||||||
|
sa.ForeignKey(Product.id),
|
||||||
|
nullable=False)
|
||||||
|
substance_id = sa.Column(sa.Integer,
|
||||||
|
sa.ForeignKey(ProductSubstance.id),
|
||||||
|
nullable=False,
|
||||||
|
index=True)
|
||||||
|
quantity = sa.Column(sa.Float, nullable=False)
|
||||||
|
units = sa.Column(sa.String, nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
class PharmaceuticalClass(SimpleModel, db.Model):
|
||||||
|
__tablename__ = "fda_pharma_classes"
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||||
|
name = sa.Column(sa.String, nullable=False, unique=True)
|
||||||
|
|
||||||
|
class PharmaceuticalClassMap(SimpleModel, db.Model):
|
||||||
|
__tablename__ = "fda_pharma_class_maps"
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||||
|
product_id = sa.Column(sa.Integer,
|
||||||
|
sa.ForeignKey(Product.id),
|
||||||
|
nullable=False)
|
||||||
|
pharma_id = sa.Column(sa.Integer,
|
||||||
|
sa.ForeignKey(PharmaceuticalClass.id),
|
||||||
|
primary_key=True,
|
||||||
|
nullable=False)
|
||||||
26
mercy/models/simplemodel.py
Normal file
26
mercy/models/simplemodel.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import mercy.MercyApplication
|
||||||
|
|
||||||
|
db = mercy.MercyApplication.get_db()
|
||||||
|
|
||||||
|
class SimpleModel():
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
db.Model.__init__(self, *args, **kwargs)
|
||||||
|
for (k, v) in kwargs.iteritems():
|
||||||
|
if hasattr(self, k):
|
||||||
|
setattr(self, k, v)
|
||||||
|
else:
|
||||||
|
raise AttributeError("Invalid attribute {} => {}".format(k, v))
|
||||||
|
|
||||||
|
def __repr__(self, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
getattr(self.__class__, "__repr_keys__")
|
||||||
|
except AttributeError, e:
|
||||||
|
return db.Model.__repr__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
values = []
|
||||||
|
for (name, otype) in self.__class__.__repr_keys__.iteritems():
|
||||||
|
if otype == basestring:
|
||||||
|
values.append("'{}'".format(str(getattr(self, name))))
|
||||||
|
else:
|
||||||
|
values.append(str(getattr(self, name)))
|
||||||
|
return "<{}({})>".format(self.__class__.__name__, ', '.join(values))
|
||||||
@@ -1 +1 @@
|
|||||||
VERSION="0.0-0"
|
VERSION="0.0-1"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
mercy/nagios
|
mercy/nagios
|
||||||
============
|
============
|
||||||
|
|
||||||
This is a directory full of nagios plugins meant to monitor the Mercy application's health.
|
This is a directory full of nagios plugins meant to monitor the Mercy application's health.
|
||||||
@@ -1,32 +1,32 @@
|
|||||||
MAJOR:=$(shell bash -c 'source version.sh ; echo $$MAJOR')
|
MAJOR:=$(shell bash -c 'source version.sh ; echo $$MAJOR')
|
||||||
BUILD:=$(shell bash -c 'source version.sh ; echo $$BUILD')
|
BUILD:=$(shell bash -c 'source version.sh ; echo $$BUILD')
|
||||||
MODULEPATH:=$(shell puppet config print modulepath)
|
MODULEPATH:=$(shell puppet config print modulepath)
|
||||||
ifeq ($(MODULEPATH),)
|
ifeq ($(MODULEPATH),)
|
||||||
$(error "I can't figure out your puppet modulepath, this will cause all kinds of bad things to happen with 'make install' and 'make uninstall'")
|
$(error "I can't figure out your puppet modulepath, this will cause all kinds of bad things to happen with 'make install' and 'make uninstall'")
|
||||||
endif
|
endif
|
||||||
PUPPET_DEPS=$(find mercy -type f)
|
PUPPET_DEPS=$(find mercy -type f)
|
||||||
PUPPET_DIST=mercy/pkg/akesterson-mercy-$(MAJOR).$(BUILD).tar.gz
|
PUPPET_DIST=mercy/pkg/akesterson-mercy-$(MAJOR).$(BUILD).tar.gz
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm pkg/*
|
rm pkg/*
|
||||||
rm Modulefile
|
rm Modulefile
|
||||||
|
|
||||||
mercy/Modulefile: Modulefile.template.sh version.sh
|
mercy/Modulefile: Modulefile.template.sh version.sh
|
||||||
source version.sh && bash Modulefile.template.sh > Modulefile
|
source version.sh && bash Modulefile.template.sh > Modulefile
|
||||||
|
|
||||||
$(PUPPET_DIST): $(PUPPET_DEPS) mercy/Modulefile
|
$(PUPPET_DIST): $(PUPPET_DEPS) mercy/Modulefile
|
||||||
rm -f $(PUPPET_DIST)
|
rm -f $(PUPPET_DIST)
|
||||||
mkdir -p $$(dirname $(PUPPET_DIST))
|
mkdir -p $$(dirname $(PUPPET_DIST))
|
||||||
echo 'We would use puppet module tool here but we dont because it doesnt work on windows buildhosts and cant upload from the command line anyway'
|
echo 'We would use puppet module tool here but we dont because it doesnt work on windows buildhosts and cant upload from the command line anyway'
|
||||||
tar --exclude=mercy/Modulefile --exclude=mercy/pkg -czvf $(PUPPET_DIST) mercy/
|
tar --exclude=mercy/Modulefile --exclude=mercy/pkg -czvf $(PUPPET_DIST) mercy/
|
||||||
|
|
||||||
dist: $(PUPPET_DIST)
|
dist: $(PUPPET_DIST)
|
||||||
|
|
||||||
all: $(PUPPET_DIST)
|
all: $(PUPPET_DIST)
|
||||||
|
|
||||||
install: $(PUPPET_DIST)
|
install: $(PUPPET_DIST)
|
||||||
tar -zxvf $(PUPPET_DIST) -C $(MODULEPATH)
|
tar -zxvf $(PUPPET_DIST) -C $(MODULEPATH)
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
rm -fr $(MODULEPATH)/mercy
|
rm -fr $(MODULEPATH)/mercy
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
name 'akesterson-mercy'
|
name 'akesterson-mercy'
|
||||||
version '${MAJOR}-${BUILD}'
|
version '${MAJOR}-${BUILD}'
|
||||||
dependency 'apache/apache'
|
dependency 'apache/apache'
|
||||||
summary 'Puppet module for deploying the mercy web application'
|
summary 'Puppet module for deploying the mercy web application'
|
||||||
description 'Puppet module for deploying the mercy web application'
|
description 'Puppet module for deploying the mercy web application'
|
||||||
project_page http://github.com/akesterson/mercy/tree/master/puppet
|
project_page http://github.com/akesterson/mercy/tree/master/puppet
|
||||||
license 'MIT'
|
license 'MIT'
|
||||||
author 'Andrew Kesterson <andrew@aklabs.net>'
|
author 'Andrew Kesterson <andrew@aklabs.net>'
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
@@ -1,49 +1,49 @@
|
|||||||
mercy
|
mercy
|
||||||
=====
|
=====
|
||||||
|
|
||||||
A puppet module for installing the Mercy Flask application
|
A puppet module for installing the Mercy Flask application
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
============
|
============
|
||||||
|
|
||||||
This puppet module assumes that you have:
|
This puppet module assumes that you have:
|
||||||
|
|
||||||
* A RabbitMQ broker that we can use for Celery tasks
|
* A RabbitMQ broker that we can use for Celery tasks
|
||||||
* Working apache on localhost, with mod_wsgi enabled and with a puppet service we can notify
|
* Working apache on localhost, with mod_wsgi enabled and with a puppet service we can notify
|
||||||
* Working python 2.7 and pip on localhost
|
* Working python 2.7 and pip on localhost
|
||||||
* Working postgres with a username, password, and database for mercy to use
|
* Working postgres with a username, password, and database for mercy to use
|
||||||
|
|
||||||
Aside from that, the module has no dependencies.
|
Aside from that, the module has no dependencies.
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
=========
|
=========
|
||||||
|
|
||||||
class { 'mercy':
|
class { 'mercy':
|
||||||
# ---- These are required, they have no defaults
|
# ---- These are required, they have no defaults
|
||||||
environment => 'dev|production',
|
environment => 'dev|production',
|
||||||
version => 'absent|latest|MAJOR-MINOR',
|
version => 'absent|latest|MAJOR-MINOR',
|
||||||
ensure => 'running|stopped'.
|
ensure => 'running|stopped'.
|
||||||
rabbitmq_uri => 'RABBITMQ_BROKER_URI',
|
rabbitmq_uri => 'RABBITMQ_BROKER_URI',
|
||||||
# ---- Everything below is optional
|
# ---- Everything below is optional
|
||||||
process_user => 'mercy',
|
process_user => 'mercy',
|
||||||
process_group => 'mercy',
|
process_group => 'mercy',
|
||||||
process_threads => 5,
|
process_threads => 5,
|
||||||
servername => $::fqdn,
|
servername => $::fqdn,
|
||||||
rabbitmq_user => 'mercy',
|
rabbitmq_user => 'mercy',
|
||||||
rabbitmq_pw => 'mercy',
|
rabbitmq_pw => 'mercy',
|
||||||
rabbitmq_vhost => 'mercy',
|
rabbitmq_vhost => 'mercy',
|
||||||
vhost_dir => '/etc/apache/httpd/conf.d',
|
vhost_dir => '/etc/apache/httpd/conf.d',
|
||||||
apache_service => 'httpd',
|
apache_service => 'httpd',
|
||||||
port => 443,
|
port => 443,
|
||||||
postgres_uri => 'localhost',
|
postgres_uri => 'localhost',
|
||||||
postgres_user => 'mercy',
|
postgres_user => 'mercy',
|
||||||
postgres_pw => 'mercy',
|
postgres_pw => 'mercy',
|
||||||
postgres_db => 'mercy'
|
postgres_db => 'mercy'
|
||||||
}
|
}
|
||||||
|
|
||||||
If 'environment' is dev, then the mercy application will be installed via a tarball located in ./mercy/files/mercy-${ensure}.tar.gz. If 'environment' is production, then mercy will be installed via pip and ensured at the given version.
|
If 'environment' is dev, then the mercy application will be installed via a tarball located in ./mercy/files/mercy-${ensure}.tar.gz. If 'environment' is production, then mercy will be installed via pip and ensured at the given version.
|
||||||
|
|
||||||
Events
|
Events
|
||||||
======
|
======
|
||||||
|
|
||||||
This puppet module will notify your apache service whenever the vhost is modified, or the mercy application version is updated, since an apache graceful restart will be required in either case.
|
This puppet module will notify your apache service whenever the vhost is modified, or the mercy application version is updated, since an apache graceful restart will be required in either case.
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
class mercy(
|
class mercy(
|
||||||
$environment,
|
$environment,
|
||||||
$version,
|
$version,
|
||||||
$ensure,
|
$ensure,
|
||||||
$rabbitmq_uri,
|
$rabbitmq_uri,
|
||||||
$process_user => 'mercy',
|
$process_user => 'mercy',
|
||||||
$process_group => 'mercy',
|
$process_group => 'mercy',
|
||||||
$process_threads => 5,
|
$process_threads => 5,
|
||||||
$servername => $::fqdn,
|
$servername => $::fqdn,
|
||||||
$rabbitmq_user => 'mercy',
|
$rabbitmq_user => 'mercy',
|
||||||
$rabbitmq_pw => 'mercy',
|
$rabbitmq_pw => 'mercy',
|
||||||
$rabbitmq_vhost => 'mercy',
|
$rabbitmq_vhost => 'mercy',
|
||||||
$vhost_dir => '/etc/apache/httpd/conf.d',
|
$vhost_dir => '/etc/apache/httpd/conf.d',
|
||||||
$apache_service => 'httpd',
|
$apache_service => 'httpd',
|
||||||
$port => 443,
|
$port => 443,
|
||||||
$postgres_uri => 'localhost',
|
$postgres_uri => 'localhost',
|
||||||
$postgres_user => 'mercy',
|
$postgres_user => 'mercy',
|
||||||
$postgres_pw => 'mercy',
|
$postgres_pw => 'mercy',
|
||||||
$postgres_db => 'mercy')
|
$postgres_db => 'mercy')
|
||||||
{
|
{
|
||||||
include 'mercy::params'
|
include 'mercy::params'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
class mercy::params
|
class mercy::params
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<VirtualHost * :<%= scope.lookupvar('::mercy::port') %>>
|
<VirtualHost * :<%= scope.lookupvar('::mercy::port') %>>
|
||||||
ServerName <%= scope.lookupvar('::fqdn') %>
|
ServerName <%= scope.lookupvar('::fqdn') %>
|
||||||
WSGIDaemonProcess mercy user=<%= scope.lookupvar('::mercy::process_user') %> group=<%= scope.lookupvar('::mercy::process_group') %> threads=<%= scope.lookupvar('::mercy::process_threads') %>
|
WSGIDaemonProcess mercy user=<%= scope.lookupvar('::mercy::process_user') %> group=<%= scope.lookupvar('::mercy::process_group') %> threads=<%= scope.lookupvar('::mercy::process_threads') %>
|
||||||
WSGIScriptAlias / /opt/mercy/scripts/mercy.wsgi
|
WSGIScriptAlias / /opt/mercy/scripts/mercy.wsgi
|
||||||
|
|
||||||
<Directory /opt/mercy>
|
<Directory /opt/mercy>
|
||||||
WSGIProcessGroup mercy
|
WSGIProcessGroup mercy
|
||||||
WSGIApplicationGroup %{GLOBAL}
|
WSGIApplicationGroup %{GLOBAL}
|
||||||
Order deny, allow
|
Order deny, allow
|
||||||
Allow from all
|
Allow from all
|
||||||
</Directory>
|
</Directory>
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
@@ -1,49 +1,49 @@
|
|||||||
# A generic, single database configuration.
|
# A generic, single database configuration.
|
||||||
|
|
||||||
[alembic]
|
[alembic]
|
||||||
# path to migration scripts
|
# path to migration scripts
|
||||||
script_location = /opt/mercy/alembic
|
script_location = /opt/mercy/alembic
|
||||||
|
|
||||||
# template used to generate migration files
|
# template used to generate migration files
|
||||||
# file_template = %%(rev)s_%%(slug)s
|
# file_template = %%(rev)s_%%(slug)s
|
||||||
|
|
||||||
# set to 'true' to run the environment during
|
# set to 'true' to run the environment during
|
||||||
# the 'revision' command, regardless of autogenerate
|
# the 'revision' command, regardless of autogenerate
|
||||||
# revision_environment = false
|
# revision_environment = false
|
||||||
|
|
||||||
sqlalchemy.url = postgresql://<%= scope.lookupvar('::mercy::postgres_user') %>:<%+ scope.lookupvar('::mercy::postgres_pw') %>@<%= scope.lookupvar('::mercy::postgres_uri') %>/<%= scope.lookupvar('::mercy::postgres_db') %>
|
sqlalchemy.url = postgresql://<%= scope.lookupvar('::mercy::postgres_user') %>:<%+ scope.lookupvar('::mercy::postgres_pw') %>@<%= scope.lookupvar('::mercy::postgres_uri') %>/<%= scope.lookupvar('::mercy::postgres_db') %>
|
||||||
|
|
||||||
# Logging configuration
|
# Logging configuration
|
||||||
[loggers]
|
[loggers]
|
||||||
keys = root,sqlalchemy,alembic
|
keys = root,sqlalchemy,alembic
|
||||||
|
|
||||||
[handlers]
|
[handlers]
|
||||||
keys = console
|
keys = console
|
||||||
|
|
||||||
[formatters]
|
[formatters]
|
||||||
keys = generic
|
keys = generic
|
||||||
|
|
||||||
[logger_root]
|
[logger_root]
|
||||||
level = WARN
|
level = WARN
|
||||||
handlers = console
|
handlers = console
|
||||||
qualname =
|
qualname =
|
||||||
|
|
||||||
[logger_sqlalchemy]
|
[logger_sqlalchemy]
|
||||||
level = WARN
|
level = WARN
|
||||||
handlers =
|
handlers =
|
||||||
qualname = sqlalchemy.engine
|
qualname = sqlalchemy.engine
|
||||||
|
|
||||||
[logger_alembic]
|
[logger_alembic]
|
||||||
level = INFO
|
level = INFO
|
||||||
handlers =
|
handlers =
|
||||||
qualname = alembic
|
qualname = alembic
|
||||||
|
|
||||||
[handler_console]
|
[handler_console]
|
||||||
class = StreamHandler
|
class = StreamHandler
|
||||||
args = (sys.stderr,)
|
args = (sys.stderr,)
|
||||||
level = NOTSET
|
level = NOTSET
|
||||||
formatter = generic
|
formatter = generic
|
||||||
|
|
||||||
[formatter_generic]
|
[formatter_generic]
|
||||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||||
datefmt = %H:%M:%S
|
datefmt = %H:%M:%S
|
||||||
|
|||||||
6
scripts/mercy-import-drugbank
Normal file
6
scripts/mercy-import-drugbank
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import mercy.importers.drugbank
|
||||||
|
|
||||||
|
sys.exit(mercy.importers.drugbank.DrugBankImporter().read(sys.argv[1]))
|
||||||
10
scripts/mercy-import-fda
Normal file
10
scripts/mercy-import-fda
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import mercy.importers.fda
|
||||||
|
|
||||||
|
startIdx = 0
|
||||||
|
if ( len(sys.argv) >= 3 ):
|
||||||
|
startIdx = int(sys.argv[2])
|
||||||
|
|
||||||
|
sys.exit(mercy.importers.fda.FDAImporter().read(sys.argv[1], startIdx=startIdx))
|
||||||
@@ -1,14 +1,6 @@
|
|||||||
from mercy.MercyApplication import MercyApplication
|
import mercy.MercyApplication
|
||||||
|
|
||||||
class ScriptNameStripper(object):
|
app = get_application()
|
||||||
def __init__(self, app):
|
|
||||||
self.app = app
|
if __name__ == "__main__":
|
||||||
|
app.run()
|
||||||
def __call_(self, environ, start_response):
|
|
||||||
environ['SCRIPT_NAME'] = ''
|
|
||||||
return self.app(environ, start_response)
|
|
||||||
|
|
||||||
app = ScriptNameStripper(MercyApplication())
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app.run()
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
from mercy.MercyApplication import MercyApplication
|
from mercy.MercyApplication import app
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = MercyApplication("mercy")
|
app.run()
|
||||||
app.run()
|
|
||||||
|
|||||||
72
setup.py
72
setup.py
@@ -1,34 +1,38 @@
|
|||||||
from distutils.core import setup
|
from distutils.core import setup
|
||||||
import mercy.version
|
import mercy.version
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
setup(
|
setup(
|
||||||
name="mercy",
|
name="mercy",
|
||||||
url="https://www.github.com/akesterson/mercy",
|
url="https://www.github.com/akesterson/mercy",
|
||||||
version=mercy.version.VERSION,
|
version=mercy.version.VERSION,
|
||||||
description="A flask application that facilitates paying for prescriptions",
|
description="A web application that facilitates charitable prescription payments",
|
||||||
long_description="",
|
long_description="",
|
||||||
author=("Andrew Kesterson"),
|
author=("Andrew Kesterson"),
|
||||||
author_email="andrew@aklabs.net",
|
author_email="andrew@aklabs.net",
|
||||||
license="MIT",
|
license="MIT",
|
||||||
install_requires=["flask",
|
install_requires=["flask",
|
||||||
"sqlalchemy",
|
"sqlalchemy",
|
||||||
"alembic",
|
"alembic",
|
||||||
"psycopg2"],
|
"psycopg2",
|
||||||
scripts=[],
|
"flask-sqlalchemy",
|
||||||
packages=["mercy"],
|
"py-dom-xpath"],
|
||||||
data_files=[],
|
scripts=["scripts/mercy.wsgi"],
|
||||||
classifiers=[
|
packages=["mercy",
|
||||||
'Development Status :: 1 - Planning',
|
"mercy/models",
|
||||||
'Environment :: Web Environment',
|
"mercy/importers"],
|
||||||
'Framework :: Flask',
|
data_files=[],
|
||||||
'Intended Audience :: Healthcare Industry',
|
classifiers=[
|
||||||
'License :: OSI Approved :: MIT License',
|
'Development Status :: 1 - Planning',
|
||||||
'Natural Language :: English',
|
'Environment :: Web Environment',
|
||||||
'Programming Language :: Python :: 2.7',
|
'Framework :: Flask',
|
||||||
'Topic :: Other/Nonlisted Topic',
|
'Intended Audience :: Healthcare Industry',
|
||||||
],
|
'License :: OSI Approved :: MIT License',
|
||||||
)
|
'Natural Language :: English',
|
||||||
|
'Programming Language :: Python :: 2.7',
|
||||||
|
'Topic :: Other/Nonlisted Topic',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|||||||
120
tests/importers/test_fda_importer.py
Normal file
120
tests/importers/test_fda_importer.py
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import os
|
||||||
|
import nose
|
||||||
|
from nose.tools import raises
|
||||||
|
import mercy.models
|
||||||
|
import mercy.importers.fda
|
||||||
|
import mercy.exceptions
|
||||||
|
|
||||||
|
COMPARISON_KEYS={
|
||||||
|
'productid': 'PRODUCTID',
|
||||||
|
'ndc': 'PRODUCTNDC',
|
||||||
|
'type': 'PRODUCTTYPENAME',
|
||||||
|
'proprietaryName': 'PROPRIETARYNAME',
|
||||||
|
'proprietaryNameSuffix': 'PROPRIETARYNAMESUFFIX',
|
||||||
|
'genericName': 'NONPROPRIETARYNAME',
|
||||||
|
'marketingCategoryName': 'MARKETINGCATEGORYNAME',
|
||||||
|
'labelerName': 'LABELERNAME',
|
||||||
|
'deaSchedule': 'DEASCHEDULE'
|
||||||
|
}
|
||||||
|
|
||||||
|
CSV_KEYS=[
|
||||||
|
'PRODUCTID',
|
||||||
|
'PRODUCTNDC',
|
||||||
|
'PRODUCTTYPENAME',
|
||||||
|
'PROPRIETARYNAME',
|
||||||
|
'PROPRIETARYNAMESUFFIX',
|
||||||
|
'NONPROPRIETARYNAME',
|
||||||
|
'DOSAGEFORMNAME',
|
||||||
|
'ROUTENAME',
|
||||||
|
'STARTMARKETINGDATE',
|
||||||
|
'ENDMARKETINGDATE',
|
||||||
|
'MARKETINGCATEGORYNAME',
|
||||||
|
'APPLICATIONNUMBER',
|
||||||
|
'LABELERNAME',
|
||||||
|
'SUBSTANCENAME',
|
||||||
|
'ACTIVE_NUMERATOR_STRENGTH',
|
||||||
|
'ACTIVE_INGRED_UNIT',
|
||||||
|
'PHARM_CLASSES',
|
||||||
|
'DEASCHEDULE'
|
||||||
|
]
|
||||||
|
|
||||||
|
CANNED_ROWS=[
|
||||||
|
{'PRODUCTID': '0002-3230_b1642902-4a44-495a-8790-39598b168276',
|
||||||
|
'PRODUCTNDC': '0002-3230',
|
||||||
|
'PRODUCTTYPENAME': 'HUMAN PRESCRIPTION DRUG',
|
||||||
|
'PROPRIETARYNAME': 'Symbyax',
|
||||||
|
'PROPRIETARYNAMESUFFIX': '',
|
||||||
|
'NONPROPRIETARYNAME': 'Olanzapine and Fluoxetine hydrochloride',
|
||||||
|
'DOSAGEFORMNAME': 'CAPSULE',
|
||||||
|
'ROUTENAME': 'ORAL',
|
||||||
|
'STARTMARKETINGDATE': '20070409',
|
||||||
|
'ENDMARKETINGDATE': '',
|
||||||
|
'MARKETINGCATEGORYNAME': 'NDA',
|
||||||
|
'APPLICATIONNUMBER': 'NDA021520',
|
||||||
|
'LABELERNAME': 'Eli Lilly and Company',
|
||||||
|
'SUBSTANCENAME': 'FLUOXETINE HYDROCHLORIDE; OLANZAPINE',
|
||||||
|
'ACTIVE_NUMERATOR_STRENGTH': '25; 3',
|
||||||
|
'ACTIVE_INGRED_UNIT': 'mg/1; mg/1',
|
||||||
|
'PHARM_CLASSES': 'Atypical Antipsychotic [EPC],Serotonin Reuptake Inhibitor [EPC],Serotonin Uptake Inhibitors [MoA]',
|
||||||
|
'DEASCHEDULE': ''
|
||||||
|
},
|
||||||
|
{'PRODUCTID': '0002-3231_b1642902-4a44-495a-8790-39598b168276',
|
||||||
|
'PRODUCTNDC': '0002-3231',
|
||||||
|
'PRODUCTTYPENAME': 'HUMAN PRESCRIPTION DRUG',
|
||||||
|
'PROPRIETARYNAME': 'Symbyax',
|
||||||
|
'PROPRIETARYNAMESUFFIX': '',
|
||||||
|
'NONPROPRIETARYNAME': 'Olanzapine and Fluoxetine hydrochloride',
|
||||||
|
'DOSAGEFORMNAME': 'CAPSULE',
|
||||||
|
'ROUTENAME': 'ORAL',
|
||||||
|
'STARTMARKETINGDATE': '20070409',
|
||||||
|
'ENDMARKETINGDATE': '',
|
||||||
|
'MARKETINGCATEGORYNAME': 'NDA',
|
||||||
|
'APPLICATIONNUMBER': 'NDA021521',
|
||||||
|
'LABELERNAME': 'Eli Lilly and Company',
|
||||||
|
'SUBSTANCENAME': 'FLUOXETINE HYDROCHLORIDE; OLANZAPINE',
|
||||||
|
'ACTIVE_NUMERATOR_STRENGTH': '25; 6',
|
||||||
|
'ACTIVE_INGRED_UNIT': 'mg/1; mg/1',
|
||||||
|
'PHARM_CLASSES': 'Atypical Antipsychotic [EPC],Serotonin Reuptake Inhibitor [EPC],Serotonin Uptake Inhibitors [MoA]',
|
||||||
|
'DEASCHEDULE': ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
FIXTUREFILE=os.path.abspath(
|
||||||
|
os.path.join(
|
||||||
|
os.path.dirname(__file__),
|
||||||
|
"..",
|
||||||
|
"fixtures",
|
||||||
|
"fda_database.txt"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
FIXTUREFILE_BAD=os.path.abspath(
|
||||||
|
os.path.join(
|
||||||
|
os.path.dirname(__file__),
|
||||||
|
"..",
|
||||||
|
"fixtures",
|
||||||
|
"fda_database_bad.txt"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_fda_import_populates_table():
|
||||||
|
with open(FIXTUREFILE, 'w') as ofile:
|
||||||
|
ofile.write("{}\n".format('\t'.join(CSV_KEYS)))
|
||||||
|
for row in CANNED_ROWS:
|
||||||
|
values = []
|
||||||
|
for key in CSV_KEYS:
|
||||||
|
values.append(row[key])
|
||||||
|
ofile.write("{}\n".format('\t'.join(values)))
|
||||||
|
importer = mercy.importers.fda.FDAImporter()
|
||||||
|
importer.read(FIXTUREFILE)
|
||||||
|
for row in CANNED_ROWS:
|
||||||
|
product = mercy.models.fda.Product.query.filter_by(productid = row['PRODUCTID']).first()
|
||||||
|
assert(product)
|
||||||
|
for (k, v) in COMPARISON_KEYS.iteritems():
|
||||||
|
assert(getattr(product, k) == row[v])
|
||||||
|
mapquery = mercy.models.fda.ProductSubstanceMap.query
|
||||||
|
substanceMaps = [x for x in mapquery.filter_by(product_id=product.id)]
|
||||||
|
assert(len(substanceMaps) == len(row['ACTIVE_NUMERATOR_STRENGTH'].split(';')))
|
||||||
|
# TO DO : This test doesn't look at the contents of the
|
||||||
|
# substances or substance maps, only that the right
|
||||||
|
# number of substance maps come out
|
||||||
15
version.sh
15
version.sh
@@ -1,15 +0,0 @@
|
|||||||
TAG="build,0.0,0"
|
|
||||||
BRANCH="master"
|
|
||||||
MAJOR="0.0"
|
|
||||||
BUILD="0"
|
|
||||||
SHA1="9b83e26ec3d0e3d1526a4226a8ac0c8580a77bcf"
|
|
||||||
OS_NAME="${OS_NAME:-win}"
|
|
||||||
OS_VERSION="${OS_VERSION:-}"
|
|
||||||
ARCH="${ARCH:-i686}"
|
|
||||||
VERSION="0.0-0"
|
|
||||||
BUILDHOST="akesterson-pc"
|
|
||||||
BUILDUSER="akesterson-pc\akesterson"
|
|
||||||
BUILDDIR="/c/Users/akesterson/source/upstream/git/akesterson/mercy"
|
|
||||||
SOURCE="git@github.com:akesterson/mercy.git"
|
|
||||||
REBUILDING=0
|
|
||||||
CHANGELOG=""
|
|
||||||
Reference in New Issue
Block a user