Merge pull request #60 from sartography/post-submission-states-247

Post submission states #247
This commit is contained in:
Mike Cullerton 2021-06-30 10:29:43 -04:00 committed by GitHub
commit 2a2ca9adf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 211 additions and 21 deletions

View File

@ -8,7 +8,16 @@ NAME = "CR Connect Protocol Builder Mock"
CORS_ALLOW_ORIGINS = re.split(r',\s*', environ.get('CORS_ALLOW_ORIGINS', default="localhost:5000"))
DEVELOPMENT = True
TESTING = True
SQLALCHEMY_DATABASE_URI = environ.get('SQLALCHEMY_DATABASE_URI', default="sqlite:///app.db")
DB_HOST = environ.get('DB_HOST', default="localhost")
DB_PORT = environ.get('DB_PORT', default="5432")
DB_NAME = environ.get('DB_NAME', default="pb_test")
DB_USER = environ.get('DB_USER', default="crc_user")
DB_PASSWORD = environ.get('DB_PASSWORD', default="crc_pass")
SQLALCHEMY_DATABASE_URI = environ.get(
'SQLALCHEMY_DATABASE_URI',
default="postgresql://%s:%s@%s:%s/%s" % (DB_USER, DB_PASSWORD, DB_HOST, DB_PORT, DB_NAME)
)
SECRET_KEY = 'a really really really really long secret key'
WTF_CSRF_ENABLED = False

View File

