Merge pull request #32 from sartography/feature/new_study_states
Updated the study status to use a different enumeration. Migration c…
This commit is contained in:
commit
79d9a66ce3
|
@ -828,7 +828,7 @@ components:
|
||||||
example: dhf8r
|
example: dhf8r
|
||||||
protocol_builder_status:
|
protocol_builder_status:
|
||||||
type: string
|
type: string
|
||||||
enum: [DRAFT, IN_PROCESS, IN_REVIEW, REVIEW_COMPLETE, INACTIVE]
|
enum: [INCOMPLETE, ACTIVE, HOLD, OPEN, ABANDONED]
|
||||||
example: done
|
example: done
|
||||||
sponsor:
|
sponsor:
|
||||||
type: string
|
type: string
|
||||||
|
|
|
@ -82,7 +82,7 @@ def post_update_study_from_protocol_builder(study_id):
|
||||||
db_study.update_from_protocol_builder(pb_study)
|
db_study.update_from_protocol_builder(pb_study)
|
||||||
else:
|
else:
|
||||||
db_study.inactive = True
|
db_study.inactive = True
|
||||||
db_study.protocol_builder_status = ProtocolBuilderStatus.INACTIVE
|
db_study.protocol_builder_status = ProtocolBuilderStatus.ABANDONED
|
||||||
|
|
||||||
return NoContent, 304
|
return NoContent, 304
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,23 @@ class ProtocolBuilderInvestigatorType(enum.Enum):
|
||||||
|
|
||||||
|
|
||||||
class ProtocolBuilderStatus(enum.Enum):
|
class ProtocolBuilderStatus(enum.Enum):
|
||||||
DRAFT = 'draft', # !Q_COMPLETE
|
# • Active: found in PB and no HSR number and not hold
|
||||||
IN_PROCESS = 'in_process', # Q_COMPLETE && !UPLOAD_COMPLETE && !HSRNUMBER
|
# • Hold: store boolean value in CR Connect (add to Study Model)
|
||||||
IN_REVIEW = 'in_review', # Q_COMPLETE && (!UPLOAD_COMPLETE || !HSRNUMBER)
|
# • Open To Enrollment: has start date and HSR number?
|
||||||
REVIEW_COMPLETE = 'review_complete', # Q_COMPLETE && UPLOAD_COMPLETE && HSRNUMBER
|
# • Abandoned: deleted in PB
|
||||||
INACTIVE = 'inactive', # Not found in PB
|
INCOMPLETE = 'incomplete' # Found in PB but not ready to start (not q_complete)
|
||||||
|
ACTIVE = 'active', # found in PB, marked as "q_complete" and no HSR number and not hold
|
||||||
|
HOLD = 'hold', # CR Connect side, if the Study ias marked as "hold".
|
||||||
|
OPEN = 'open', # Open To Enrollment: has start date and HSR number?
|
||||||
|
ABANDONED = 'Abandoned' # Not found in PB
|
||||||
|
|
||||||
|
|
||||||
|
#DRAFT = 'draft', # !Q_COMPLETE
|
||||||
|
#IN_PROCESS = 'in_process', # Q_COMPLETE && !UPLOAD_COMPLETE && !HSRNUMBER
|
||||||
|
#IN_REVIEW = 'in_review', # Q_COMPLETE && (!UPLOAD_COMPLETE || !HSRNUMBER)
|
||||||
|
#REVIEW_COMPLETE = 'review_complete', # Q_COMPLETE && UPLOAD_COMPLETE && HSRNUMBER
|
||||||
|
#INACTIVE = 'inactive', # Not found in PB
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ProtocolBuilderStudy(object):
|
class ProtocolBuilderStudy(object):
|
||||||
|
|
|
@ -22,24 +22,22 @@ class StudyModel(db.Model):
|
||||||
ind_number = db.Column(db.String, nullable=True)
|
ind_number = db.Column(db.String, nullable=True)
|
||||||
user_uid = db.Column(db.String, db.ForeignKey('user.uid'), nullable=False)
|
user_uid = db.Column(db.String, db.ForeignKey('user.uid'), nullable=False)
|
||||||
investigator_uids = db.Column(db.ARRAY(db.String), nullable=True)
|
investigator_uids = db.Column(db.ARRAY(db.String), nullable=True)
|
||||||
inactive = db.Column(db.Boolean, default=False)
|
|
||||||
requirements = db.Column(db.ARRAY(db.Integer), nullable=True)
|
requirements = db.Column(db.ARRAY(db.Integer), nullable=True)
|
||||||
|
on_hold = db.Column(db.Boolean, default=False)
|
||||||
|
|
||||||
def update_from_protocol_builder(self, pbs: ProtocolBuilderStudy):
|
def update_from_protocol_builder(self, pbs: ProtocolBuilderStudy):
|
||||||
self.hsr_number = pbs.HSRNUMBER
|
self.hsr_number = pbs.HSRNUMBER
|
||||||
self.title = pbs.TITLE
|
self.title = pbs.TITLE
|
||||||
self.user_uid = pbs.NETBADGEID
|
self.user_uid = pbs.NETBADGEID
|
||||||
self.last_updated = pbs.DATE_MODIFIED
|
self.last_updated = pbs.DATE_MODIFIED
|
||||||
self.protocol_builder_status = ProtocolBuilderStatus.DRAFT
|
self.protocol_builder_status = ProtocolBuilderStatus.INCOMPLETE
|
||||||
self.inactive = False
|
|
||||||
|
|
||||||
if pbs.HSRNUMBER: # And Up load complete?
|
|
||||||
self.protocol_builder_status = ProtocolBuilderStatus.REVIEW_COMPLETE
|
|
||||||
elif pbs.Q_COMPLETE:
|
|
||||||
self.protocol_builder_status = ProtocolBuilderStatus.IN_PROCESS
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if pbs.Q_COMPLETE:
|
||||||
|
self.protocol_builder_status = ProtocolBuilderStatus.ACTIVE
|
||||||
|
if pbs.HSRNUMBER:
|
||||||
|
self.protocol_builder_status = ProtocolBuilderStatus.OPEN
|
||||||
|
if self.on_hold:
|
||||||
|
self.protocol_builder_status = ProtocolBuilderStatus.HOLD
|
||||||
|
|
||||||
|
|
||||||
class WorkflowMetadata(object):
|
class WorkflowMetadata(object):
|
||||||
|
@ -106,7 +104,7 @@ class Study(object):
|
||||||
|
|
||||||
def __init__(self, id, title, last_updated, primary_investigator_id, user_uid,
|
def __init__(self, id, title, last_updated, primary_investigator_id, user_uid,
|
||||||
protocol_builder_status=None,
|
protocol_builder_status=None,
|
||||||
sponsor="", hsr_number="", ind_number="", inactive=False, categories=[], **argsv):
|
sponsor="", hsr_number="", ind_number="", categories=[], **argsv):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.user_uid = user_uid
|
self.user_uid = user_uid
|
||||||
self.title = title
|
self.title = title
|
||||||
|
@ -116,7 +114,6 @@ class Study(object):
|
||||||
self.sponsor = sponsor
|
self.sponsor = sponsor
|
||||||
self.hsr_number = hsr_number
|
self.hsr_number = hsr_number
|
||||||
self.ind_number = ind_number
|
self.ind_number = ind_number
|
||||||
self.inactive = inactive
|
|
||||||
self.categories = categories
|
self.categories = categories
|
||||||
self.warnings = []
|
self.warnings = []
|
||||||
|
|
||||||
|
@ -149,7 +146,7 @@ class StudySchema(ma.Schema):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Study
|
model = Study
|
||||||
additional = ["id", "title", "last_updated", "primary_investigator_id", "user_uid",
|
additional = ["id", "title", "last_updated", "primary_investigator_id", "user_uid",
|
||||||
"sponsor", "ind_number", "inactive"]
|
"sponsor", "ind_number"]
|
||||||
unknown = INCLUDE
|
unknown = INCLUDE
|
||||||
|
|
||||||
@marshmallow.post_load
|
@marshmallow.post_load
|
||||||
|
|
|
@ -88,8 +88,7 @@ class StudyService(object):
|
||||||
for study in db_studies:
|
for study in db_studies:
|
||||||
pb_study = next((pbs for pbs in pb_studies if pbs.STUDYID == study.id), None)
|
pb_study = next((pbs for pbs in pb_studies if pbs.STUDYID == study.id), None)
|
||||||
if not pb_study:
|
if not pb_study:
|
||||||
study.inactive = True
|
study.protocol_builder_status = ProtocolBuilderStatus.ABANDONED
|
||||||
study.protocol_builder_status = ProtocolBuilderStatus.INACTIVE
|
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 0f38d7a36f21
|
||||||
|
Revises: 476f8a4933ba
|
||||||
|
Create Date: 2020-04-21 15:42:46.430272
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '0f38d7a36f21'
|
||||||
|
down_revision = '476f8a4933ba'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('study', sa.Column('on_hold', sa.Boolean(), nullable=True))
|
||||||
|
op.drop_column('study', 'inactive')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
op.execute('ALTER TYPE protocolbuilderstatus RENAME TO pbs_old;')
|
||||||
|
op.execute("CREATE TYPE protocolbuilderstatus AS ENUM('INCOMPLETE', 'ACTIVE', 'HOLD', 'OPEN', 'ABANDONED')")
|
||||||
|
op.execute("ALTER TABLE study ALTER COLUMN protocol_builder_status TYPE protocolbuilderstatus USING protocol_builder_status::text::protocolbuilderstatus;")
|
||||||
|
op.execute('DROP TYPE pbs_old;')
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('study', sa.Column('inactive', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||||
|
op.drop_column('study', 'on_hold')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
op.execute('ALTER TYPE protocolbuilderstatus RENAME TO pbs_old;')
|
||||||
|
op.execute("CREATE TYPE protocolbuilderstatus AS ENUM('DRAFT', 'IN_PROCESS', 'IN_REVIEW', 'REVIEW_COMPLETE', 'INACTIVE')")
|
||||||
|
op.execute("ALTER TABLE study ALTER COLUMN protocol_builder_status TYPE protocolbuilderstatus USING protocol_builder_status::text::protocolbuilderstatus;")
|
||||||
|
op.execute('DROP TYPE pbs_old;')
|
|
@ -53,7 +53,7 @@ class BaseTest(unittest.TestCase):
|
||||||
'id':0,
|
'id':0,
|
||||||
'title':'The impact of fried pickles on beer consumption in bipedal software developers.',
|
'title':'The impact of fried pickles on beer consumption in bipedal software developers.',
|
||||||
'last_updated':datetime.datetime.now(),
|
'last_updated':datetime.datetime.now(),
|
||||||
'protocol_builder_status':ProtocolBuilderStatus.IN_PROCESS,
|
'protocol_builder_status':ProtocolBuilderStatus.ACTIVE,
|
||||||
'primary_investigator_id':'dhf8r',
|
'primary_investigator_id':'dhf8r',
|
||||||
'sponsor':'Sartography Pharmaceuticals',
|
'sponsor':'Sartography Pharmaceuticals',
|
||||||
'ind_number':'1234',
|
'ind_number':'1234',
|
||||||
|
@ -63,7 +63,7 @@ class BaseTest(unittest.TestCase):
|
||||||
'id':1,
|
'id':1,
|
||||||
'title':'Requirement of hippocampal neurogenesis for the behavioral effects of soft pretzels',
|
'title':'Requirement of hippocampal neurogenesis for the behavioral effects of soft pretzels',
|
||||||
'last_updated':datetime.datetime.now(),
|
'last_updated':datetime.datetime.now(),
|
||||||
'protocol_builder_status':ProtocolBuilderStatus.IN_PROCESS,
|
'protocol_builder_status':ProtocolBuilderStatus.ACTIVE,
|
||||||
'primary_investigator_id':'dhf8r',
|
'primary_investigator_id':'dhf8r',
|
||||||
'sponsor':'Makerspace & Co.',
|
'sponsor':'Makerspace & Co.',
|
||||||
'ind_number':'5678',
|
'ind_number':'5678',
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"DATE_MODIFIED": "2020-02-19T14:24:55.101695",
|
"DATE_MODIFIED": "2020-02-19T14:24:55.101695",
|
||||||
"HSRNUMBER": "56753",
|
"HSRNUMBER": "",
|
||||||
"NETBADGEID": "dhf8r",
|
"NETBADGEID": "dhf8r",
|
||||||
"Q_COMPLETE": true,
|
"Q_COMPLETE": true,
|
||||||
"STUDYID": 65432,
|
"STUDYID": 65432,
|
||||||
|
@ -17,9 +17,9 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"DATE_MODIFIED": "2020-02-19T14:24:55.101695",
|
"DATE_MODIFIED": "2020-02-19T14:24:55.101695",
|
||||||
"HSRNUMBER": "45678",
|
"HSRNUMBER": "",
|
||||||
"NETBADGEID": "dhf8r",
|
"NETBADGEID": "dhf8r",
|
||||||
"Q_COMPLETE": true,
|
"Q_COMPLETE": false,
|
||||||
"STUDYID": 1,
|
"STUDYID": 1,
|
||||||
"TITLE": "Efficacy of xenomorph bio-augmented circuits on dexterity of cybernetic prostheses"
|
"TITLE": "Efficacy of xenomorph bio-augmented circuits on dexterity of cybernetic prostheses"
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ class TestStudyApi(BaseTest):
|
||||||
"title": "Phase III Trial of Genuine People Personalities (GPP) Autonomous Intelligent Emotional Agents "
|
"title": "Phase III Trial of Genuine People Personalities (GPP) Autonomous Intelligent Emotional Agents "
|
||||||
"for Interstellar Spacecraft",
|
"for Interstellar Spacecraft",
|
||||||
"last_updated": datetime.now(tz=timezone.utc),
|
"last_updated": datetime.now(tz=timezone.utc),
|
||||||
"protocol_builder_status": ProtocolBuilderStatus.IN_PROCESS,
|
"protocol_builder_status": ProtocolBuilderStatus.ACTIVE,
|
||||||
"primary_investigator_id": "tricia.marie.mcmillan@heartofgold.edu",
|
"primary_investigator_id": "tricia.marie.mcmillan@heartofgold.edu",
|
||||||
"sponsor": "Sirius Cybernetics Corporation",
|
"sponsor": "Sirius Cybernetics Corporation",
|
||||||
"ind_number": "567890",
|
"ind_number": "567890",
|
||||||
|
@ -104,7 +104,7 @@ class TestStudyApi(BaseTest):
|
||||||
self.load_example_data()
|
self.load_example_data()
|
||||||
study: StudyModel = session.query(StudyModel).first()
|
study: StudyModel = session.query(StudyModel).first()
|
||||||
study.title = "Pilot Study of Fjord Placement for Single Fraction Outcomes to Cortisol Susceptibility"
|
study.title = "Pilot Study of Fjord Placement for Single Fraction Outcomes to Cortisol Susceptibility"
|
||||||
study.protocol_builder_status = ProtocolBuilderStatus.REVIEW_COMPLETE
|
study.protocol_builder_status = ProtocolBuilderStatus.ACTIVE
|
||||||
rv = self.app.put('/v1.0/study/%i' % study.id,
|
rv = self.app.put('/v1.0/study/%i' % study.id,
|
||||||
content_type="application/json",
|
content_type="application/json",
|
||||||
headers=self.logged_in_headers(),
|
headers=self.logged_in_headers(),
|
||||||
|
@ -142,26 +142,31 @@ class TestStudyApi(BaseTest):
|
||||||
self.assert_success(api_response)
|
self.assert_success(api_response)
|
||||||
json_data = json.loads(api_response.get_data(as_text=True))
|
json_data = json.loads(api_response.get_data(as_text=True))
|
||||||
|
|
||||||
num_inactive = 0
|
num_incomplete = 0
|
||||||
|
num_abandoned = 0
|
||||||
num_active = 0
|
num_active = 0
|
||||||
|
num_open = 0
|
||||||
|
|
||||||
for study in json_data:
|
for study in json_data:
|
||||||
if study['inactive']:
|
if study['protocol_builder_status'] == 'INCOMPLETE': # One study in user_studies.json is not q_complete
|
||||||
num_inactive += 1
|
num_incomplete += 1
|
||||||
else:
|
if study['protocol_builder_status'] == 'ABANDONED': # One study does not exist in user_studies.json
|
||||||
|
num_abandoned += 1
|
||||||
|
if study['protocol_builder_status'] == 'ACTIVE': # One study is marked complete without HSR Number
|
||||||
num_active += 1
|
num_active += 1
|
||||||
|
if study['protocol_builder_status'] == 'OPEN': # One study is marked complete and has an HSR Number
|
||||||
|
num_open += 1
|
||||||
|
|
||||||
db_studies_after = session.query(StudyModel).all()
|
db_studies_after = session.query(StudyModel).all()
|
||||||
num_db_studies_after = len(db_studies_after)
|
num_db_studies_after = len(db_studies_after)
|
||||||
self.assertGreater(num_db_studies_after, num_db_studies_before)
|
self.assertGreater(num_db_studies_after, num_db_studies_before)
|
||||||
self.assertGreater(num_inactive, 0)
|
self.assertEquals(num_abandoned, 1)
|
||||||
self.assertGreater(num_active, 0)
|
self.assertEquals(num_open, 1)
|
||||||
|
self.assertEquals(num_active, 1)
|
||||||
|
self.assertEquals(num_incomplete, 1)
|
||||||
self.assertEqual(len(json_data), num_db_studies_after)
|
self.assertEqual(len(json_data), num_db_studies_after)
|
||||||
self.assertEqual(num_active + num_inactive, num_db_studies_after)
|
self.assertEqual(num_open + num_active + num_incomplete + num_abandoned, num_db_studies_after)
|
||||||
|
|
||||||
# Assure that the existing study is properly updated.
|
|
||||||
test_study = session.query(StudyModel).filter_by(id=54321).first()
|
|
||||||
self.assertFalse(test_study.inactive)
|
|
||||||
|
|
||||||
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_required_docs') # mock_docs
|
@patch('crc.services.protocol_builder.ProtocolBuilderService.get_required_docs') # mock_docs
|
||||||
def test_get_single_study(self, mock_docs):
|
def test_get_single_study(self, mock_docs):
|
||||||
|
|
|
@ -29,7 +29,7 @@ class TestStudyService(BaseTest):
|
||||||
user = UserModel(uid="dhf8r", email_address="whatever@stuff.com", display_name="Stayathome Smellalots")
|
user = UserModel(uid="dhf8r", email_address="whatever@stuff.com", display_name="Stayathome Smellalots")
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
study = StudyModel(title="My title", protocol_builder_status=ProtocolBuilderStatus.IN_PROCESS, user_uid=user.uid)
|
study = StudyModel(title="My title", protocol_builder_status=ProtocolBuilderStatus.ACTIVE, user_uid=user.uid)
|
||||||
cat = WorkflowSpecCategoryModel(name="cat", display_name="cat", display_order=0)
|
cat = WorkflowSpecCategoryModel(name="cat", display_name="cat", display_order=0)
|
||||||
db.session.add_all([study, cat])
|
db.session.add_all([study, cat])
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
Loading…
Reference in New Issue