WIP: Adds sponsor model, mock sponsors list, and form for adding sponsors to a study.

This commit is contained in:
Aaron Louie 2020-08-12 19:15:16 -04:00
parent 86101375b2
commit be441a7c53
6 changed files with 3756 additions and 2 deletions

45
example_data.py Normal file
View File

@ -0,0 +1,45 @@
import csv
from pb import db, session
from pb.models import Sponsor
class ExampleDataLoader:
@staticmethod
def clean_db():
session.flush() # Clear out any transactions before deleting it all to avoid spurious errors.
for table in reversed(db.metadata.sorted_tables):
session.execute(table.delete())
session.commit()
session.flush()
def load_all(self):
self.load_sponsors()
# self.load_studies()
# self.load_investigators()
def load_sponsors(self):
# Load sponsors from csv
with open('./pb/static/csv/sponsors.csv') as csv_file:
data = csv.reader(csv_file, delimiter=',')
first_line = True
sponsors = []
for row in data:
# Skip first line, which will be the column headings
if first_line:
# row[0]: SPONSOR_ID
# row[1]: SP_NAME
# row[2]: SP_MAILING_ADDRESS
# row[3]: SP_TYPE
first_line = False
elif int(row[0] or -1) is not -1:
print('row', row)
new_sponsor = Sponsor(SPONSOR_ID=int(row[0]), SP_NAME=row[1], SP_MAILING_ADDRESS=row[2], SP_TYPE=row[3])
new_sponsor.SP_TYPE_GROUP_NAME = Sponsor.get_type_group_name(new_sponsor.SP_TYPE)
sponsors.append(new_sponsor)
session.add_all(sponsors)
session.commit()

View File

