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
|
virtualenv
|
||||||
|
version.sh
|
||||||
|
|||||||
9
Makefile
9
Makefile
@@ -1,13 +1,8 @@
|
|||||||
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
|
|
||||||
else
|
|
||||||
PIP=$(shell pwd)/virtualenv/bin/pip
|
|
||||||
VIRTUALENV_PKGS_DIR=$(shell pwd)/virtualenv/lib/site-packages
|
|
||||||
endif
|
|
||||||
VIRTUALENV=$(shell which virtualenv)
|
VIRTUALENV=$(shell which virtualenv)
|
||||||
PYTHON=$(shell which python)
|
PYTHON=$(shell which python)
|
||||||
|
|
||||||
|
|||||||
37
README.md
37
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
|
||||||
========
|
|
||||||
|
|
||||||
* Patient: "Here's my prescription, Mr Pharmacist"
|
Python Application
|
||||||
* 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 ...
|
The mercy/ directory contains all of the python code used to serve the Mercy website and perform all necessary functions.
|
||||||
|
|
||||||
* Charitable Person's Phone: *DING*
|
Alembic ORM database upgrade scripts
|
||||||
* 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 ...
|
The alembic/ directory contains scripts used by the alembic flask extension to automatically upgrade and downgrade the postgres database used by Mercy.
|
||||||
|
|
||||||
* Pharmacist : "Hello, Patient? I just wanted to let you know that your prescription is filled and has been paid in full."
|
Puppet Modules for Deployment
|
||||||
* 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.
|
The puppet code necessary to deploy Mercy onto Linux servers lives in the puppet/ directory.
|
||||||
|
|
||||||
(That's the idea anyway. WE'll see if reality follows.)
|
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.
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ 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
|
||||||
|
import mercy.models
|
||||||
|
import mercy.config
|
||||||
|
|
||||||
|
db = mercy.MercyApplication.get_db()
|
||||||
# this is the Alembic Config object, which provides
|
# this is the Alembic Config object, which provides
|
||||||
# access to the values within the .ini file in use.
|
# access to the values within the .ini file in use.
|
||||||
config = context.config
|
config = context.config
|
||||||
@@ -15,7 +19,7 @@ fileConfig(config.config_file_name)
|
|||||||
# for 'autogenerate' support
|
# for 'autogenerate' support
|
||||||
# from myapp import mymodel
|
# from myapp import mymodel
|
||||||
# target_metadata = mymodel.Base.metadata
|
# target_metadata = mymodel.Base.metadata
|
||||||
target_metadata = None
|
target_metadata = db.Model.metadata
|
||||||
|
|
||||||
# other values from the config, defined by the needs of env.py,
|
# other values from the config, defined by the needs of env.py,
|
||||||
# can be acquired:
|
# can be acquired:
|
||||||
@@ -47,6 +51,10 @@ def run_migrations_online():
|
|||||||
and associate a connection with the context.
|
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(
|
engine = engine_from_config(
|
||||||
config.get_section(config.config_ini_section),
|
config.get_section(config.config_ini_section),
|
||||||
prefix='sqlalchemy.',
|
prefix='sqlalchemy.',
|
||||||
|
|||||||
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
|
||||||
|
from flask.ext.sqlalchemy import SQLAlchemy
|
||||||
|
|
||||||
class MercyApplication(flask.Flask):
|
class MercyApplication(flask.Flask):
|
||||||
pass
|
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"
|
||||||
|
|||||||
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
|
|
||||||
|
|
||||||
def __call_(self, environ, start_response):
|
|
||||||
environ['SCRIPT_NAME'] = ''
|
|
||||||
return self.app(environ, start_response)
|
|
||||||
|
|
||||||
app = ScriptNameStripper(MercyApplication())
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run()
|
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()
|
||||||
|
|||||||
12
setup.py
12
setup.py
@@ -8,7 +8,7 @@ if __name__ == "__main__":
|
|||||||
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",
|
||||||
@@ -16,9 +16,13 @@ if __name__ == "__main__":
|
|||||||
install_requires=["flask",
|
install_requires=["flask",
|
||||||
"sqlalchemy",
|
"sqlalchemy",
|
||||||
"alembic",
|
"alembic",
|
||||||
"psycopg2"],
|
"psycopg2",
|
||||||
scripts=[],
|
"flask-sqlalchemy",
|
||||||
packages=["mercy"],
|
"py-dom-xpath"],
|
||||||
|
scripts=["scripts/mercy.wsgi"],
|
||||||
|
packages=["mercy",
|
||||||
|
"mercy/models",
|
||||||
|
"mercy/importers"],
|
||||||
data_files=[],
|
data_files=[],
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 1 - Planning',
|
'Development Status :: 1 - Planning',
|
||||||
|
|||||||
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