diff --git a/crc/models/study.py b/crc/models/study.py index 147ce4f6..cae78fbb 100644 --- a/crc/models/study.py +++ b/crc/models/study.py @@ -13,6 +13,7 @@ from crc.models.file import FileModel, SimpleFileSchema, FileSchema from crc.models.protocol_builder import ProtocolBuilderStatus, ProtocolBuilderStudy from crc.models.workflow import WorkflowSpecCategoryModel, WorkflowState, WorkflowStatus, WorkflowSpecModel, \ WorkflowModel +from crc.services.user_service import UserService class StudyStatus(enum.Enum): @@ -50,7 +51,7 @@ class StudyModel(db.Model): on_hold = db.Column(db.Boolean, default=False) enrollment_date = db.Column(db.DateTime(timezone=True), nullable=True) # events = db.relationship("TaskEventModel") - events_history = db.relationship("StudyEvent") + events_history = db.relationship("StudyEvent", cascade="all, delete, delete-orphan") def update_from_protocol_builder(self, pbs: ProtocolBuilderStudy): self.hsr_number = pbs.HSRNUMBER @@ -76,6 +77,7 @@ class StudyEvent(db.Model): status = db.Column(db.Enum(StudyStatus)) comment = db.Column(db.String, default='') event_type = db.Column(db.Enum(StudyEventType)) + user_uid = db.Column(db.String, db.ForeignKey('user.uid'), nullable=True) class WorkflowMetadata(object): @@ -190,7 +192,8 @@ class Study(object): study=study_model, status=status, comment='' if not hasattr(self, 'comment') else self.comment, - event_type=StudyEventType.user + event_type=StudyEventType.user, + user_uid=UserService.current_user().uid if UserService.has_user() else None, ) db.session.add(study_event) db.session.commit() diff --git a/crc/services/study_service.py b/crc/services/study_service.py index 19e67061..a5fc0b64 100644 --- a/crc/services/study_service.py +++ b/crc/services/study_service.py @@ -12,7 +12,7 @@ from crc.api.common import ApiError from crc.models.file import FileModel, FileModelSchema, File from crc.models.ldap import LdapSchema from crc.models.protocol_builder import ProtocolBuilderStudy, ProtocolBuilderStatus -from crc.models.study import StudyModel, Study, StudyStatus, Category, WorkflowMetadata +from crc.models.study import StudyModel, Study, StudyStatus, Category, WorkflowMetadata, StudyEvent from crc.models.task_event import TaskEventModel, TaskEvent from crc.models.workflow import WorkflowSpecCategoryModel, WorkflowModel, WorkflowSpecModel, WorkflowState, \ WorkflowStatus @@ -78,9 +78,11 @@ class StudyService(object): @staticmethod def delete_study(study_id): session.query(TaskEventModel).filter_by(study_id=study_id).delete() + # session.query(StudyEvent).filter_by(study_id=study_id).delete() for workflow in session.query(WorkflowModel).filter_by(study_id=study_id): StudyService.delete_workflow(workflow) - session.query(StudyModel).filter_by(id=study_id).delete() + study = session.query(StudyModel).filter_by(id=study_id).first() + session.delete(study) session.commit() @staticmethod diff --git a/migrations/versions/59a71fb532cb_.py b/migrations/versions/69081f1ff387_.py similarity index 73% rename from migrations/versions/59a71fb532cb_.py rename to migrations/versions/69081f1ff387_.py index 576bb11f..842174f4 100644 --- a/migrations/versions/59a71fb532cb_.py +++ b/migrations/versions/69081f1ff387_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: 59a71fb532cb +Revision ID: 69081f1ff387 Revises: 1c3f88dbccc3 -Create Date: 2020-08-05 19:45:53.039959 +Create Date: 2020-08-12 09:58:36.886096 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '59a71fb532cb' +revision = '69081f1ff387' down_revision = '1c3f88dbccc3' branch_labels = None depends_on = None @@ -22,10 +22,12 @@ def upgrade(): sa.Column('id', sa.Integer(), nullable=False), sa.Column('study_id', sa.Integer(), nullable=False), sa.Column('create_date', sa.DateTime(timezone=True), nullable=True), - sa.Column('status', sa.Enum('in_progress', 'hold', 'open_for_enrollment', 'abandoned', name='studyeventstatus'), nullable=True), + sa.Column('status', sa.Enum('in_progress', 'hold', 'open_for_enrollment', 'abandoned', name='studystatusenum'), nullable=True), sa.Column('comment', sa.String(), nullable=True), sa.Column('event_type', sa.Enum('user', 'automatic', name='studyeventtype'), nullable=True), + sa.Column('user_uid', sa.String(), nullable=True), sa.ForeignKeyConstraint(['study_id'], ['study.id'], ), + sa.ForeignKeyConstraint(['user_uid'], ['user.uid'], ), sa.PrimaryKeyConstraint('id') ) # ### end Alembic commands ### @@ -34,4 +36,6 @@ def upgrade(): def downgrade(): # ### commands auto generated by Alembic - please adjust! ### op.drop_table('study_event') + op.execute('drop type studystatusenum') + op.execute('drop type studyeventtype') # ### end Alembic commands ### diff --git a/tests/study/test_study_api.py b/tests/study/test_study_api.py index d01cc3f1..fce7a6e1 100644 --- a/tests/study/test_study_api.py +++ b/tests/study/test_study_api.py @@ -11,7 +11,7 @@ from crc.models.protocol_builder import ProtocolBuilderStatus, \ ProtocolBuilderStudySchema from crc.models.approval import ApprovalStatus from crc.models.task_event import TaskEventModel -from crc.models.study import StudyEvent, StudyModel, StudySchema, StudyStatus +from crc.models.study import StudyEvent, StudyModel, StudySchema, StudyStatus, StudyEventType from crc.models.workflow import WorkflowSpecModel, WorkflowModel from crc.services.file_service import FileService from crc.services.workflow_processor import WorkflowProcessor @@ -150,10 +150,11 @@ class TestStudyApi(BaseTest): self.assertEqual(study.status.value, json_data['status']) # Making sure events history is being properly recorded - study_events = session.query(StudyEvent) - self.assertEqual(study_events.count(), 1) - self.assertEqual(study_events.first().status, StudyStatus.in_progress) - self.assertEqual(study_events.first().comment, update_comment) + study_event = session.query(StudyEvent).first() + self.assertIsNotNone(study_event) + self.assertEqual(study_event.status, StudyStatus.in_progress) + self.assertEqual(study_event.comment, update_comment) + self.assertEqual(study_event.user_uid, self.test_uid) @patch('crc.services.protocol_builder.ProtocolBuilderService.get_investigators') # mock_studies @patch('crc.services.protocol_builder.ProtocolBuilderService.get_required_docs') # mock_docs @@ -253,8 +254,15 @@ class TestStudyApi(BaseTest): def test_delete_study_with_workflow_and_status(self): self.load_example_data() workflow = session.query(WorkflowModel).first() + stats1 = StudyEvent( + study_id=workflow.study_id, + status=StudyStatus.in_progress, + comment='Some study status change event', + event_type=StudyEventType.user, + user_uid=self.users[0]['uid'], + ) stats2 = TaskEventModel(study_id=workflow.study_id, workflow_id=workflow.id, user_uid=self.users[0]['uid']) - session.add(stats2) + session.add_all([stats1, stats2]) session.commit() rv = self.app.delete('/v1.0/study/%i' % workflow.study_id, headers=self.logged_in_headers()) self.assert_success(rv)