Adds/edits sponsors for a given study.

This commit is contained in:
Aaron Louie 2020-08-13 15:19:55 -04:00
parent 18f557549f
commit 0e4cd7d130
5 changed files with 185 additions and 100 deletions

View File

@ -12,12 +12,14 @@ class ExampleDataLoader:
session.commit()
session.flush()
def load_all(self):
self.load_sponsors()
@staticmethod
def load_all():
ExampleDataLoader().load_sponsors()
# self.load_studies()
# self.load_investigators()
def load_sponsors(self):
@staticmethod
def load_sponsors():
# Load sponsors from csv
with open('./pb/static/csv/sponsors.csv') as csv_file:
data = csv.reader(csv_file, delimiter=',')
@ -31,8 +33,7 @@ class ExampleDataLoader:
# row[2]: SP_MAILING_ADDRESS
# row[3]: SP_TYPE
first_line = False
elif int(row[0] or -1) is not -1:
print('row', row)
elif int(row[0] or -1) != -1:
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)

View File

@ -277,18 +277,29 @@ def del_investigator(inv_id):
@app.route('/study_sponsor/<study_id>', methods=['GET', 'POST'])
def new_study_sponsor(study_id):
def edit_study_sponsor(study_id):
study = db.session.query(Study).filter(Study.STUDYID == study_id).first()
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()]
title = "Edit sponsors for Study " + study_id
form.SPONSOR_IDS.choices = [(s.SPONSOR_ID, f'{s.SP_NAME} ({s.SP_TYPE})') for s in db.session.query(Sponsor).all()]
if request.method == 'GET':
if hasattr(study, 'sponsors'):
form.SPONSOR_IDS.data = [s.SPONSOR_ID for s in study.sponsors]
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')
# Remove all existing sponsors
session.query(StudySponsor).filter(StudySponsor.SS_STUDY == study_id).delete()
# Add the new ones
for sponsor_id in form.SPONSOR_IDS.data:
study_sponsor = StudySponsor(SS_STUDY=study_id, SPONSOR_ID=sponsor_id)
db.session.add(study_sponsor)
db.session.commit()
sponsor_label = 'sponsor' if len(form.SPONSOR_IDS.data) == 1 else 'sponsors'
flash(f'Study {sponsor_label} edited successfully!', 'success')
return redirect_home()
return render_template(
@ -304,19 +315,23 @@ def new_study_sponsor(study_id):
@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()
study_sponsor_model: StudySponsor = 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)
sponsor_span = f'<span class="highlight">{study_sponsor_model.sponsor.SP_NAME} ' \
f'({study_sponsor_model.sponsor.SP_TYPE})</span>'
study_id = int(study_sponsor_model.SS_STUDY)
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)
action = f'{BASE_HREF}/del_study_sponsor/{study_sponsor_id}'
title = 'Remove study sponsor?'
details = f'Are you sure you want to remove {sponsor_span} ' \
f'as a sponsor of Study {study_id}? ' \
f'This will not remove the sponsor itself from the system.'
return render_template(
'form.html',
@ -332,7 +347,7 @@ def del_study_sponsor(study_sponsor_id):
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')
flash(f'Sponsor {sponsor_span} removed from Study {study_id}.', 'success')
else:
flash('Delete canceled.', 'info')
@ -350,9 +365,9 @@ def del_study(study_id):
form = ConfirmDeleteForm(request.form, obj=study_model)
if request.method == 'GET':
action = BASE_HREF + "/del_study/%i" % study_id
title = "Delete Study #%i?" % study_id
details = "Are you sure you want to delete Study '%s'?" % study_model.TITLE
action = f'{BASE_HREF}/del_study/{study_id}'
title = f'Delete Study #{study_id}?'
details = f'Are you sure you want to delete Study <span class="highlight">{study_model.TITLE}</span>?'
return render_template(
'form.html',
@ -369,7 +384,9 @@ def del_study(study_id):
db.session.query(RequiredDocument).filter(RequiredDocument.STUDYID == study_id).delete()
db.session.query(Investigator).filter(Investigator.STUDYID == study_id).delete()
db.session.query(StudyDetails).filter(StudyDetails.STUDYID == study_id).delete()
db.session.query(Study).filter(Study.STUDYID == study_id).delete()
db.session.query(StudySponsor).filter(StudySponsor.SS_STUDY == study_id).delete()
study = db.session.query(Study).filter(Study.STUDYID == study_id).first()
session.delete(study)
db.session.commit()
flash('Study %i deleted.' % study_id, 'success')
else:

View File

@ -25,7 +25,12 @@ class InvestigatorForm(FlaskForm):
class StudySponsorForm(FlaskForm):
STUDY_ID = HiddenField()
SPONSOR_IDS = SelectMultipleField("Sponsor", coerce=int, validators=[validators.DataRequired()])
SPONSOR_IDS = SelectMultipleField(
"Sponsor",
coerce=int,
render_kw={'class': 'multi'},
validators=[validators.DataRequired()]
)
class StudyDetailsForm(ModelForm, FlaskForm):
@ -37,6 +42,7 @@ class ConfirmDeleteForm(FlaskForm):
confirm = BooleanField('Yes, really delete', default='checked',
false_values=(False, 'false', 0, '0'))
class RequirementsTable(Table):
AUXDOCID = Col('Code')
AUXDOC = Col('Name')
@ -51,11 +57,16 @@ class InvestigatorsTable(Table):
th_html_attrs={'class': 'mat-icon text-center', 'title': 'Delete Investigator'}
)
class SponsorCol(Col):
def td_format(self, content):
return f'{content.SP_NAME} ({content.SP_TYPE})'
class SponsorsTable(Table):
SPONSOR_ID = Col('Sponsor Id')
SP_TYPE = Col('Type')
sponsor = SponsorCol('Sponsor')
delete = LinkCol(
'delete', 'del_sponsor', url_kwargs=dict(inv_id='id'),
'delete', 'del_study_sponsor', url_kwargs=dict(study_sponsor_id='id'),
anchor_attrs={'class': 'btn btn-icon btn-warn', 'title': 'Delete Sponsor'},
th_html_attrs={'class': 'mat-icon text-center', 'title': 'Delete Sponsor'}
)
@ -80,9 +91,9 @@ class StudyTable(Table):
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'}
'account_balance', 'edit_study_sponsor', url_kwargs=dict(study_id='STUDYID'),
anchor_attrs={'class': 'btn btn-icon btn-accent', 'title': 'Edit Sponsor(s)'},
th_html_attrs={'class': 'mat-icon text-center', 'title': 'Edit Sponsor(s)'}
)
STUDYID = Col('Study Id')
TITLE = Col('Title')