@ -0,0 +1,49 @@
"""empty message
Revision ID: ffba4886d280
Revises: d3592c4e8a39
Create Date: 2020-08-12 14:06:05.072787
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'ffba4886d280'
down_revision = 'd3592c4e8a39'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('sponsor',
sa.Column('SPONSOR_ID', sa.Integer(), nullable=False),
sa.Column('SP_NAME', sa.String(), nullable=True),
sa.Column('SP_MAILING_ADDRESS', sa.String(), nullable=True),
sa.Column('SP_PHONE', sa.String(), nullable=True),
sa.Column('SP_FAX', sa.String(), nullable=True),
sa.Column('SP_EMAIL', sa.String(), nullable=True),
sa.Column('SP_HOMEPAGE', sa.String(), nullable=True),
sa.Column('COMMONRULEAGENCY', sa.Boolean(), nullable=True),
sa.Column('SP_TYPE', sa.String(), nullable=True),
sa.Column('SP_TYPE_GROUP_NAME', sa.String(), nullable=True),
sa.PrimaryKeyConstraint('SPONSOR_ID')
)
op.create_table('study_sponsor',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('SS_STUDY', sa.Integer(), nullable=True),
sa.Column('SPONSOR_ID', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['SPONSOR_ID'], ['sponsor.SPONSOR_ID'], ),
sa.ForeignKeyConstraint(['SS_STUDY'], ['study.STUDYID'], ),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('study_sponsor')
op.drop_table('sponsor')
# ### end Alembic commands ###

View File

@ -59,6 +59,11 @@ origins_re = [r"^https?:\/\/%s(.*)" % o.replace('.', '\.') for o in app.config['
cors = CORS(connexion_app.app, origins=origins_re)
db = SQLAlchemy(app)
""":type: sqlalchemy.orm.SQLAlchemy"""
session = db.session
""":type: sqlalchemy.orm.Session"""
migrate = Migrate(app, db)
ma = Marshmallow(app)
@ -115,6 +120,21 @@ def has_no_empty_params(rule):
return len(defaults) >= len(arguments)
@app.cli.command()
def load_example_data():
"""Load example data into the database."""
from example_data import ExampleDataLoader
ExampleDataLoader().clean_db()
ExampleDataLoader().load_all()
@app.cli.command()
def load_example_sponsors():
"""Load example data into the database."""
from example_data import ExampleDataLoader
ExampleDataLoader().load_sponsors()
@app.route('/site_map')
def site_map():
links = []
@ -130,9 +150,9 @@ def site_map():
# **************************
# WEB FORMS
# **************************
from pb.forms import StudyForm, StudyTable, InvestigatorForm, StudyDetailsForm, ConfirmDeleteForm
from pb.forms import StudyForm, StudyTable, InvestigatorForm, StudyDetailsForm, ConfirmDeleteForm, StudySponsorForm
from pb.models import Study, RequiredDocument, Investigator, StudySchema, RequiredDocumentSchema, InvestigatorSchema, \
StudyDetails, StudyDetailsSchema
StudyDetails, StudyDetailsSchema, StudySponsor, Sponsor
@app.route('/', methods=['GET', 'POST'])
@ -256,6 +276,69 @@ def del_investigator(inv_id):
return redirect_home()
@app.route('/study_sponsor/<study_id>', methods=['GET', 'POST'])
def new_study_sponsor(study_id):
form = StudySponsorForm(request.form)
action = BASE_HREF + "/study_sponsor/" + study_id
title = "Add StudySponsor to Study " + study_id
form.SPONSOR_IDS.choices = [(s.SPONSOR_ID, s.SP_NAME) for s in db.session.query(Sponsor).all()]
if request.method == 'POST':
study_sponsor = StudySponsor(STUDYID=study_id)
study_sponsor.NETBADGEID = form.NETBADGEID.data
study_sponsor.set_type(form.INVESTIGATORTYPE.data)
db.session.add(study_sponsor)
db.session.commit()
flash('StudySponsor created successfully!', 'success')
return redirect_home()
return render_template(
'form.html',
form=form,
action=action,
title=title,
description_map={},
base_href=BASE_HREF
)
@app.route('/del_study_sponsor/<study_sponsor_id>', methods=['GET', 'POST'])
def del_study_sponsor(study_sponsor_id):
study_sponsor_id = int(study_sponsor_id)
study_sponsor_model = db.session.query(StudySponsor).filter(StudySponsor.id == study_sponsor_id).first()
if study_sponsor_model is None:
flash('StudySponsor not found.', 'warn')
return redirect_home()
uid = study_sponsor_model.NETBADGEID
study_id = int(study_sponsor_model.STUDYID)
form = ConfirmDeleteForm(request.form, obj=study_sponsor_model)
if request.method == 'GET':
action = BASE_HREF + "/del_study_sponsor/%i" % study_sponsor_id
title = "Delete StudySponsor #%i?" % study_sponsor_id
details = "Are you sure you want to delete StudySponsor '%s' from Study %i?" % (uid, study_id)
return render_template(
'form.html',
form=form,
action=action,
title=title,
details=details,
description_map=description_map,
base_href=BASE_HREF
)
if request.method == 'POST':
if form.confirm and form.confirm.data:
db.session.query(StudySponsor).filter(StudySponsor.id == study_sponsor_id).delete()
db.session.commit()
flash('StudySponsor %s deleted from Study %i.' % (uid, study_id), 'success')
else:
flash('Delete canceled.', 'info')
return redirect_home()
@app.route('/del_study/<study_id>', methods=['GET', 'POST'])
def del_study(study_id):
study_id = int(study_id)

View File

@ -23,6 +23,11 @@ class InvestigatorForm(FlaskForm):
INVESTIGATORTYPE = SelectField("InvestigatorType", choices=[(i.INVESTIGATORTYPE, i.INVESTIGATORTYPEFULL) for i in Investigator.all_types()])
class StudySponsorForm(FlaskForm):
STUDY_ID = HiddenField()
SPONSOR_IDS = SelectMultipleField("Sponsor", coerce=int, validators=[validators.DataRequired()])
class StudyDetailsForm(ModelForm, FlaskForm):
class Meta:
model = StudyDetails
@ -46,6 +51,15 @@ class InvestigatorsTable(Table):
th_html_attrs={'class': 'mat-icon text-center', 'title': 'Delete Investigator'}
)
class SponsorsTable(Table):
SPONSOR_ID = Col('Sponsor Id')
SP_TYPE = Col('Type')
delete = LinkCol(
'delete', 'del_sponsor', url_kwargs=dict(inv_id='id'),
anchor_attrs={'class': 'btn btn-icon btn-warn', 'title': 'Delete Sponsor'},
th_html_attrs={'class': 'mat-icon text-center', 'title': 'Delete Sponsor'}
)
class StudyTable(Table):
def sort_url(self, col_id, reverse=False):
@ -65,6 +79,11 @@ class StudyTable(Table):
anchor_attrs={'class': 'btn btn-icon btn-accent', 'title': 'Add Investigator'},
th_html_attrs={'class': 'mat-icon text-center', 'title': 'Add Investigator'}
)
add_sponsor = LinkCol(
'add_business', 'new_study_sponsor', url_kwargs=dict(study_id='STUDYID'),
anchor_attrs={'class': 'btn btn-icon btn-accent', 'title': 'Add Sponsor'},
th_html_attrs={'class': 'mat-icon text-center', 'title': 'Add Sponsor'}
)
STUDYID = Col('Study Id')
TITLE = Col('Title')
NETBADGEID = Col('User')
@ -72,6 +91,7 @@ class StudyTable(Table):
Q_COMPLETE = BoolCol('Complete?')
requirements = NestedTableCol('Requirements', RequirementsTable)
investigators = NestedTableCol('Investigators', InvestigatorsTable)
sponsors = NestedTableCol('Sponsors', SponsorsTable)
delete = LinkCol(
'delete', 'del_study', url_kwargs=dict(study_id='STUDYID'),
anchor_attrs={'class': 'btn btn-icon btn-warn', 'title': 'Delete Study'},

View File

@ -1,3 +1,4 @@
from marshmallow import fields
from sqlalchemy import func
from pb import db, ma
@ -11,6 +12,7 @@ class Study(db.Model):
DATE_MODIFIED = db.Column(db.DateTime(timezone=True), default=func.now())
requirements = db.relationship("RequiredDocument", backref="study", lazy='dynamic')
investigators = db.relationship("Investigator", backref="study", lazy='dynamic')
sponsors = db.relationship("StudySponsor", backref="study", lazy='dynamic')
study_details = db.relationship("StudyDetails", uselist=False, backref="study")
@ -178,3 +180,61 @@ class StudyDetailsSchema(ma.SQLAlchemyAutoSchema):
model = StudyDetails
load_instance = True
include_relationships = False
class Sponsor(db.Model):
SPONSOR_ID = db.Column(db.Integer, primary_key=True)
SP_NAME = db.Column(db.String, nullable=True)
SP_MAILING_ADDRESS = db.Column(db.String, nullable=True)
SP_PHONE = db.Column(db.String, nullable=True)
SP_FAX = db.Column(db.String, nullable=True)
SP_EMAIL = db.Column(db.String, nullable=True)
SP_HOMEPAGE = db.Column(db.String, nullable=True)
COMMONRULEAGENCY = db.Column(db.Boolean, nullable=True)
SP_TYPE = db.Column(db.String, nullable=True)
SP_TYPE_GROUP_NAME = db.Column(db.String, nullable=True)
@staticmethod
def all_types():
types = [
Sponsor(SP_TYPE="Federal", SP_TYPE_GROUP_NAME="Government"),
Sponsor(SP_TYPE="Foundation/Not for Profit", SP_TYPE_GROUP_NAME="Other External Funding"),
Sponsor(SP_TYPE="Incoming Sub Award", SP_TYPE_GROUP_NAME="Government"),
Sponsor(SP_TYPE="Industry", SP_TYPE_GROUP_NAME="Industry"),
Sponsor(SP_TYPE="Internal/Departmental/Gift", SP_TYPE_GROUP_NAME="Internal Funding"),
Sponsor(SP_TYPE="No Funding", SP_TYPE_GROUP_NAME="Internal Funding"),
Sponsor(SP_TYPE="Other Colleges and Universities", SP_TYPE_GROUP_NAME="Other External Funding"),
Sponsor(SP_TYPE="State", SP_TYPE_GROUP_NAME="Government"),
]
return types
@staticmethod
def get_type_group_name(self, type_code):
for t in self.all_types():
if t.SP_TYPE == type_code:
return t.SP_TYPE_GROUP_NAME
class SponsorSchema(ma.Schema):
class Meta:
fields = ("SPONSOR_ID", "SP_NAME", "SP_MAILING_ADDRESS",
"SP_PHONE", "SP_FAX", "SP_EMAIL", "SP_HOMEPAGE",
"COMMONRULEAGENCY", "SP_TYPE")
class StudySponsor(db.Model):
id = db.Column(db.Integer, primary_key=True)
SS_STUDY = db.Column(db.Integer, db.ForeignKey('study.STUDYID'))
SPONSOR_ID = db.Column(db.Integer, db.ForeignKey('sponsor.SPONSOR_ID'))
study = db.relationship("Study")
sponsor = db.relationship("Sponsor")
class StudySponsorSchema(ma.Schema):
class Meta:
fields = ("SS_STUDY", "SPONSOR_ID", "SP_NAME", "SP_TYPE", "SP_TYPE_GROUP_NAME", "COMMONRULEAGENCY")
SP_TYPE = fields.Function(lambda obj: obj.sponsor.SP_TYPE)
SP_NAME = fields.Function(lambda obj: obj.sponsor.SP_NAME)
SP_TYPE_GROUP_NAME = fields.Function(lambda obj: obj.sponsor.SP_TYPE_GROUP_NAME)
COMMONRULEAGENCY = fields.Function(lambda obj: obj.sponsor.COMMONRULEAGENCY)

3497
pb/static/csv/sponsors.csv Normal file

File diff suppressed because it is too large Load Diff