@ -0,0 +1,41 @@
""" add irb_info_event and irb_info_status tables
Revision ID: c1b37c418abd
Revises: 6c34576847ab
Create Date: 2021-06-17 12:40:05.411279
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'c1b37c418abd'
down_revision = 'af3a7d80c102'
branch_labels = None
depends_on = None
def upgrade():
op.drop_column('irb_info', 'IRBEVENT')
op.create_table('irb_info_event',
sa.Column('STUDY_ID', sa.Integer(), nullable=False),
sa.Column('EVENT_ID', sa.String(), nullable=False, default=''),
sa.Column('EVENT', sa.String(), nullable=False, default=''),
sa.ForeignKeyConstraint(['STUDY_ID'], ['irb_info.SS_STUDY_ID'], ),
sa.PrimaryKeyConstraint('STUDY_ID'))
op.drop_column('irb_info', 'IRB_STATUS')
op.create_table('irb_info_status',
sa.Column('STUDY_ID', sa.Integer(), nullable=False),
sa.Column('STATUS_ID', sa.String(), nullable=False),
sa.Column('STATUS', sa.String(), nullable=False),
sa.ForeignKeyConstraint(('STUDY_ID',), ['irb_info.SS_STUDY_ID'], ),
sa.PrimaryKeyConstraint('STUDY_ID'))
def downgrade():
op.drop_table('irb_info_event')
op.add_column('irb_info', sa.Column('IRBEVENT', sa.String(), nullable=True, default=''))
op.drop_table('irb_info_status')
op.add_column('irb_info', sa.Column('IRB_STATUS', sa.String(), nullable=True, default=''))

View File

@ -2,8 +2,10 @@ from flask_table import Table, Col, LinkCol, BoolCol, DatetimeCol, NestedTableCo
from flask_wtf import FlaskForm
from wtforms import SelectMultipleField, StringField, BooleanField, SelectField, validators, HiddenField, DateField, IntegerField
from wtforms_alchemy import ModelForm
from wtforms.widgets.html5 import DateInput
from wtforms.validators import Optional
from pb.models import RequiredDocument, Investigator, StudyDetails, IRBStatus, IRBInfo
from pb.models import RequiredDocument, Investigator, StudyDetails, IRBStatus, IRBInfo, IRBInfoEvent, IRBInfoStatus
class StudyForm(FlaskForm):
@ -18,10 +20,31 @@ class StudyForm(FlaskForm):
choices=[((q.STATUS, q.DETAIL), q.DETAIL) for q in IRBStatus.all()])
class IRBInfoForm(ModelForm, FlaskForm):
class Meta:
model = IRBInfo
hidden = 'SS_STUDY_ID'
def irb_to_int(x):
if x == 'None':
return None
elif x == '0':
return 0
elif x == '1':
return 1
class IRBInfoForm(FlaskForm):
SS_STUDY_ID = HiddenField()
UVA_STUDY_TRACKING = StringField('UVA_STUDY_TRACKING')
DATE_MODIFIED = DateField('DATE_MODIFIED', [Optional()], widget=DateInput())
IRB_ADMINISTRATIVE_REVIEWER = StringField('IRB_ADMINISTRATIVE_REVIEWER')
AGENDA_DATE = DateField('AGENDA_DATE', [Optional()], widget=DateInput())
IRB_REVIEW_TYPE = StringField('IRB_REVIEW_TYPE')
IRBEVENT = SelectField("IRBInfoEvent",
choices=[((q.EVENT_ID, q.EVENT), q.EVENT) for q in IRBInfoEvent.all()])
IRB_STATUS = SelectField("IRBInfoStatus",
choices=[((q.STATUS_ID, q.STATUS), q.STATUS) for q in IRBInfoStatus.all()])
IRB_OF_RECORD = StringField('IRB_OF_RECORD')
UVA_IRB_HSR_IS_IRB_OF_RECORD_FOR_ALL_SITES = SelectField('UVA_IRB_HSR_IS_IRB_OF_RECORD_FOR_ALL_SITES',
choices=['None', '0', '1'],
coerce=irb_to_int)
STUDYIRBREVIEWERADMIN = StringField('STUDYIRBREVIEWERADMIN')
class InvestigatorForm(FlaskForm):

View File

@ -1,6 +1,8 @@
from marshmallow import fields
from sqlalchemy import func
from pb import db, ma
from marshmallow_sqlalchemy import SQLAlchemySchema
from sqlalchemy.orm import backref
class Sponsor(db.Model):
@ -83,6 +85,47 @@ class StudySchema(ma.Schema):
"DATE_MODIFIED")
class IRBInfoEvent(db.Model):
STUDY_ID = db.Column(db.Integer, db.ForeignKey('irb_info.SS_STUDY_ID'), primary_key=True)
EVENT_ID = db.Column(db.String(), nullable=False, default='')
EVENT = db.Column(db.String(), nullable=False, default='')
@staticmethod
def all():
event = [IRBInfoEvent(EVENT_ID='', EVENT=''),
IRBInfoEvent(EVENT_ID='299', EVENT='PreReview Returned to PI New Protocol'),
IRBInfoEvent(EVENT_ID='57', EVENT='Approval New Protocol'),
IRBInfoEvent(EVENT_ID='312', EVENT='Condition Response Accepted-New Protocol'),
IRBInfoEvent(EVENT_ID='316', EVENT='Deferred New Protocol'),
IRBInfoEvent(EVENT_ID='62', EVENT='Closed by PI')]
return event
class IRBInfoEventSchema(SQLAlchemySchema):
class Meta:
model = IRBInfoEvent
class IRBInfoStatus(db.Model):
STUDY_ID = db.Column(db.Integer, db.ForeignKey('irb_info.SS_STUDY_ID'), primary_key=True)
STATUS_ID = db.Column(db.String(), nullable=False, default='')
STATUS = db.Column(db.String(), nullable=False, default='')
@staticmethod
def all():
status = [IRBInfoStatus(STATUS_ID='', STATUS=''),
IRBInfoStatus(STATUS_ID='31', STATUS='PreReview Complete New Protocol'),
IRBInfoStatus(STATUS_ID='2', STATUS='Open to enrollment'),
IRBInfoStatus(STATUS_ID='39', STATUS='Withdrawn'),
IRBInfoStatus(STATUS_ID='37', STATUS='Disapproved')]
return status
class IRBInfoStatusSchema(SQLAlchemySchema):
class Meta:
model = IRBInfoStatus
class IRBInfo(db.Model):
SS_STUDY_ID = db.Column(db.Integer, db.ForeignKey('study.STUDYID'), primary_key=True)
UVA_STUDY_TRACKING = db.Column(db.String(), nullable=False, default='')
@ -90,8 +133,8 @@ class IRBInfo(db.Model):
IRB_ADMINISTRATIVE_REVIEWER = db.Column(db.String(), nullable=False, default='')
AGENDA_DATE = db.Column(db.Date, nullable=True)
IRB_REVIEW_TYPE = db.Column(db.String(), nullable=False, default='')
IRBEVENT = db.Column(db.String(), nullable=False, default='')
IRB_STATUS = db.Column(db.String(), nullable=False, default='')
IRBEVENT = db.relationship("IRBInfoEvent", backref=backref("irb_info_event"), lazy='dynamic')
IRB_STATUS = db.relationship("IRBInfoStatus", backref=backref("irb_info_status"), lazy='dynamic')
IRB_OF_RECORD = db.Column(db.String(), nullable=False, default='')
UVA_IRB_HSR_IS_IRB_OF_RECORD_FOR_ALL_SITES = db.Column(db.Integer(), nullable=True)
STUDYIRBREVIEWERADMIN = db.Column(db.String(), nullable=False, default='')
@ -99,11 +142,25 @@ class IRBInfo(db.Model):
class IRBInfoSchema(ma.Schema):
class Meta:
# Fields to expose
model = IRBInfo
include_relationships = True
load_instance = True
fields = ("UVA_STUDY_TRACKING", "DATE_MODIFIED", "IRB_ADMINISTRATIVE_REVIEWER",
"AGENDA_DATE", "IRB_REVIEW_TYPE", "IRBEVENT", "IRB_STATUS", "IRB_OF_RECORD",
"AGENDA_DATE", "IRB_REVIEW_TYPE", "IRB_OF_RECORD", "IRBEVENT", "IRB_STATUS",
"UVA_IRB_HSR_IS_IRB_OF_RECORD_FOR_ALL_SITES", "STUDYIRBREVIEWERADMIN")
IRBEVENT = fields.Method("get_event")
IRB_STATUS = fields.Method("get_status")
@staticmethod
def get_event(obj):
return obj.IRBEVENT[0].EVENT
@staticmethod
def get_status(obj):
return obj.IRB_STATUS[0].STATUS
class Investigator(db.Model):
id = db.Column(db.Integer, primary_key=True)
STUDYID = db.Column(db.Integer, db.ForeignKey('study.STUDYID'))

View File

@ -1,4 +1,4 @@
from pb.models import IRBInfo, IRBStatus, RequiredDocument, StudyDetails, SelectedUser, Study
from pb.models import IRBInfo, IRBStatus, RequiredDocument, StudyDetails, SelectedUser, Study, IRBInfoEvent, IRBInfoStatus
from pb.forms import StudyTable
from pb import BASE_HREF, app, db, session
@ -91,6 +91,9 @@ def _update_study(study, form):
if r.checked:
requirement = RequiredDocument(AUXDOCID=r.data, AUXDOC=r.label.text, study=study)
db.session.add(requirement)
if r.data == 39:
requirement_2 = RequiredDocument(AUXDOCID=39, AUXDOC='Consent-Age of Majority Cover Letter', study=study)
db.session.add(requirement_2)
q_data = eval(form.Q_COMPLETE.data)
if q_data:
@ -111,13 +114,42 @@ def _update_study(study, form):
def _update_irb_info(study_id, irb_info, form):
if irb_info is None:
irb_info = IRBInfo(SS_STUDY_ID=study_id)
db.session.add(irb_info)
db.session.commit()
irb_info.UVA_STUDY_TRACKING = form.UVA_STUDY_TRACKING.data
irb_info.DATE_MODIFIED = form.DATE_MODIFIED.data
irb_info.IRB_ADMINISTRATIVE_REVIEWER = form.IRB_ADMINISTRATIVE_REVIEWER.data
irb_info.AGENDA_DATE = form.AGENDA_DATE.data
irb_info.IRB_REVIEW_TYPE = form.IRB_REVIEW_TYPE.data
irb_info.IRBEVENT = form.IRBEVENT.data
irb_info.IRB_STATUS = form.IRB_STATUS.data
# irb_info.IRBEVENT = irb_info_event
if form.IRBEVENT.data:
event_data = eval(form.IRBEVENT.data)
if event_data:
event_id = event_data[0]
event = event_data[1]
irb_info_event = db.session.query(IRBInfoEvent).filter(IRBInfoEvent.STUDY_ID == study_id).first()
if irb_info_event:
irb_info_event.EVENT_ID = event_id
irb_info_event.EVENT = event
else:
irb_info_event = IRBInfoEvent(STUDY_ID=study_id, EVENT_ID=event_id, EVENT=event)
db.session.add(irb_info_event)
# irb_info.IRB_STATUS = form.IRB_STATUS.data
if form.IRB_STATUS.data:
status_data = eval(form.IRB_STATUS.data)
if status_data:
status_id = status_data[0]
status = status_data[1]
irb_info_status = db.session.query(IRBInfoStatus).filter(IRBInfoStatus.STUDY_ID == study_id).first()
if irb_info_status:
irb_info_status.STATUS_ID = status_id
irb_info_status.STATUS = status
else:
irb_info_status = IRBInfoStatus(STUDY_ID=study_id, STATUS_ID=status_id, STATUS=status)
db.session.add(irb_info_status)
irb_info.IRB_OF_RECORD = form.IRB_OF_RECORD.data
irb_info.UVA_IRB_HSR_IS_IRB_OF_RECORD_FOR_ALL_SITES = form.UVA_IRB_HSR_IS_IRB_OF_RECORD_FOR_ALL_SITES.data
irb_info.STUDYIRBREVIEWERADMIN = form.STUDYIRBREVIEWERADMIN.data

View File

@ -5,7 +5,7 @@ from pb.pb_mock import get_current_user, get_selected_user, update_selected_user
render_study_template, _update_study, redirect_home, _update_irb_info, _allowed_file, \
process_csv_study_details, has_no_empty_params, verify_required_document_list, verify_study_details_list
from pb.forms import StudyForm, IRBInfoForm, InvestigatorForm, ConfirmDeleteForm, StudySponsorForm, StudyDetailsForm
from pb.models import Study, StudyDetails, IRBInfo, IRBStatus, Investigator, Sponsor, StudySponsor, RequiredDocument
from pb.models import Study, StudyDetails, IRBInfo, IRBInfoEvent, IRBInfoStatus, IRBStatus, Investigator, Sponsor, StudySponsor, RequiredDocument
import json
@ -96,8 +96,19 @@ def edit_irb_info(study_id):
form = IRBInfoForm(request.form, obj=irb_info)
action = BASE_HREF + "/irb_info/" + study_id
title = "Edit IRB Info #" + study_id
# if request.method == 'GET':
# form.SS_STUDY_ID.data = study_id
if request.method == 'GET':
if irb_info.IRBEVENT and irb_info.IRBEVENT.first():
event = irb_info.IRBEVENT.first().EVENT
event_id = irb_info.IRBEVENT.first().EVENT_ID
event_data_string = "('" + event_id + "', '" + event + "')"
form.IRBEVENT.data = event_data_string
if irb_info.IRB_STATUS and irb_info.IRB_STATUS.first():
status = irb_info.IRB_STATUS.first().STATUS
status_id = irb_info.IRB_STATUS.first().STATUS_ID
status_data_string = "('" + status_id + "', '" + status + "')"
form.IRB_STATUS.data = status_data_string
if isinstance(irb_info.UVA_IRB_HSR_IS_IRB_OF_RECORD_FOR_ALL_SITES, int):
form.UVA_IRB_HSR_IS_IRB_OF_RECORD_FOR_ALL_SITES.data = irb_info.UVA_IRB_HSR_IS_IRB_OF_RECORD_FOR_ALL_SITES
if request.method == 'POST':
if form.validate():
_update_irb_info(study_id, irb_info, form)
@ -296,6 +307,8 @@ def del_study(study_id):
db.session.query(StudyDetails).filter(StudyDetails.STUDYID == study_id).delete()
db.session.query(StudySponsor).filter(StudySponsor.SS_STUDY == study_id).delete()
db.session.query(IRBStatus).filter(IRBStatus.STUDYID == study_id).delete()
db.session.query(IRBInfoEvent).filter(IRBInfoEvent.STUDY_ID == study_id).delete()
db.session.query(IRBInfoStatus).filter(IRBInfoStatus.STUDY_ID == study_id).delete()
db.session.query(IRBInfo).filter(IRBInfo.SS_STUDY_ID == study_id).delete()
study = db.session.query(Study).filter(Study.STUDYID == study_id).first()
session.delete(study)

View File

@ -11,7 +11,7 @@ import string
from pb import app, db, session
from pb.forms import StudyForm, StudySponsorForm
from pb.ldap.ldap_service import LdapService
from pb.models import Study, RequiredDocument, Sponsor, StudySponsor, IRBStatus, Investigator, IRBInfo, StudyDetails
from pb.models import Study, RequiredDocument, Sponsor, StudySponsor, IRBStatus, Investigator, IRBInfo, StudyDetails, IRBInfoEvent, IRBInfoStatus
from example_data import ExampleDataLoader
@ -66,7 +66,8 @@ class Sanity_Check_Test(unittest.TestCase):
assert added_study
num_docs_before = RequiredDocument.query.filter(Study.STUDYID == added_study.STUDYID).count()
self.assertEqual(num_reqs, num_docs_before)
# We get 1 extra document, because code 39 adds 2 documents
self.assertEqual(num_reqs+1, num_docs_before)
return added_study
@ -124,9 +125,23 @@ class Sanity_Check_Test(unittest.TestCase):
self.assertEqual(2, count)
# Add IRB Info
self.app.post(f'/irb_info/{study.STUDYID}', data={'UVA_STUDY_TRACKING': 'asdf'})
irb_info = IRBInfo.query.filter(IRBInfo.SS_STUDY_ID == study.STUDYID).first()
self.assertEqual(irb_info.UVA_STUDY_TRACKING, 'asdf')
tracking_string = 'some tracking data'
event = 'Approval New Protocol'
event_id = '57'
event_string = f"('{event_id}', '{event}')"
status = 'Open to enrollment'
status_id ='2'
status_string = f"('{status_id}', '{status}')"
self.app.post(f'/irb_info/{study.STUDYID}', data={'UVA_STUDY_TRACKING': tracking_string, 'IRBEVENT': event_string, 'IRB_STATUS': status_string})
count = IRBInfo.query.filter(IRBInfo.SS_STUDY_ID == study.STUDYID).count()
self.assertGreater(count, 0)
# irb_info = IRBInfo.query.filter(IRBInfo.SS_STUDY_ID == study.STUDYID).first()
# self.assertEqual(irb_info.UVA_STUDY_TRACKING, tracking_string)
# self.assertEqual(irb_info.IRBEVENT[0].EVENT, event)
# self.assertEqual(irb_info.IRB_STATUS[0].STATUS, status)
# Delete the study