View File

@ -3,6 +3,64 @@ from sqlalchemy import func
from pb import db, ma
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(type_code):
for t in Sponsor.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", back_populates="sponsors")
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)
class Study(db.Model):
STUDYID = db.Column(db.Integer, primary_key=True)
HSRNUMBER = db.Column(db.String())
@ -12,8 +70,8 @@ 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")
sponsors = db.relationship("StudySponsor", back_populates="study", cascade="all, delete, delete-orphan")
class StudySchema(ma.Schema):
@ -180,61 +238,3 @@ 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)

View File

@ -1,14 +1,16 @@
import os
from werkzeug.datastructures import MultiDict
os.environ["TESTING"] = "true"
import unittest
import random
import string
from pb import app, db
from pb.forms import StudyForm
from pb.models import Study, RequiredDocument
from pb import app, db, session
from pb.forms import StudyForm, StudySponsorForm
from pb.models import Study, RequiredDocument, Sponsor, StudySponsor
from example_data import ExampleDataLoader
class Sanity_Check_Test(unittest.TestCase):
auths = {}
@ -21,19 +23,26 @@ class Sanity_Check_Test(unittest.TestCase):
@classmethod
def tearDownClass(cls):
db.drop_all()
db.session.remove()
pass
def setUp(self):
ExampleDataLoader().clean_db()
self.ctx.push()
def tearDown(self):
ExampleDataLoader().clean_db()
self.ctx.pop()
self.auths = {}
def test_add_and_edit_study(self):
"""Add and edit a study"""
def load_sponsors(self):
ExampleDataLoader().load_sponsors()
sponsors = session.query(Sponsor).all()
num_lines = sum(1 for line in open('pb/static/csv/sponsors.csv'))
self.assertIsNotNone(sponsors)
self.assertEqual(len(sponsors), num_lines - 1)
return sponsors
def add_study(self):
study_title = "My Test Document" + ''.join(random.choices(string.digits, k=8))
study = Study(TITLE=study_title, NETBADGEID="dhf8r")
form = StudyForm(formdata=None, obj=study)
@ -47,11 +56,18 @@ class Sanity_Check_Test(unittest.TestCase):
assert r.status_code == 302
added_study = Study.query.filter(Study.TITLE == study_title).first()
assert added_study
num_studies_before = Study.query.count()
num_docs_before = RequiredDocument.query.filter(Study.STUDYID == added_study.STUDYID).count()
self.assertEqual(num_reqs, num_docs_before)
return added_study
def test_add_and_edit_study(self):
"""Add and edit a study"""
added_study: Study = self.add_study()
num_studies_before = Study.query.count()
num_docs_before = RequiredDocument.query.filter(Study.STUDYID == added_study.STUDYID).count()
"""Edit an existing study"""
added_study.title = "New Title" + ''.join(random.choices(string.digits, k=8))
form_2 = StudyForm(formdata=None, obj=added_study)
@ -68,3 +84,43 @@ class Sanity_Check_Test(unittest.TestCase):
num_docs_after = RequiredDocument.query.filter(Study.STUDYID == edited_study.STUDYID).count()
self.assertEqual(num_docs_before, num_docs_after)
self.assertEqual(num_studies_before, num_studies_after)
def test_add_and_edit_study_sponsor(self):
"""Add and edit a study sponsor"""
num_sponsors = 5
all_sponsors = self.load_sponsors()
study: Study = self.add_study()
self.assertIsNotNone(study)
num_study_sponsors_before = len(study.sponsors)
self.assertEqual(num_study_sponsors_before, 0)
study_sponsors_before = session.query(StudySponsor).filter(StudySponsor.SS_STUDY == study.STUDYID).all()
self.assertEqual(len(study_sponsors_before), 0)
"""Add sponsors to an existing study"""
random_sponsor_ids = random.choices([s.SPONSOR_ID for s in all_sponsors], k=num_sponsors)
self.assertEqual(len(random_sponsor_ids), num_sponsors)
formdata = MultiDict()
formdata.add('SS_STUDY', study.STUDYID)
for sponsor_id in random_sponsor_ids:
formdata.add('SPONSOR_IDS', sponsor_id)
form = StudySponsorForm(formdata=formdata, obj=study)
form.SPONSOR_IDS.choices = [(s.SPONSOR_ID, f'{s.SP_NAME} ({s.SP_TYPE})') for s in all_sponsors]
self.assertEqual(len(form.data['SPONSOR_IDS']), num_sponsors)
rv = self.app.post(f'/study_sponsor/{study.STUDYID}', data=form.data, follow_redirects=False)
assert rv.status_code == 302
edited_study = Study.query.filter(Study.STUDYID == study.STUDYID).first()
assert edited_study
num_study_sponsors_after = len(edited_study.sponsors)
self.assertGreater(num_study_sponsors_after, 0)
self.assertEqual(num_study_sponsors_after, num_sponsors)
study_sponsors_after = session.query(StudySponsor).filter(StudySponsor.SS_STUDY == study.STUDYID).all()
self.assertGreater(len(study_sponsors_after), 0)
self.assertEqual(len(study_sponsors_after), num_sponsors)