Compare commits
11 Commits
803d1202f6
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c66b0e2af | |||
| 00a6a4da74 | |||
| 867fd0c437 | |||
| b20dc5acab | |||
| 16cc7adb8e | |||
| 2b8707be81 | |||
| 0a7bf8f174 | |||
| e0232aa02d | |||
| e2bc602264 | |||
| ecef739fdd | |||
| 1753230e27 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
virtualenv
|
||||
version.sh
|
||||
|
||||
173
Makefile
173
Makefile
@@ -1,90 +1,85 @@
|
||||
MAJOR:=$(shell bash -c 'source version.sh ; echo $$MAJOR')
|
||||
BUILD:=$(shell bash -c 'source version.sh ; echo $$BUILD')
|
||||
OS_NAME:=$(shell bash -c 'source version.sh ; echo $$OS_NAME')
|
||||
ifeq "$(OS_NAME)" "win"
|
||||
PIP=$(shell pwd)/virtualenv/Scripts/pip
|
||||
VIRTUALENV_PKGS_DIR=$(shell pwd)/virtualenv/Lib/site-packages
|
||||
else
|
||||
PIP=$(shell pwd)/virtualenv/bin/pip
|
||||
VIRTUALENV_PKGS_DIR=$(shell pwd)/virtualenv/lib/site-packages
|
||||
endif
|
||||
VIRTUALENV=$(shell which virtualenv)
|
||||
PYTHON=$(shell which python)
|
||||
|
||||
PYTHON_FILES=setup.py mercy/version.py $(shell find mercy -iname "*py")
|
||||
PYTHON_SDIST=./dist/mercy-$(MAJOR)-$(BUILD).tar.gz
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -fr dist/*
|
||||
find mercy -iname "*pyc" -exec rm -vf \{\} \;
|
||||
cd puppet && make clean
|
||||
|
||||
############## Targets for puppet module
|
||||
|
||||
puppet/version.sh: version.sh
|
||||
cp version.sh $@
|
||||
|
||||
.PHONY: puppet
|
||||
puppet: puppet_dist
|
||||
|
||||
.PHONY: puppet_dist
|
||||
puppet_dist: puppet/version.sh
|
||||
cd puppet && make dist
|
||||
|
||||
.PHONY: puppet_install
|
||||
puppet_install: puppet/version.sh
|
||||
cd puppet && make install
|
||||
|
||||
.PHONY: puppet_uninstall
|
||||
puppet_uninstall: puppet/version.sh
|
||||
cd puppet && make uninstall
|
||||
|
||||
################ /puppet module
|
||||
|
||||
############### Targets for python app
|
||||
|
||||
dist: $(PYTHON_SDIST) puppet
|
||||
|
||||
sdist: $(PYTHON_SDIST)
|
||||
|
||||
mercy/version.py: version.sh
|
||||
bash -c 'source version.sh && echo "VERSION=\"$${MAJOR}-$${BUILD}\"" > $@'
|
||||
|
||||
$(PYTHON_SDIST): $(PYTHON_FILES)
|
||||
$(PYTHON) setup.py sdist --formats=gztar
|
||||
|
||||
uninstall:
|
||||
rm -fr $(VIRTUALENV_PKGS_DIR)/mercy-* || echo 'not installed'
|
||||
|
||||
install: $(PYTHON_SDIST) virtualenv
|
||||
$(PIP) install $(PYTHON_SDIST) --upgrade
|
||||
|
||||
.PHONY: virtualenv
|
||||
virtualenv:
|
||||
if [ ! -e $(PIP) ]; then \
|
||||
$(VIRTUALENV) --no-site-packages --distribute virtualenv ; \
|
||||
fi
|
||||
|
||||
################## /python app
|
||||
|
||||
################## Targets for supporting development work
|
||||
|
||||
databases: databases/fda_ndc.zip databases/drugbank.xml.zip
|
||||
|
||||
.PHONY: databases/fda_ndc.zip
|
||||
databases/fda_ndc.zip:
|
||||
rm -fr databases/fda_ndc*
|
||||
mkdir -p databases/fda_ndc
|
||||
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); \
|
||||
LINK=$$(wget -O - http://www.fda.gov/$${DBPAGE} --quiet | grep ">NDC Database File" | cut -d \" -f 2) ; \
|
||||
wget -O $@ http://www.fda.gov/$${LINK}
|
||||
cd databases/fda_ndc && unzip -e ../fda_ndc.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
|
||||
MAJOR:=$(shell bash -c 'source version.sh ; echo $$MAJOR')
|
||||
BUILD:=$(shell bash -c 'source version.sh ; echo $$BUILD')
|
||||
OS_NAME:=$(shell bash -c 'source version.sh ; echo $$OS_NAME')
|
||||
PIP=$(shell pwd)/virtualenv/bin/pip
|
||||
VIRTUALENV_PKGS_DIR=$(shell pwd)/virtualenv/lib/python2.7/site-packages
|
||||
VIRTUALENV=$(shell which virtualenv)
|
||||
PYTHON=$(shell which python)
|
||||
|
||||
PYTHON_FILES=setup.py mercy/version.py $(shell find mercy -iname "*py")
|
||||
PYTHON_SDIST=./dist/mercy-$(MAJOR)-$(BUILD).tar.gz
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -fr dist/*
|
||||
find mercy -iname "*pyc" -exec rm -vf \{\} \;
|
||||
cd puppet && make clean
|
||||
|
||||
############## Targets for puppet module
|
||||
|
||||
puppet/version.sh: version.sh
|
||||
cp version.sh $@
|
||||
|
||||
.PHONY: puppet
|
||||
puppet: puppet_dist
|
||||
|
||||
.PHONY: puppet_dist
|
||||
puppet_dist: puppet/version.sh
|
||||
cd puppet && make dist
|
||||
|
||||
.PHONY: puppet_install
|
||||
puppet_install: puppet/version.sh
|
||||
cd puppet && make install
|
||||
|
||||
.PHONY: puppet_uninstall
|
||||
puppet_uninstall: puppet/version.sh
|
||||
cd puppet && make uninstall
|
||||
|
||||
################ /puppet module
|
||||
|
||||
############### Targets for python app
|
||||
|
||||
dist: $(PYTHON_SDIST) puppet
|
||||
|
||||
sdist: $(PYTHON_SDIST)
|
||||
|
||||
mercy/version.py: version.sh
|
||||
bash -c 'source version.sh && echo "VERSION=\"$${MAJOR}-$${BUILD}\"" > $@'
|
||||
|
||||
$(PYTHON_SDIST): $(PYTHON_FILES)
|
||||
$(PYTHON) setup.py sdist --formats=gztar
|
||||
|
||||
uninstall:
|
||||
rm -fr $(VIRTUALENV_PKGS_DIR)/mercy-* || echo 'not installed'
|
||||
|
||||
install: $(PYTHON_SDIST) virtualenv
|
||||
$(PIP) install $(PYTHON_SDIST) --upgrade
|
||||
|
||||
.PHONY: virtualenv
|
||||
virtualenv:
|
||||
if [ ! -e $(PIP) ]; then \
|
||||
$(VIRTUALENV) --no-site-packages --distribute virtualenv ; \
|
||||
fi
|
||||
|
||||
################## /python app
|
||||
|
||||
################## Targets for supporting development work
|
||||
|
||||
databases: databases/fda_ndc.zip databases/drugbank.xml.zip
|
||||
|
||||
.PHONY: databases/fda_ndc.zip
|
||||
databases/fda_ndc.zip:
|
||||
rm -fr databases/fda_ndc*
|
||||
mkdir -p databases/fda_ndc
|
||||
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); \
|
||||
LINK=$$(wget -O - http://www.fda.gov/$${DBPAGE} --quiet | grep ">NDC Database File" | cut -d \" -f 2) ; \
|
||||
wget -O $@ http://www.fda.gov/$${LINK}
|
||||
cd databases/fda_ndc && unzip -e ../fda_ndc.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
|
||||
59
README.md
59
README.md
@@ -1,28 +1,31 @@
|
||||
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
|
||||
|
||||
The Idea
|
||||
========
|
||||
|
||||
* 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?"
|
||||
* Pharmacist: "Sure thing. We'll call you when it gets filled."
|
||||
|
||||
... Meanwhile, back at stately Charitable Person Manor ...
|
||||
|
||||
* Charitable Person's Phone: *DING*
|
||||
* 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
|
||||
* 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 ...
|
||||
|
||||
* Pharmacist : "Hello, Patient? I just wanted to let you know that your prescription is filled and has been paid in full."
|
||||
* 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.
|
||||
|
||||
(That's the idea anyway. WE'll see if reality follows.)
|
||||
mercy
|
||||
=====
|
||||
|
||||
This project comprises the entirety of The Mercy Project; everything needed to deploy and serve The Mercy Project currently lives in this repository.
|
||||
|
||||
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
|
||||
===================
|
||||
|
||||
The mercy/ directory contains all of the python code used to serve the Mercy website and perform all necessary functions.
|
||||
|
||||
Alembic ORM database upgrade scripts
|
||||
====================================
|
||||
|
||||
The alembic/ directory contains scripts used by the alembic flask extension to automatically upgrade and downgrade the postgres database used by Mercy.
|
||||
|
||||
Puppet Modules for Deployment
|
||||
==============================
|
||||
|
||||
The puppet code necessary to deploy Mercy onto Linux servers lives in the puppet/ directory.
|
||||
|
||||
Nagios Checks for Monitoring the Mercy Platform
|
||||
===============================================
|
||||
|
||||
These live in the nagios/ directory.
|
||||
|
||||
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.
|
||||
|
||||
[alembic]
|
||||
# path to migration scripts
|
||||
script_location = alembic
|
||||
|
||||
# template used to generate migration files
|
||||
# file_template = %%(rev)s_%%(slug)s
|
||||
|
||||
# set to 'true' to run the environment during
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# revision_environment = false
|
||||
|
||||
sqlalchemy.url = postgresql://mercy:mercy@postgresql.aklabs.net/mercy
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARN
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARN
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
||||
# A generic, single database configuration.
|
||||
|
||||
[alembic]
|
||||
# path to migration scripts
|
||||
script_location = alembic
|
||||
|
||||
# template used to generate migration files
|
||||
# file_template = %%(rev)s_%%(slug)s
|
||||
|
||||
# set to 'true' to run the environment during
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# revision_environment = false
|
||||
|
||||
sqlalchemy.url = postgresql://mercy:mercy@postgresql.aklabs.net/mercy
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARN
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARN
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
||||
|
||||
150
alembic/env.py
150
alembic/env.py
@@ -1,71 +1,79 @@
|
||||
from __future__ import with_statement
|
||||
from alembic import context
|
||||
from sqlalchemy import engine_from_config, pool
|
||||
from logging.config import fileConfig
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
fileConfig(config.config_file_name)
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
# from myapp import mymodel
|
||||
# target_metadata = mymodel.Base.metadata
|
||||
target_metadata = None
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
# my_important_option = config.get_main_option("my_important_option")
|
||||
# ... etc.
|
||||
|
||||
def run_migrations_offline():
|
||||
"""Run migrations in 'offline' mode.
|
||||
|
||||
This configures the context with just a URL
|
||||
and not an Engine, though an Engine is acceptable
|
||||
here as well. By skipping the Engine creation
|
||||
we don't even need a DBAPI to be available.
|
||||
|
||||
Calls to context.execute() here emit the given string to the
|
||||
script output.
|
||||
|
||||
"""
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(url=url)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
def run_migrations_online():
|
||||
"""Run migrations in 'online' mode.
|
||||
|
||||
In this scenario we need to create an Engine
|
||||
and associate a connection with the context.
|
||||
|
||||
"""
|
||||
engine = engine_from_config(
|
||||
config.get_section(config.config_ini_section),
|
||||
prefix='sqlalchemy.',
|
||||
poolclass=pool.NullPool)
|
||||
|
||||
connection = engine.connect()
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata
|
||||
)
|
||||
|
||||
try:
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
finally:
|
||||
connection.close()
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
run_migrations_online()
|
||||
|
||||
from __future__ import with_statement
|
||||
from alembic import context
|
||||
from sqlalchemy import engine_from_config, pool
|
||||
from logging.config import fileConfig
|
||||
import mercy.MercyApplication
|
||||
import mercy.models
|
||||
import mercy.config
|
||||
|
||||
db = mercy.MercyApplication.get_db()
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
fileConfig(config.config_file_name)
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
# from myapp import mymodel
|
||||
# target_metadata = mymodel.Base.metadata
|
||||
target_metadata = db.Model.metadata
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
# my_important_option = config.get_main_option("my_important_option")
|
||||
# ... etc.
|
||||
|
||||
def run_migrations_offline():
|
||||
"""Run migrations in 'offline' mode.
|
||||
|
||||
This configures the context with just a URL
|
||||
and not an Engine, though an Engine is acceptable
|
||||
here as well. By skipping the Engine creation
|
||||
we don't even need a DBAPI to be available.
|
||||
|
||||
Calls to context.execute() here emit the given string to the
|
||||
script output.
|
||||
|
||||
"""
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(url=url)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
def run_migrations_online():
|
||||
"""Run migrations in 'online' mode.
|
||||
|
||||
In this scenario we need to create an Engine
|
||||
and associate a connection with the context.
|
||||
|
||||
"""
|
||||
|
||||
alembic_config = config.get_section(config.config_ini_section)
|
||||
alembic_config['sqlalchemy.url'] = mercy.config.SQLALCHEMY_URI
|
||||
|
||||
engine = engine_from_config(
|
||||
config.get_section(config.config_ini_section),
|
||||
prefix='sqlalchemy.',
|
||||
poolclass=pool.NullPool)
|
||||
|
||||
connection = engine.connect()
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata
|
||||
)
|
||||
|
||||
try:
|
||||
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
|
||||
|
||||
class MercyApplication(flask.Flask):
|
||||
pass
|
||||
import flask
|
||||
import mercy.config
|
||||
from flask.ext.sqlalchemy import SQLAlchemy
|
||||
|
||||
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.
|
||||
@@ -1,32 +1,32 @@
|
||||
MAJOR:=$(shell bash -c 'source version.sh ; echo $$MAJOR')
|
||||
BUILD:=$(shell bash -c 'source version.sh ; echo $$BUILD')
|
||||
MODULEPATH:=$(shell puppet config print 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'")
|
||||
endif
|
||||
PUPPET_DEPS=$(find mercy -type f)
|
||||
PUPPET_DIST=mercy/pkg/akesterson-mercy-$(MAJOR).$(BUILD).tar.gz
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm pkg/*
|
||||
rm Modulefile
|
||||
|
||||
mercy/Modulefile: Modulefile.template.sh version.sh
|
||||
source version.sh && bash Modulefile.template.sh > Modulefile
|
||||
|
||||
$(PUPPET_DIST): $(PUPPET_DEPS) mercy/Modulefile
|
||||
rm -f $(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'
|
||||
tar --exclude=mercy/Modulefile --exclude=mercy/pkg -czvf $(PUPPET_DIST) mercy/
|
||||
|
||||
dist: $(PUPPET_DIST)
|
||||
|
||||
all: $(PUPPET_DIST)
|
||||
|
||||
install: $(PUPPET_DIST)
|
||||
tar -zxvf $(PUPPET_DIST) -C $(MODULEPATH)
|
||||
|
||||
uninstall:
|
||||
MAJOR:=$(shell bash -c 'source version.sh ; echo $$MAJOR')
|
||||
BUILD:=$(shell bash -c 'source version.sh ; echo $$BUILD')
|
||||
MODULEPATH:=$(shell puppet config print 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'")
|
||||
endif
|
||||
PUPPET_DEPS=$(find mercy -type f)
|
||||
PUPPET_DIST=mercy/pkg/akesterson-mercy-$(MAJOR).$(BUILD).tar.gz
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm pkg/*
|
||||
rm Modulefile
|
||||
|
||||
mercy/Modulefile: Modulefile.template.sh version.sh
|
||||
source version.sh && bash Modulefile.template.sh > Modulefile
|
||||
|
||||
$(PUPPET_DIST): $(PUPPET_DEPS) mercy/Modulefile
|
||||
rm -f $(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'
|
||||
tar --exclude=mercy/Modulefile --exclude=mercy/pkg -czvf $(PUPPET_DIST) mercy/
|
||||
|
||||
dist: $(PUPPET_DIST)
|
||||
|
||||
all: $(PUPPET_DIST)
|
||||
|
||||
install: $(PUPPET_DIST)
|
||||
tar -zxvf $(PUPPET_DIST) -C $(MODULEPATH)
|
||||
|
||||
uninstall:
|
||||
rm -fr $(MODULEPATH)/mercy
|
||||
@@ -1,12 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
cat <<EOF
|
||||
name 'akesterson-mercy'
|
||||
version '${MAJOR}-${BUILD}'
|
||||
dependency 'apache/apache'
|
||||
summary '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
|
||||
license 'MIT'
|
||||
author 'Andrew Kesterson <andrew@aklabs.net>'
|
||||
EOF
|
||||
#!/bin/bash
|
||||
|
||||
cat <<EOF
|
||||
name 'akesterson-mercy'
|
||||
version '${MAJOR}-${BUILD}'
|
||||
dependency 'apache/apache'
|
||||
summary '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
|
||||
license 'MIT'
|
||||
author 'Andrew Kesterson <andrew@aklabs.net>'
|
||||
EOF
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
mercy
|
||||
=====
|
||||
|
||||
A puppet module for installing the Mercy Flask application
|
||||
|
||||
Requirements
|
||||
============
|
||||
|
||||
This puppet module assumes that you have:
|
||||
|
||||
* 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 python 2.7 and pip on localhost
|
||||
* Working postgres with a username, password, and database for mercy to use
|
||||
|
||||
Aside from that, the module has no dependencies.
|
||||
|
||||
Usage
|
||||
=========
|
||||
|
||||
class { 'mercy':
|
||||
# ---- These are required, they have no defaults
|
||||
environment => 'dev|production',
|
||||
version => 'absent|latest|MAJOR-MINOR',
|
||||
ensure => 'running|stopped'.
|
||||
rabbitmq_uri => 'RABBITMQ_BROKER_URI',
|
||||
# ---- Everything below is optional
|
||||
process_user => 'mercy',
|
||||
process_group => 'mercy',
|
||||
process_threads => 5,
|
||||
servername => $::fqdn,
|
||||
rabbitmq_user => 'mercy',
|
||||
rabbitmq_pw => 'mercy',
|
||||
rabbitmq_vhost => 'mercy',
|
||||
vhost_dir => '/etc/apache/httpd/conf.d',
|
||||
apache_service => 'httpd',
|
||||
port => 443,
|
||||
postgres_uri => 'localhost',
|
||||
postgres_user => 'mercy',
|
||||
postgres_pw => '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.
|
||||
|
||||
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.
|
||||
mercy
|
||||
=====
|
||||
|
||||
A puppet module for installing the Mercy Flask application
|
||||
|
||||
Requirements
|
||||
============
|
||||
|
||||
This puppet module assumes that you have:
|
||||
|
||||
* 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 python 2.7 and pip on localhost
|
||||
* Working postgres with a username, password, and database for mercy to use
|
||||
|
||||
Aside from that, the module has no dependencies.
|
||||
|
||||
Usage
|
||||
=========
|
||||
|
||||
class { 'mercy':
|
||||
# ---- These are required, they have no defaults
|
||||
environment => 'dev|production',
|
||||
version => 'absent|latest|MAJOR-MINOR',
|
||||
ensure => 'running|stopped'.
|
||||
rabbitmq_uri => 'RABBITMQ_BROKER_URI',
|
||||
# ---- Everything below is optional
|
||||
process_user => 'mercy',
|
||||
process_group => 'mercy',
|
||||
process_threads => 5,
|
||||
servername => $::fqdn,
|
||||
rabbitmq_user => 'mercy',
|
||||
rabbitmq_pw => 'mercy',
|
||||
rabbitmq_vhost => 'mercy',
|
||||
vhost_dir => '/etc/apache/httpd/conf.d',
|
||||
apache_service => 'httpd',
|
||||
port => 443,
|
||||
postgres_uri => 'localhost',
|
||||
postgres_user => 'mercy',
|
||||
postgres_pw => '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.
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
class mercy(
|
||||
$environment,
|
||||
$version,
|
||||
$ensure,
|
||||
$rabbitmq_uri,
|
||||
$process_user => 'mercy',
|
||||
$process_group => 'mercy',
|
||||
$process_threads => 5,
|
||||
$servername => $::fqdn,
|
||||
$rabbitmq_user => 'mercy',
|
||||
$rabbitmq_pw => 'mercy',
|
||||
$rabbitmq_vhost => 'mercy',
|
||||
$vhost_dir => '/etc/apache/httpd/conf.d',
|
||||
$apache_service => 'httpd',
|
||||
$port => 443,
|
||||
$postgres_uri => 'localhost',
|
||||
$postgres_user => 'mercy',
|
||||
$postgres_pw => 'mercy',
|
||||
$postgres_db => 'mercy')
|
||||
{
|
||||
include 'mercy::params'
|
||||
}
|
||||
class mercy(
|
||||
$environment,
|
||||
$version,
|
||||
$ensure,
|
||||
$rabbitmq_uri,
|
||||
$process_user => 'mercy',
|
||||
$process_group => 'mercy',
|
||||
$process_threads => 5,
|
||||
$servername => $::fqdn,
|
||||
$rabbitmq_user => 'mercy',
|
||||
$rabbitmq_pw => 'mercy',
|
||||
$rabbitmq_vhost => 'mercy',
|
||||
$vhost_dir => '/etc/apache/httpd/conf.d',
|
||||
$apache_service => 'httpd',
|
||||
$port => 443,
|
||||
$postgres_uri => 'localhost',
|
||||
$postgres_user => 'mercy',
|
||||
$postgres_pw => 'mercy',
|
||||
$postgres_db => 'mercy')
|
||||
{
|
||||
include 'mercy::params'
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
class mercy::params
|
||||
{
|
||||
|
||||
}
|
||||
class mercy::params
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<VirtualHost * :<%= scope.lookupvar('::mercy::port') %>>
|
||||
ServerName <%= scope.lookupvar('::fqdn') %>
|
||||
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
|
||||
|
||||
<Directory /opt/mercy>
|
||||
WSGIProcessGroup mercy
|
||||
WSGIApplicationGroup %{GLOBAL}
|
||||
Order deny, allow
|
||||
Allow from all
|
||||
</Directory>
|
||||
<VirtualHost * :<%= scope.lookupvar('::mercy::port') %>>
|
||||
ServerName <%= scope.lookupvar('::fqdn') %>
|
||||
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
|
||||
|
||||
<Directory /opt/mercy>
|
||||
WSGIProcessGroup mercy
|
||||
WSGIApplicationGroup %{GLOBAL}
|
||||
Order deny, allow
|
||||
Allow from all
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
@@ -1,49 +1,49 @@
|
||||
# A generic, single database configuration.
|
||||
|
||||
[alembic]
|
||||
# path to migration scripts
|
||||
script_location = /opt/mercy/alembic
|
||||
|
||||
# template used to generate migration files
|
||||
# file_template = %%(rev)s_%%(slug)s
|
||||
|
||||
# set to 'true' to run the environment during
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# 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') %>
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARN
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARN
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
||||
# A generic, single database configuration.
|
||||
|
||||
[alembic]
|
||||
# path to migration scripts
|
||||
script_location = /opt/mercy/alembic
|
||||
|
||||
# template used to generate migration files
|
||||
# file_template = %%(rev)s_%%(slug)s
|
||||
|
||||
# set to 'true' to run the environment during
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# 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') %>
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARN
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARN
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)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
|
||||
|
||||
class ScriptNameStripper(object):
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def __call_(self, environ, start_response):
|
||||
environ['SCRIPT_NAME'] = ''
|
||||
return self.app(environ, start_response)
|
||||
|
||||
app = ScriptNameStripper(MercyApplication())
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
import mercy.MercyApplication
|
||||
|
||||
app = get_application()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from mercy.MercyApplication import MercyApplication
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = MercyApplication("mercy")
|
||||
app.run()
|
||||
#!/usr/bin/env python
|
||||
|
||||
from mercy.MercyApplication import app
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
|
||||
72
setup.py
72
setup.py
@@ -1,34 +1,38 @@
|
||||
from distutils.core import setup
|
||||
import mercy.version
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup(
|
||||
name="mercy",
|
||||
url="https://www.github.com/akesterson/mercy",
|
||||
version=mercy.version.VERSION,
|
||||
description="A flask application that facilitates paying for prescriptions",
|
||||
long_description="",
|
||||
author=("Andrew Kesterson"),
|
||||
author_email="andrew@aklabs.net",
|
||||
license="MIT",
|
||||
install_requires=["flask",
|
||||
"sqlalchemy",
|
||||
"alembic",
|
||||
"psycopg2"],
|
||||
scripts=[],
|
||||
packages=["mercy"],
|
||||
data_files=[],
|
||||
classifiers=[
|
||||
'Development Status :: 1 - Planning',
|
||||
'Environment :: Web Environment',
|
||||
'Framework :: Flask',
|
||||
'Intended Audience :: Healthcare Industry',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Natural Language :: English',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Topic :: Other/Nonlisted Topic',
|
||||
],
|
||||
)
|
||||
|
||||
from distutils.core import setup
|
||||
import mercy.version
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup(
|
||||
name="mercy",
|
||||
url="https://www.github.com/akesterson/mercy",
|
||||
version=mercy.version.VERSION,
|
||||
description="A web application that facilitates charitable prescription payments",
|
||||
long_description="",
|
||||
author=("Andrew Kesterson"),
|
||||
author_email="andrew@aklabs.net",
|
||||
license="MIT",
|
||||
install_requires=["flask",
|
||||
"sqlalchemy",
|
||||
"alembic",
|
||||
"psycopg2",
|
||||
"flask-sqlalchemy",
|
||||
"py-dom-xpath"],
|
||||
scripts=["scripts/mercy.wsgi"],
|
||||
packages=["mercy",
|
||||
"mercy/models",
|
||||
"mercy/importers"],
|
||||
data_files=[],
|
||||
classifiers=[
|
||||
'Development Status :: 1 - Planning',
|
||||
'Environment :: Web Environment',
|
||||
'Framework :: Flask',
|
||||
'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