Merge pull request #46 from sartography/update-study-details-fields-257
Update study details fields 257
This commit is contained in:
commit
60da05df4e
|
@ -0,0 +1,42 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 6cd52db404f6
|
||||
Revises: 9493f29b0898
|
||||
Create Date: 2021-03-24 10:04:28.215143
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '6cd52db404f6'
|
||||
down_revision = '9493f29b0898'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('study_details', sa.Column('IS_SPONSOR_TRACKING', sa.Integer(), nullable=True))
|
||||
op.add_column('study_details', sa.Column('SPONSOR_TRACKING', sa.Integer(), nullable=True))
|
||||
op.add_column('study_details', sa.Column('IS_DSMB', sa.Integer(), nullable=True))
|
||||
op.add_column('study_details', sa.Column('IS_COMPLETE_NON_IRB_REGULATORY', sa.Integer(), nullable=True))
|
||||
op.add_column('study_details', sa.Column('IS_INSIDE_CONTRACT', sa.Integer(), nullable=True))
|
||||
op.add_column('study_details', sa.Column('IS_CODED_RESEARCH', sa.Integer(), nullable=True))
|
||||
op.add_column('study_details', sa.Column('IS_OUTSIDE_SPONSOR', sa.Integer(), nullable=True))
|
||||
op.add_column('study_details', sa.Column('IS_UVA_COLLABANALYSIS', sa.Integer(), nullable=True))
|
||||
op.drop_column('study_details', 'SPONSORS_PROTOCOL_REVISION_DATE')
|
||||
op.add_column('study_details', sa.Column('SPONSORS_PROTOCOL_REVISION_DATE', sa.Date(), nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('study_details', 'IS_SPONSOR_TRACKING')
|
||||
op.drop_column('study_details', 'SPONSOR_TRACKING')
|
||||
op.drop_column('study_details', 'IS_DSMB')
|
||||
op.drop_column('study_details', 'IS_COMPLETE_NON_IRB_REGULATORY')
|
||||
op.drop_column('study_details', 'IS_INSIDE_CONTRACT')
|
||||
op.drop_column('study_details', 'IS_CODED_RESEARCH')
|
||||
op.drop_column('study_details', 'IS_OUTSIDE_SPONSOR')
|
||||
op.drop_column('study_details', 'IS_UVA_COLLABANALYSIS')
|
||||
op.drop_column('study_details', 'SPONSORS_PROTOCOL_REVISION_DATE')
|
||||
op.add_column('study_details', sa.Column('SPONSORS_PROTOCOL_REVISION_DATE', sa.Integer(), nullable=True))
|
|
@ -0,0 +1,30 @@
|
|||
"""empty message
|
||||
|
||||
Revision ID: 93a1e2ce38dc
|
||||
Revises: 6cd52db404f6
|
||||
Create Date: 2021-03-29 15:06:32.100287
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '93a1e2ce38dc'
|
||||
down_revision = '6cd52db404f6'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.drop_column('study_details', 'DSMB')
|
||||
op.add_column('study_details', sa.Column('DSMB', sa.VARCHAR, nullable=True))
|
||||
op.add_column('study_details', sa.Column('REVIEW_TYPE', sa.Integer, nullable=True))
|
||||
op.add_column('study_details', sa.Column('REVIEWTYPENAME', sa.VARCHAR, nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_column('study_details', 'DSMB')
|
||||
op.add_column('study_details', sa.Column('DSMB', sa.Integer, nullable=True))
|
||||
op.drop_column('study_details', 'REVIEW_TYPE')
|
||||
op.drop_column('study_details', 'REVIEWTYPENAME')
|
|
@ -1,8 +1,8 @@
|
|||
import csv
|
||||
import datetime
|
||||
import os
|
||||
import re
|
||||
import yaml
|
||||
from datetime import date
|
||||
from io import TextIOWrapper
|
||||
|
||||
import connexion
|
||||
from flask_cors import CORS
|
||||
|
@ -453,26 +453,78 @@ def _update_study(study, form):
|
|||
db.session.commit()
|
||||
|
||||
|
||||
def _allowed_file(filename):
|
||||
allowed_extensions = ['csv', 'xls', 'xlsx']
|
||||
return '.' in filename and \
|
||||
filename.rsplit('.', 1)[1].lower() in allowed_extensions
|
||||
|
||||
|
||||
def process_csv_study_details(study_id, csv_file):
|
||||
study_details = db.session.query(StudyDetails).filter(StudyDetails.STUDYID == study_id).first()
|
||||
csv_file.seek(0)
|
||||
data = csv.DictReader(TextIOWrapper(csv_file))
|
||||
for row in data:
|
||||
if data.line_num == 2:
|
||||
|
||||
for key in row.keys():
|
||||
if key != 'STUDYID':
|
||||
if hasattr(study_details, key):
|
||||
if key in row.keys():
|
||||
val = row[key]
|
||||
print(key, val)
|
||||
if key == 'SPONSORS_PROTOCOL_REVISION_DATE':
|
||||
print(type(val))
|
||||
if val == '':
|
||||
val = None
|
||||
if val == 'null':
|
||||
val = None
|
||||
setattr(study_details, key, val)
|
||||
|
||||
session.add(study_details)
|
||||
session.commit()
|
||||
|
||||
|
||||
@app.route('/study_details/<study_id>', methods=['GET', 'POST'])
|
||||
def study_details(study_id):
|
||||
|
||||
study_details = db.session.query(StudyDetails).filter(StudyDetails.STUDYID == study_id).first()
|
||||
if not study_details:
|
||||
study_details = StudyDetails(STUDYID=study_id)
|
||||
form = StudyDetailsForm(request.form, obj=study_details)
|
||||
if request.method == 'GET':
|
||||
action = BASE_HREF + "/study_details/" + study_id
|
||||
title = "Edit Study Details for Study #" + study_id
|
||||
details = "Numeric fields can be 1 for true, 0 or false, or Null if not applicable."
|
||||
|
||||
action = BASE_HREF + "/study_details/" + study_id
|
||||
title = "Edit Study Details for Study #" + study_id
|
||||
details = "Numeric fields can be 1 for true, 0 or false, or Null if not applicable."
|
||||
|
||||
if request.method == 'POST':
|
||||
form.populate_obj(study_details)
|
||||
db.session.add(study_details)
|
||||
db.session.commit()
|
||||
flash('Study updated successfully!', 'success')
|
||||
return redirect_home()
|
||||
# update study details with csv file
|
||||
if 'file' in request.files:
|
||||
file = request.files['file']
|
||||
if file.filename == '':
|
||||
flash('No file selected', 'failure')
|
||||
return redirect(action)
|
||||
elif file and _allowed_file(file.filename):
|
||||
process_csv_study_details(study_id, file)
|
||||
flash('CSV file uploaded', 'success')
|
||||
return redirect_home()
|
||||
else:
|
||||
flash('There was a problem processing your file', 'failure')
|
||||
return redirect(action)
|
||||
|
||||
# update study details from the form
|
||||
elif form.validate():
|
||||
form.populate_obj(study_details)
|
||||
db.session.add(study_details)
|
||||
db.session.commit()
|
||||
flash('Study updated successfully!', 'success')
|
||||
return redirect_home()
|
||||
|
||||
# display the study details form
|
||||
return render_template(
|
||||
'form.html',
|
||||
form=form,
|
||||
action=action,
|
||||
csv_action=action,
|
||||
title=title,
|
||||
details=details,
|
||||
description_map=description_map,
|
||||
|
|
24
pb/forms.py
24
pb/forms.py
|
@ -10,7 +10,7 @@ class StudyForm(FlaskForm):
|
|||
STUDYID = HiddenField()
|
||||
TITLE = StringField('Title', [validators.DataRequired()])
|
||||
NETBADGEID = StringField('User UVA Computing Id', [validators.DataRequired()])
|
||||
requirements = SelectMultipleField("Requirements",
|
||||
requirements = SelectMultipleField("Documents",
|
||||
render_kw={'class': 'multi'},
|
||||
choices=[(rd.AUXDOCID, rd.AUXDOC) for rd in RequiredDocument.all()])
|
||||
HSRNUMBER = StringField('HSR Number')
|
||||
|
@ -36,6 +36,26 @@ class StudySponsorForm(FlaskForm):
|
|||
class StudyDetailsForm(ModelForm, FlaskForm):
|
||||
class Meta:
|
||||
model = StudyDetails
|
||||
only = ['IS_IND', 'IND_1', 'IND_2', 'IND_3', 'IS_UVA_IND', 'IS_IDE', 'IDE',
|
||||
'IS_UVA_IDE', 'IS_CHART_REVIEW', 'IS_RADIATION', 'GCRC_NUMBER', 'IS_GCRC',
|
||||
'IS_PRC_DSMP', 'IS_PRC', 'PRC_NUMBER', 'IS_IBC', 'IBC_NUMBER',
|
||||
'IS_SPONSOR_TRACKING', 'SPONSOR_TRACKING', 'SPONSORS_PROTOCOL_REVISION_DATE',
|
||||
'IS_SPONSOR_MONITORING', 'IS_DSMB', 'IS_COMPLETE_NON_IRB_REGULATORY',
|
||||
'IS_AUX', 'IS_SPONSOR', 'IS_GRANT', 'IS_COMMITTEE_CONFLICT', 'DSMB',
|
||||
'DSMB_FREQUENCY', 'IS_DB', 'IS_UVA_DB', 'IS_CENTRAL_REG_DB',
|
||||
'IS_CONSENT_WAIVER', 'IS_HGT', 'IS_GENE_TRANSFER', 'IS_TISSUE_BANKING',
|
||||
'IS_SURROGATE_CONSENT', 'IS_ADULT_PARTICIPANT', 'IS_MINOR_PARTICIPANT',
|
||||
'IS_MINOR', 'IS_BIOMEDICAL', 'IS_QUALITATIVE', 'IS_PI_SCHOOL',
|
||||
'IS_PRISONERS_POP', 'IS_PREGNANT_POP', 'IS_FETUS_POP',
|
||||
'IS_MENTAL_IMPAIRMENT_POP', 'IS_ELDERLY_POP', 'IS_OTHER_VULNERABLE_POP',
|
||||
'OTHER_VULNERABLE_DESC', 'IS_MULTI_SITE', 'IS_UVA_LOCATION',
|
||||
'NON_UVA_LOCATION', 'MULTI_SITE_LOCATIONS', 'IS_OUTSIDE_CONTRACT',
|
||||
'IS_UVA_PI_MULTI', 'IS_NOT_PRC_WAIVER', 'IS_INSIDE_CONTRACT',
|
||||
'IS_CANCER_PATIENT', 'UPLOAD_COMPLETE', 'IS_FUNDING_SOURCE',
|
||||
'IS_CODED_RESEARCH', 'IS_OUTSIDE_SPONSOR', 'IS_PI_INITIATED',
|
||||
'IS_ENGAGED_RESEARCH', 'IS_APPROVED_DEVICE', 'IS_FINANCIAL_CONFLICT',
|
||||
'IS_NOT_CONSENT_WAIVER', 'IS_FOR_CANCER_CENTER', 'IS_REVIEW_BY_CENTRAL_IRB',
|
||||
'IRBREVIEWERADMIN', 'IS_UVA_COLLABANALYSIS', 'REVIEW_TYPE', 'REVIEWTYPENAME']
|
||||
|
||||
|
||||
class ConfirmDeleteForm(FlaskForm):
|
||||
|
@ -100,7 +120,7 @@ class StudyTable(Table):
|
|||
NETBADGEID = Col('User')
|
||||
DATE_MODIFIED = DatetimeCol('Last Update', "medium")
|
||||
Q_COMPLETE = BoolCol('Complete?')
|
||||
requirements = NestedTableCol('Requirements', RequirementsTable)
|
||||
requirements = NestedTableCol('Documents', RequirementsTable)
|
||||
investigators = NestedTableCol('Investigators', InvestigatorsTable)
|
||||
sponsors = NestedTableCol('Sponsors', SponsorsTable)
|
||||
delete = LinkCol(
|
||||
|
|
18
pb/models.py
18
pb/models.py
|
@ -100,7 +100,6 @@ class Investigator(db.Model):
|
|||
Investigator(INVESTIGATORTYPE="AS_C", INVESTIGATORTYPEFULL="Additional Study Coordinators"),
|
||||
Investigator(INVESTIGATORTYPE="DEPT_CH", INVESTIGATORTYPEFULL="Department Chair"),
|
||||
Investigator(INVESTIGATORTYPE="IRBC", INVESTIGATORTYPEFULL="IRB Coordinator"),
|
||||
Investigator(INVESTIGATORTYPE="SCI", INVESTIGATORTYPEFULL="Scientific Contact"),
|
||||
]
|
||||
return types
|
||||
|
||||
|
@ -137,11 +136,9 @@ class RequiredDocument(db.Model):
|
|||
RequiredDocument(AUXDOCID=21, AUXDOC="New Medical Device Form"),
|
||||
RequiredDocument(AUXDOCID=22, AUXDOC="SOM CTO Review regarding need for IDE"),
|
||||
RequiredDocument(AUXDOCID=23, AUXDOC="SOM CTO Review regarding need for IND"),
|
||||
RequiredDocument(AUXDOCID=24, AUXDOC="InfoSec Approval"),
|
||||
RequiredDocument(AUXDOCID=25, AUXDOC="Scientific Pre-review Documentation"),
|
||||
RequiredDocument(AUXDOCID=26, AUXDOC="IBC Number"),
|
||||
RequiredDocument(AUXDOCID=32, AUXDOC="IDS - Investigational Drug Service Approval"),
|
||||
RequiredDocument(AUXDOCID=33, AUXDOC="Data Security Plan"),
|
||||
RequiredDocument(AUXDOCID=36, AUXDOC="RDRC Approval "),
|
||||
RequiredDocument(AUXDOCID=40, AUXDOC="SBS/IRB Approval-FERPA"),
|
||||
RequiredDocument(AUXDOCID=41, AUXDOC="HIRE Standard Radiation Language"),
|
||||
|
@ -185,13 +182,13 @@ class StudyDetails(db.Model):
|
|||
PRC_NUMBER = db.Column(db.String, nullable=True)
|
||||
IS_IBC = db.Column(db.Integer, nullable=True)
|
||||
IBC_NUMBER = db.Column(db.String, nullable=True)
|
||||
SPONSORS_PROTOCOL_REVISION_DATE = db.Column(db.Integer, nullable=True)
|
||||
SPONSORS_PROTOCOL_REVISION_DATE = db.Column(db.Date, nullable=True)
|
||||
IS_SPONSOR_MONITORING = db.Column(db.Integer, nullable=True)
|
||||
IS_AUX = db.Column(db.Integer, nullable=True)
|
||||
IS_SPONSOR = db.Column(db.Integer, nullable=True)
|
||||
IS_GRANT = db.Column(db.Integer, nullable=True)
|
||||
IS_COMMITTEE_CONFLICT = db.Column(db.Integer, nullable=True)
|
||||
DSMB = db.Column(db.Integer, nullable=True)
|
||||
DSMB = db.Column(db.String, nullable=True)
|
||||
DSMB_FREQUENCY = db.Column(db.Integer, nullable=True)
|
||||
IS_DB = db.Column(db.Integer, nullable=True)
|
||||
IS_UVA_DB = db.Column(db.Integer, nullable=True)
|
||||
|
@ -232,6 +229,17 @@ class StudyDetails(db.Model):
|
|||
IS_FOR_CANCER_CENTER = db.Column(db.Integer, nullable=True)
|
||||
IS_REVIEW_BY_CENTRAL_IRB = db.Column(db.Integer, nullable=True)
|
||||
IRBREVIEWERADMIN = db.Column(db.String, nullable=True)
|
||||
IS_SPONSOR_TRACKING = db.Column(db.Integer, nullable=True)
|
||||
SPONSOR_TRACKING = db.Column(db.Integer, nullable=True)
|
||||
IS_DSMB = db.Column(db.Integer, nullable=True)
|
||||
IS_COMPLETE_NON_IRB_REGULATORY = db.Column(db.Integer, nullable=True)
|
||||
IS_INSIDE_CONTRACT = db.Column(db.Integer, nullable=True)
|
||||
IS_CODED_RESEARCH = db.Column(db.Integer, nullable=True)
|
||||
IS_OUTSIDE_SPONSOR = db.Column(db.Integer, nullable=True)
|
||||
IS_UVA_COLLABANALYSIS = db.Column(db.Integer, nullable=True)
|
||||
REVIEW_TYPE = db.Column(db.Integer, nullable=True)
|
||||
REVIEWTYPENAME = db.Column(db.String, nullable=True)
|
||||
|
||||
|
||||
|
||||
class StudyDetailsSchema(ma.SQLAlchemyAutoSchema):
|
||||
|
|
|
@ -14,6 +14,16 @@
|
|||
<body>
|
||||
<h2>{{ title }}</h2>
|
||||
<p>{{ details|safe }}</p>
|
||||
|
||||
{% if 'Edit Study Details' in title %}
|
||||
<div>Upload CSV</div>
|
||||
<form action="{{ csv_action }}" method="post" enctype=multipart/form-data>
|
||||
{{ form.csrf_token() }}
|
||||
<input type=file name=file>
|
||||
<input type=submit value=Upload label="Upload CSV">
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
<form action="{{ action }}" method="post">
|
||||
|
||||
{{ form.csrf_token() }}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
STUDYID,UVA_STUDY_TRACKING,IS_IND,IND_1,IND_2,IND_3,IS_UVA_IND,IS_IDE,IDE,IS_UVA_IDE,IS_CHART_REVIEW,IS_RADIATION,GCRC_NUMBER,IS_GCRC,IS_PRC_DSMP,IS_PRC,PRC_NUMBER,IS_IBC,IBC_NUMBER,IS_SPONSOR_TRACKING,SPONSOR_TRACKING,SPONSORS_PROTOCOL_REVISION_DATE,IS_SPONSOR_MONITORING,IS_DSMB,IS_COMPLETE_NON_IRB_REGULATORY,IS_AUX,IS_SPONSOR,IS_GRANT,IS_COMMITTEE_CONFLICT,DSMB,DSMB_FREQUENCY,IS_DB,IS_UVA_DB,IS_CENTRAL_REG_DB,IS_CONSENT_WAIVER,IS_HGT,IS_GENE_TRANSFER,IS_TISSUE_BANKING,IS_SURROGATE_CONSENT,IS_ADULT_PARTICIPANT,IS_MINOR_PARTICIPANT,IS_MINOR,IS_BIOMEDICAL,IS_QUALITATIVE,IS_PI_SCHOOL,IS_PRISONERS_POP,IS_PREGNANT_POP,IS_FETUS_POP,IS_MENTAL_IMPAIRMENT_POP,IS_ELDERLY_POP,IS_OTHER_VULNERABLE_POP,OTHER_VULNERABLE_DESC,IS_MULTI_SITE,IS_UVA_LOCATION,NON_UVA_LOCATION,MULTI_SITE_LOCATIONS,IS_OUTSIDE_CONTRACT,IS_UVA_PI_MULTI,IS_NOT_PRC_WAIVER,IS_INSIDE_CONTRACT,IS_CANCER_PATIENT,UPLOAD_COMPLETE,IS_FUNDING_SOURCE,IS_CODED_RESEARCH,IS_OUTSIDE_SPONSOR,IS_PI_INITIATED,IS_ENGAGED_RESEARCH,IS_APPROVED_DEVICE,IS_FINANCIAL_CONFLICT,IS_NOT_CONSENT_WAIVER,IS_FOR_CANCER_CENTER,IS_REVIEW_BY_CENTRAL_IRB,IRBREVIEWERADMIN,IS_UVA_COLLABANALYSIS
|
||||
15370,,0,abc,def,ghi,,1,1234,1,0,,,,1,,,,,1,123456,2019-01-01T00:00:00,1,1,,1,1,1,0,UVA Cancer Center,4,,,1,0,0,0,,,,,,1,0,1,,,,,,,,1,0,"Martha Jefferson Hospital, Augusta Health Medical Center, Rockingham Memorial Hospital",,1,,,1,1,,,,1,1,,1,1,0,,1,,1
|
|
|
@ -25,7 +25,7 @@ class Sanity_Check_Test(unittest.TestCase):
|
|||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
pass
|
||||
db.drop_all()
|
||||
|
||||
def setUp(self):
|
||||
ExampleDataLoader().clean_db()
|
||||
|
@ -164,3 +164,27 @@ class Sanity_Check_Test(unittest.TestCase):
|
|||
study = self.add_study(title=title)
|
||||
self.assertEqual(title, study.TITLE)
|
||||
|
||||
# def test_update_study_from_csv(self):
|
||||
# study = self.add_study()
|
||||
# f = open('tests/data/ExampleStudyID15370.csv', 'rb')
|
||||
# r = self.app.post(f'/study_details/{study.STUDYID}', data={'file': [f]}, follow_redirects=False)
|
||||
#
|
||||
# print(r)
|
||||
# print('test_update_study_from_csv')
|
||||
|
||||
# def test_study_details_validation(self):
|
||||
#
|
||||
# test_study = self.add_study()
|
||||
# data = {'IS_IND': 1, 'IND_1': 1234}
|
||||
# r = self.app.post(f'/study_details/{test_study.STUDYID}', data=data, follow_redirects=False)
|
||||
# self.assertNotIn('Form did not validate!', r.data.decode('utf-8'))
|
||||
#
|
||||
# print('test_study_details_validation')
|
||||
#
|
||||
# def test_study_details_validation_fail(self):
|
||||
# test_study = self.add_study()
|
||||
# data = {'IS_IND': 1, 'IND_1': 1234, 'IS_IDE': 'b'}
|
||||
# r = self.app.post(f'/study_details/{test_study.STUDYID}', data=data, follow_redirects=False)
|
||||
# self.assertIn('Form did not validate!', r.data.decode('utf-8'))
|
||||
#
|
||||
# print('test_study_details_validation_fail')
|
||||
|
|
Loading…
Reference in New Issue