From e78cfd89c065f928aa46a44e84637cae786f95fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Oct 2021 18:41:22 +0000 Subject: [PATCH 01/21] Bump babel from 2.9.0 to 2.9.1 in /deploy Bumps [babel](https://github.com/python-babel/babel) from 2.9.0 to 2.9.1. - [Release notes](https://github.com/python-babel/babel/releases) - [Changelog](https://github.com/python-babel/babel/blob/master/CHANGES) - [Commits](https://github.com/python-babel/babel/compare/v2.9.0...v2.9.1) --- updated-dependencies: - dependency-name: babel dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- deploy/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/requirements.txt b/deploy/requirements.txt index 182048dd..95377c21 100644 --- a/deploy/requirements.txt +++ b/deploy/requirements.txt @@ -2,7 +2,7 @@ alabaster==0.7.12 alembic==1.4.3 aniso8601==8.0.0 attrs==20.3.0 -babel==2.9.0 +babel==2.9.1 bcrypt==3.2.0 beautifulsoup4==4.9.3 blinker==1.4 From f88aba1db0836d0daeced8de41b25310303d5a9c Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Tue, 26 Oct 2021 14:29:38 -0400 Subject: [PATCH 02/21] Cleanup validation for get_locatime. Make sure we send the same type of values back for validation. --- crc/scripts/email.py | 5 +++- crc/scripts/get_email_data.py | 23 ++++++++++++++---- crc/scripts/get_localtime.py | 4 +-- tests/data/get_localtime/get_localtime.bpmn | 27 ++++++++++++--------- 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/crc/scripts/email.py b/crc/scripts/email.py index 1520308d..5c35b46b 100644 --- a/crc/scripts/email.py +++ b/crc/scripts/email.py @@ -1,5 +1,6 @@ import sys import traceback +import datetime from crc import app, session from crc.api.common import ApiError @@ -43,7 +44,9 @@ email(subject="My Subject", recipients="user@example.com", attachments=['Study_A subject = self.get_subject(kwargs['subject']) recipients = self.get_email_addresses(kwargs['recipients'], study_id) content, content_html = EmailService().get_rendered_content(task.task_spec.documentation, task.data) - return EmailModel(subject=subject, recipients=recipients, content=content, content_html=content_html) + + email_model = EmailModel(subject=subject, recipients=recipients, content=content, content_html=content_html, timestamp=datetime.datetime.utcnow()) + return EmailModelSchema().dump(email_model) def do_task(self, task, study_id, workflow_id, *args, **kwargs): diff --git a/crc/scripts/get_email_data.py b/crc/scripts/get_email_data.py index c3539823..ba6c2a39 100644 --- a/crc/scripts/get_email_data.py +++ b/crc/scripts/get_email_data.py @@ -2,7 +2,9 @@ from crc.scripts.script import Script from crc.api.common import ApiError from crc import session from crc.models.email import EmailModel, EmailModelSchema -import json +from crc.services.email_service import EmailService + +import datetime class EmailData(Script): @@ -12,11 +14,22 @@ class EmailData(Script): def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): if 'email_id' in kwargs or 'workflow_spec_id' in kwargs: - return True - else: - return False + subject = 'My Test Email' + recipients = 'user@example.com' + content, content_html = EmailService().get_rendered_content(task.task_spec.documentation, task.data) + email_model = EmailModel(subject=subject, + recipients=recipients, + content=content, + content_html=content_html, + timestamp=datetime.datetime.utcnow()) + return EmailModelSchema(many=True).dump([email_model]) - def do_task(self, task, study_id, workflow_id, **kwargs): + else: + raise ApiError.from_task(code='missing_email_id', + message='You must include an email_id or workflow_spec_id with the get_email_data script.', + task=task) + + def do_task(self, task, study_id, workflow_id, *args, **kwargs): email_models = None email_data = None if 'email_id' in kwargs: diff --git a/crc/scripts/get_localtime.py b/crc/scripts/get_localtime.py index c3add55e..c6d120bf 100644 --- a/crc/scripts/get_localtime.py +++ b/crc/scripts/get_localtime.py @@ -1,5 +1,3 @@ -import datetime - from crc.api.common import ApiError from crc.scripts.script import Script @@ -15,7 +13,7 @@ class GetLocaltime(Script): def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): if 'timestamp' in kwargs: - return datetime.datetime.now() + return self.do_task(task, study_id, workflow_id, *args, **kwargs) raise ApiError(code='missing_timestamp', message='You must include a timestamp to convert.') diff --git a/tests/data/get_localtime/get_localtime.bpmn b/tests/data/get_localtime/get_localtime.bpmn index 43a136d5..23ef708f 100644 --- a/tests/data/get_localtime/get_localtime.bpmn +++ b/tests/data/get_localtime/get_localtime.bpmn @@ -15,7 +15,9 @@ This is my email Flow_0lnc9x0 Flow_0gtgzcf - email_model = email(subject='My Email Subject', recipients='user@example.com') + email_model = email(subject='My Email Subject', recipients='user@example.com') +email_data = get_email_data(email_id=email_model['id']) + timestamp = email_model.timestamp @@ -23,7 +25,8 @@ localtime = get_localtime(str(timestamp)) Flow_0gtgzcf Flow_0k1hbif timestamp=email_model.timestamp -localtime = get_localtime(timestamp=timestamp) +localtime = get_localtime(timestamp=timestamp) + # Timestamp @@ -38,21 +41,21 @@ localtime = get_localtime(timestamp=timestamp) - - - - - - - + + + - - - + + + + + + + From a65c104a0b9dba710843f1c1191b45254cac5c11 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Tue, 26 Oct 2021 15:27:24 -0400 Subject: [PATCH 03/21] To validate get_email_data --- crc/scripts/email.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crc/scripts/email.py b/crc/scripts/email.py index 5c35b46b..46e1f2e1 100644 --- a/crc/scripts/email.py +++ b/crc/scripts/email.py @@ -45,7 +45,7 @@ email(subject="My Subject", recipients="user@example.com", attachments=['Study_A recipients = self.get_email_addresses(kwargs['recipients'], study_id) content, content_html = EmailService().get_rendered_content(task.task_spec.documentation, task.data) - email_model = EmailModel(subject=subject, recipients=recipients, content=content, content_html=content_html, timestamp=datetime.datetime.utcnow()) + email_model = EmailModel(id=1, subject=subject, recipients=recipients, content=content, content_html=content_html, timestamp=datetime.datetime.utcnow()) return EmailModelSchema().dump(email_model) def do_task(self, task, study_id, workflow_id, *args, **kwargs): From 7090a22a23c8588ac243959a0a3a12f163e84d35 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Tue, 26 Oct 2021 16:28:50 -0400 Subject: [PATCH 04/21] Remove content_html from the data we return from the email and get_email_data scripts --- crc/models/email.py | 2 +- tests/emails/test_email_script.py | 3 +++ tests/scripts/test_get_email_data.py | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/crc/models/email.py b/crc/models/email.py index cf2bb543..96d3227b 100644 --- a/crc/models/email.py +++ b/crc/models/email.py @@ -26,5 +26,5 @@ class EmailModelSchema(ma.Schema): class Meta: model = EmailModel - fields = ["id", "subject", "sender", "recipients", "cc", "bcc", "content", "content_html", + fields = ["id", "subject", "sender", "recipients", "cc", "bcc", "content", "study_id", "timestamp", "workflow_spec_id"] diff --git a/tests/emails/test_email_script.py b/tests/emails/test_email_script.py index 0cb1d558..216bf58e 100644 --- a/tests/emails/test_email_script.py +++ b/tests/emails/test_email_script.py @@ -51,6 +51,9 @@ class TestEmailScript(BaseTest): # Make sure timestamp is UTC self.assertEqual(db_emails[0].timestamp.tzinfo, datetime.timezone.utc) + # Make sure we remove content_html from the returned email_model + self.assertNotIn('content_html', workflow_api.next_task.data['email_model']) + @patch('crc.services.email_service.EmailService.add_email') def test_email_raises_exception(self, mock_response): self.load_example_data() diff --git a/tests/scripts/test_get_email_data.py b/tests/scripts/test_get_email_data.py index b3b648d6..8b847b90 100644 --- a/tests/scripts/test_get_email_data.py +++ b/tests/scripts/test_get_email_data.py @@ -32,6 +32,8 @@ class TestGetEmailData(BaseTest): self.assertEqual('My Email Subject', email_data[0]['subject']) self.assertEqual('sender@example.com', email_data[0]['sender']) self.assertEqual('[\'joe@example.com\']', email_data[0]['recipients']) + # Make sure we remove content_html from email_data + self.assertNotIn('content_html', email_data[0]) def test_get_email_data_by_workflow_spec_id(self): self.load_example_data() From b0c479420d19bbab887101956e8fb0f86fef29a2 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Wed, 27 Oct 2021 12:19:09 -0400 Subject: [PATCH 05/21] New task_log model with schema, and migration --- crc/models/task_log.py | 23 ++++++++++++ .../versions/a4f87f90cc64_add_log_table.py | 36 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 crc/models/task_log.py create mode 100644 migrations/versions/a4f87f90cc64_add_log_table.py diff --git a/crc/models/task_log.py b/crc/models/task_log.py new file mode 100644 index 00000000..0636c113 --- /dev/null +++ b/crc/models/task_log.py @@ -0,0 +1,23 @@ +from crc import db, ma +from crc.models.study import StudyModel +from crc.models.workflow import WorkflowModel +from sqlalchemy import func + + +class TaskLogModel(db.Model): + __tablename__ = 'task_log' + id = db.Column(db.Integer, primary_key=True) + level = db.Column(db.String) + code = db.Column(db.String) + message = db.Column(db.String) + study_id = db.Column(db.Integer, db.ForeignKey(StudyModel.id), nullable=False) + workflow_id = db.Column(db.Integer, db.ForeignKey(WorkflowModel.id), nullable=False) + task = db.Column(db.String) + timestamp = db.Column(db.DateTime(timezone=True), default=func.now()) + + +class TaskLogModelSchema(ma.Schema): + + class Meta: + model = TaskLogModel + fields = ["id", "level", "code", "message", "study_id", "workflow_id", "timestamp"] diff --git a/migrations/versions/a4f87f90cc64_add_log_table.py b/migrations/versions/a4f87f90cc64_add_log_table.py new file mode 100644 index 00000000..ec981b66 --- /dev/null +++ b/migrations/versions/a4f87f90cc64_add_log_table.py @@ -0,0 +1,36 @@ +"""add log table + +Revision ID: a4f87f90cc64 +Revises: ba6df7e560a1 +Create Date: 2021-10-27 10:54:40.233325 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'a4f87f90cc64' +down_revision = 'ba6df7e560a1' +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table('task_log', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('level', sa.String(), nullable=True), + sa.Column('code', sa.String(), nullable=True), + sa.Column('message', sa.String(), nullable=True), + sa.Column('study_id', sa.Integer(), nullable=False), + sa.Column('workflow_id', sa.Integer(), nullable=False), + sa.Column('task', sa.String(), nullable=True), + sa.Column('timestamp', sa.DateTime(timezone=True), nullable=True), + sa.ForeignKeyConstraint(['study_id'], ['study.id'], ), + sa.ForeignKeyConstraint(['workflow_id'], ['workflow.id'], ), + sa.PrimaryKeyConstraint('id') + ) + + +def downgrade(): + op.drop_table('task_log') From e783da00afc713fc18f5f4299d884ca03b4a61ac Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Wed, 27 Oct 2021 12:21:38 -0400 Subject: [PATCH 06/21] Script to add a log message from a task. Also added `*args` to the definitions for `do_task` and `do_task_validate_only` to the main script file. (These are the methods we overwrite in scripts.) --- crc/scripts/log.py | 53 +++++++++++++++++++++++++++++++++++++++++++ crc/scripts/script.py | 4 ++-- 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 crc/scripts/log.py diff --git a/crc/scripts/log.py b/crc/scripts/log.py new file mode 100644 index 00000000..866cf7a2 --- /dev/null +++ b/crc/scripts/log.py @@ -0,0 +1,53 @@ +from crc import session +from crc.api.common import ApiError +from crc.models.task_log import TaskLogModel, TaskLogModelSchema +from crc.scripts.script import Script + + +class MyScript(Script): + + def get_description(self): + return """Script to log events in a Script Task. + Takes `level`, `code`, and `message` arguments. + Example: + log(level='info', code='missing_info', message='You must include the correct info!') + + Level must be `debug`, `info`, `warning`, `error` or `critical`. + Code is a short string meant for searching the logs. + Message is a more descriptive string, including any info you want to log. + """ + + def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): + pass + + def do_task(self, task, study_id, workflow_id, *args, **kwargs): + if len(args) == 3 or ('level' in kwargs and 'code' in kwargs and 'message' in kwargs): + if 'level' in kwargs: + level = kwargs['level'] + else: + level = args[0] + if 'code' in kwargs: + code = kwargs['code'] + else: + code = args[0] + if 'message' in kwargs: + message = kwargs['message'] + else: + message = args[0] + task_name = task.get_name() + log_model = TaskLogModel(level=level, + code=code, + message=message, + study_id=study_id, + workflow_id=workflow_id, + task=task_name) + session.add(log_model) + session.commit() + + print('do_task') + return TaskLogModelSchema().dump(log_model) + + else: + raise ApiError.from_task(code='missing_arguments', + message='You must include a level, code, and message to log.', + task=task) diff --git a/crc/scripts/script.py b/crc/scripts/script.py index edb3cbf5..556b2a74 100644 --- a/crc/scripts/script.py +++ b/crc/scripts/script.py @@ -13,12 +13,12 @@ class Script(object): raise ApiError("invalid_script", "This script does not supply a description.") - def do_task(self, task, study_id, workflow_id, **kwargs): + def do_task(self, task, study_id, workflow_id, *args, **kwargs): raise ApiError("invalid_script", "This is an internal error. The script you are trying to execute '%s' " % self.__class__.__name__ + "does not properly implement the do_task function.") - def do_task_validate_only(self, task, study_id, workflow_id, **kwargs): + def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): raise ApiError("invalid_script", "This is an internal error. The script you are trying to execute '%s' " % self.__class__.__name__ + "does must provide a validate_only option that mimics the do_task, " + From a3c3a9a75d27e837f526b708c67fbe6eaa3e7fe6 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Wed, 27 Oct 2021 12:22:17 -0400 Subject: [PATCH 07/21] Test and workflow for the new logging script --- tests/data/logging_task/logging_task.bpmn | 54 +++++++++++++++++++++++ tests/scripts/test_logging_script.py | 19 ++++++++ 2 files changed, 73 insertions(+) create mode 100644 tests/data/logging_task/logging_task.bpmn create mode 100644 tests/scripts/test_logging_script.py diff --git a/tests/data/logging_task/logging_task.bpmn b/tests/data/logging_task/logging_task.bpmn new file mode 100644 index 00000000..22193e0b --- /dev/null +++ b/tests/data/logging_task/logging_task.bpmn @@ -0,0 +1,54 @@ + + + + + Flow_1vjxvjd + + + + Flow_1vjxvjd + Flow_1mw0dlv + log_model = log(level='info', code='test_code', message='You forgot to include the correct data.') + + + + # Log Model +{{ log_model }} + + Flow_1mw0dlv + Flow_016ui0e + + + Flow_016ui0e + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/scripts/test_logging_script.py b/tests/scripts/test_logging_script.py new file mode 100644 index 00000000..31879aa8 --- /dev/null +++ b/tests/scripts/test_logging_script.py @@ -0,0 +1,19 @@ +from tests.base_test import BaseTest + +from crc import session +from crc.models.task_log import TaskLogModel + + +class TestLoggingScript(BaseTest): + + def test_logging_script(self): + workflow = self.create_workflow('logging_task') + workflow_api = self.get_workflow_api(workflow) + task = workflow_api.next_task + + log_id = task.data['log_model']['id'] + log_model = session.query(TaskLogModel).filter(TaskLogModel.id == log_id).first() + + self.assertEqual('test_code', log_model.code) + self.assertEqual('info', log_model.level) + self.assertEqual('Activity_LogEvent', log_model.task) From 38fbd08517153444187b378e3f77c897b18b09ff Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Wed, 27 Oct 2021 16:36:48 -0400 Subject: [PATCH 08/21] Renamed file --- tests/scripts/{test_logging_script.py => test_task_logging.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/scripts/{test_logging_script.py => test_task_logging.py} (100%) diff --git a/tests/scripts/test_logging_script.py b/tests/scripts/test_task_logging.py similarity index 100% rename from tests/scripts/test_logging_script.py rename to tests/scripts/test_task_logging.py From c61fad752fe32633089822c3007117737d6768bb Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Wed, 27 Oct 2021 16:38:36 -0400 Subject: [PATCH 09/21] Bug fix --- crc/scripts/log.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crc/scripts/log.py b/crc/scripts/log.py index 866cf7a2..61cbac4a 100644 --- a/crc/scripts/log.py +++ b/crc/scripts/log.py @@ -29,11 +29,11 @@ class MyScript(Script): if 'code' in kwargs: code = kwargs['code'] else: - code = args[0] + code = args[1] if 'message' in kwargs: message = kwargs['message'] else: - message = args[0] + message = args[2] task_name = task.get_name() log_model = TaskLogModel(level=level, code=code, @@ -44,7 +44,6 @@ class MyScript(Script): session.add(log_model) session.commit() - print('do_task') return TaskLogModelSchema().dump(log_model) else: From 6322353c884f38d63d53e048dab3c6724b344c0d Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Wed, 27 Oct 2021 16:39:25 -0400 Subject: [PATCH 10/21] Scripts to retrieve logging information --- crc/scripts/get_logs.py | 38 +++++++++++++++++++++++++++++++ crc/scripts/get_logs_for_study.py | 38 +++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 crc/scripts/get_logs.py create mode 100644 crc/scripts/get_logs_for_study.py diff --git a/crc/scripts/get_logs.py b/crc/scripts/get_logs.py new file mode 100644 index 00000000..8255e94a --- /dev/null +++ b/crc/scripts/get_logs.py @@ -0,0 +1,38 @@ +from crc import session +from crc.api.common import ApiError +from crc.models.task_log import TaskLogModel, TaskLogModelSchema +from crc.scripts.script import Script + + +class GetLogsByWorkflow(Script): + + def get_description(self): + return """Script to retrieve logs for the current workflow. + Accepts an optional `code` argument that is used to filter the DB query. + """ + + def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): + if len(args) == 1 or 'code' in kwargs: + pass + else: + raise ApiError.from_task(code='missing_code', + message='You must include a `code` to use in the search.', + task=task) + + def do_task(self, task, study_id, workflow_id, *args, **kwargs): + code = None + if 'code' in kwargs: + code = kwargs['code'] + elif len(args) > 0: + code = args[0] + if code is not None: + log_models = session.query(TaskLogModel).\ + filter(TaskLogModel.code == code).\ + filter(TaskLogModel.workflow_id == workflow_id).\ + all() + else: + log_models = session.query(TaskLogModel). \ + filter(TaskLogModel.workflow_id == workflow_id). \ + all() + + return TaskLogModelSchema(many=True).dump(log_models) diff --git a/crc/scripts/get_logs_for_study.py b/crc/scripts/get_logs_for_study.py new file mode 100644 index 00000000..d6526741 --- /dev/null +++ b/crc/scripts/get_logs_for_study.py @@ -0,0 +1,38 @@ +from crc import session +from crc.api.common import ApiError +from crc.models.task_log import TaskLogModel, TaskLogModelSchema +from crc.scripts.script import Script + + +class GetLogsByWorkflow(Script): + + def get_description(self): + return """Script to retrieve logs for the current study. + Accepts an optional `code` argument that is used to filter the DB query. + """ + + def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): + if len(args) == 1 or 'code' in kwargs: + pass + else: + raise ApiError.from_task(code='missing_code', + message='You must include a `code` to use in the search.', + task=task) + + def do_task(self, task, study_id, workflow_id, *args, **kwargs): + code = None + if 'code' in kwargs: + code = kwargs['code'] + elif len(args) > 0: + code = args[0] + if code is not None: + log_models = session.query(TaskLogModel).\ + filter(TaskLogModel.code == code).\ + filter(TaskLogModel.study_id == study_id).\ + all() + else: + log_models = session.query(TaskLogModel). \ + filter(TaskLogModel.study_id == study_id). \ + all() + + return TaskLogModelSchema(many=True).dump(log_models) From 629fe6f571db3b1086986676937e2f34be5a9ad3 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Wed, 27 Oct 2021 16:39:50 -0400 Subject: [PATCH 11/21] Tests and workflows --- tests/data/get_logging/get_logging.bpmn | 96 +++++++++++++++++++ .../get_logging_for_study.bpmn | 90 +++++++++++++++++ tests/scripts/test_task_logging.py | 72 +++++++++++++- 3 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 tests/data/get_logging/get_logging.bpmn create mode 100644 tests/data/get_logging_for_study/get_logging_for_study.bpmn diff --git a/tests/data/get_logging/get_logging.bpmn b/tests/data/get_logging/get_logging.bpmn new file mode 100644 index 00000000..d67463a2 --- /dev/null +++ b/tests/data/get_logging/get_logging.bpmn @@ -0,0 +1,96 @@ + + + + + Flow_0d5wpav + + + Flow_0pc42yp + Flow_0n34cdi + log_model_info = log(level='info', code='test_code', message='You forgot to include the correct data.') +log_model_debug = log(level='degug', code='debug_test_code', message='This is my debugging message') + + + # Logging Models Pre +{{ logging_models_pre }} + +# Log Model +{{ log_model }} + +# Logging Models All Post +{{ logging_models_all_post }} + + +# Logging Models Info Post +{{ logging_models_info_post }} + + +# Logging Models Debug Post +{{ logging_models_debug_post }} + Flow_07j4f0v + Flow_016ui0e + + + Flow_016ui0e + + + + Flow_0d5wpav + Flow_0pc42yp + logging_models_pre = get_logs() + + + Flow_0n34cdi + Flow_07j4f0v + logging_models_all_post = get_logs() +logging_models_info_post = get_logs('test_code') +logging_models_debug_post = get_logs('debug_test_code') + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/data/get_logging_for_study/get_logging_for_study.bpmn b/tests/data/get_logging_for_study/get_logging_for_study.bpmn new file mode 100644 index 00000000..e3de15e6 --- /dev/null +++ b/tests/data/get_logging_for_study/get_logging_for_study.bpmn @@ -0,0 +1,90 @@ + + + + + Flow_0bbqksl + + + + # Hello +You may manipulate this in a test, as you see fit + Flow_0bbqksl + Flow_0lh4lq8 + + + + Flow_0lh4lq8 + Flow_10fc3fk + some_text = 'variable' +log('info', 'some_code', 'Some longer message') +log('info', 'some_other_code', 'Another really long message') +log('debug', 'debug_code', f'This message has a { some_text }!') + + + + Flow_10fc3fk + Flow_1dfqchi + workflow_logs = get_logs() +study_logs = get_logs_for_study() + + + + # Display Info + +## Workflow Logs +{{ workflow_logs }} + + +## Study Logs +{{ study_logs }} + Flow_1dfqchi + Flow_0yxmlin + + + Flow_0yxmlin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/scripts/test_task_logging.py b/tests/scripts/test_task_logging.py index 31879aa8..62f8448a 100644 --- a/tests/scripts/test_task_logging.py +++ b/tests/scripts/test_task_logging.py @@ -1,12 +1,17 @@ from tests.base_test import BaseTest from crc import session +from crc.models.api_models import Task from crc.models.task_log import TaskLogModel +from crc.models.study import StudyModel +from crc.scripts.log import MyScript + +import types -class TestLoggingScript(BaseTest): +class TestTaskLogging(BaseTest): - def test_logging_script(self): + def test_add_log(self): workflow = self.create_workflow('logging_task') workflow_api = self.get_workflow_api(workflow) task = workflow_api.next_task @@ -17,3 +22,66 @@ class TestLoggingScript(BaseTest): self.assertEqual('test_code', log_model.code) self.assertEqual('info', log_model.level) self.assertEqual('Activity_LogEvent', log_model.task) + + def test_get_logs(self): + workflow = self.create_workflow('get_logging') + workflow_api = self.get_workflow_api(workflow) + task = workflow_api.next_task + + self.assertEqual(2, len(task.data['logging_models_all_post'])) + self.assertEqual(1, len(task.data['logging_models_info_post'])) + self.assertEqual(1, len(task.data['logging_models_debug_post'])) + self.assertIn(task.data['logging_models_info_post'][0], task.data['logging_models_all_post']) + self.assertIn(task.data['logging_models_debug_post'][0], task.data['logging_models_all_post']) + self.assertEqual('test_code', task.data['logging_models_info_post'][0]['code']) + self.assertEqual('debug_test_code', task.data['logging_models_debug_post'][0]['code']) + + def test_get_logs_for_study(self): + self.load_example_data() + study = session.query(StudyModel).first() + + workflow = self.create_workflow('hello_world', study=study) + workflow_api = self.get_workflow_api(workflow) + task = workflow_api.next_task + + task_model = Task(id=task.id, + name=task.name, + title=task.title, + type=task.type, + state=task.state, + lane=task.lane, + form=task.form, + documentation=task.documentation, + data=task.data, + multi_instance_type=task.multi_instance_type, + multi_instance_count=task.multi_instance_count, + multi_instance_index=task.multi_instance_index, + process_name=task.process_name, + properties=task.properties) + + task_model.get_name = types.MethodType(lambda x: x.name, task_model) + + MyScript().do_task(task_model, study.id, workflow.id, + level='critical', + code='critical_code', + message='This is my critical message.') + + MyScript().do_task(task_model, study.id, workflow.id, + level='debug', + code='debug_code', + message='This is my debug message.') + + # This workflow adds 3 logs + # some_text = 'variable' + # log('info', 'some_code', 'Some longer message') + # log('info', 'some_other_code', 'Another really long message') + # log('debug', 'debug_code', f'This message has a { some_text }!') + workflow = self.create_workflow('get_logging_for_study', study=study) + workflow_api = self.get_workflow_api(workflow) + task = workflow_api.next_task + workflow_api = self.complete_form(workflow, task, {}) + task = workflow_api.next_task + workflow_logs = task.data['workflow_logs'] + study_logs = task.data['study_logs'] + self.assertEqual(3, len(workflow_logs)) + self.assertEqual(5, len(study_logs)) From c76d8230c08528e61c4c4b5f0b2bfa429146f939 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Wed, 27 Oct 2021 17:03:27 -0400 Subject: [PATCH 12/21] Cleaned up validation --- crc/scripts/get_logs.py | 13 +++++++------ crc/scripts/get_logs_for_study.py | 13 +++++++------ crc/scripts/log.py | 16 ++++++++++++++-- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/crc/scripts/get_logs.py b/crc/scripts/get_logs.py index 8255e94a..d8b2af77 100644 --- a/crc/scripts/get_logs.py +++ b/crc/scripts/get_logs.py @@ -12,12 +12,13 @@ class GetLogsByWorkflow(Script): """ def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): - if len(args) == 1 or 'code' in kwargs: - pass - else: - raise ApiError.from_task(code='missing_code', - message='You must include a `code` to use in the search.', - task=task) + log_model = TaskLogModel(level='info', + code='mocked_code', + message='This is my logging message', + study_id=study_id, + workflow_id=workflow_id, + task=task.get_name()) + TaskLogModelSchema(many=True).dump([log_model]) def do_task(self, task, study_id, workflow_id, *args, **kwargs): code = None diff --git a/crc/scripts/get_logs_for_study.py b/crc/scripts/get_logs_for_study.py index d6526741..60e55f25 100644 --- a/crc/scripts/get_logs_for_study.py +++ b/crc/scripts/get_logs_for_study.py @@ -12,12 +12,13 @@ class GetLogsByWorkflow(Script): """ def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): - if len(args) == 1 or 'code' in kwargs: - pass - else: - raise ApiError.from_task(code='missing_code', - message='You must include a `code` to use in the search.', - task=task) + log_model = TaskLogModel(level='info', + code='mocked_code', + message='This is my logging message', + study_id=study_id, + workflow_id=workflow_id, + task=task.get_name()) + return TaskLogModelSchema(many=True).dump([log_model]) def do_task(self, task, study_id, workflow_id, *args, **kwargs): code = None diff --git a/crc/scripts/log.py b/crc/scripts/log.py index 61cbac4a..6da1df38 100644 --- a/crc/scripts/log.py +++ b/crc/scripts/log.py @@ -13,12 +13,24 @@ class MyScript(Script): log(level='info', code='missing_info', message='You must include the correct info!') Level must be `debug`, `info`, `warning`, `error` or `critical`. - Code is a short string meant for searching the logs. + Code is a short string meant for searching the logs. By convention, it is lower case with underscores. Message is a more descriptive string, including any info you want to log. """ def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): - pass + if len(args) == 3 or ('level' in kwargs and 'code' in kwargs and 'message' in kwargs): + log_model = TaskLogModel(level='info', + code='mocked_code', + message='This is my logging message', + study_id=study_id, + workflow_id=workflow_id, + task=task.get_name()) + return TaskLogModelSchema().dump(log_model) + else: + raise ApiError.from_task(code='missing_arguments', + message='You must include a level, code, and message to log.', + task=task) + def do_task(self, task, study_id, workflow_id, *args, **kwargs): if len(args) == 3 or ('level' in kwargs and 'code' in kwargs and 'message' in kwargs): From 14593d54ef0cdcab31ac91d955441114eb468548 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Wed, 27 Oct 2021 17:03:46 -0400 Subject: [PATCH 13/21] Added tests for validation --- tests/scripts/test_task_logging.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/scripts/test_task_logging.py b/tests/scripts/test_task_logging.py index 62f8448a..ec416d9b 100644 --- a/tests/scripts/test_task_logging.py +++ b/tests/scripts/test_task_logging.py @@ -11,6 +11,12 @@ import types class TestTaskLogging(BaseTest): + def test_logging_validation(self): + self.load_example_data() + spec_model = self.load_test_spec('logging_task') + rv = self.app.get('/v1.0/workflow-specification/%s/validate' % spec_model.id, headers=self.logged_in_headers()) + self.assertEqual([], rv.json) + def test_add_log(self): workflow = self.create_workflow('logging_task') workflow_api = self.get_workflow_api(workflow) @@ -23,6 +29,12 @@ class TestTaskLogging(BaseTest): self.assertEqual('info', log_model.level) self.assertEqual('Activity_LogEvent', log_model.task) + def test_get_logging_validation(self): + self.load_example_data() + spec_model = self.load_test_spec('get_logging') + rv = self.app.get('/v1.0/workflow-specification/%s/validate' % spec_model.id, headers=self.logged_in_headers()) + self.assertEqual([], rv.json) + def test_get_logs(self): workflow = self.create_workflow('get_logging') workflow_api = self.get_workflow_api(workflow) From 81bb0013c8bad3290d3b4f85f79fa1f68dfacdb1 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Wed, 27 Oct 2021 17:15:49 -0400 Subject: [PATCH 14/21] Doh --- crc/scripts/log.py | 2 +- tests/scripts/test_task_logging.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crc/scripts/log.py b/crc/scripts/log.py index 6da1df38..3f33b603 100644 --- a/crc/scripts/log.py +++ b/crc/scripts/log.py @@ -4,7 +4,7 @@ from crc.models.task_log import TaskLogModel, TaskLogModelSchema from crc.scripts.script import Script -class MyScript(Script): +class TaskLog(Script): def get_description(self): return """Script to log events in a Script Task. diff --git a/tests/scripts/test_task_logging.py b/tests/scripts/test_task_logging.py index ec416d9b..1e348165 100644 --- a/tests/scripts/test_task_logging.py +++ b/tests/scripts/test_task_logging.py @@ -4,7 +4,7 @@ from crc import session from crc.models.api_models import Task from crc.models.task_log import TaskLogModel from crc.models.study import StudyModel -from crc.scripts.log import MyScript +from crc.scripts.log import TaskLog import types @@ -73,12 +73,12 @@ class TestTaskLogging(BaseTest): task_model.get_name = types.MethodType(lambda x: x.name, task_model) - MyScript().do_task(task_model, study.id, workflow.id, + TaskLog().do_task(task_model, study.id, workflow.id, level='critical', code='critical_code', message='This is my critical message.') - MyScript().do_task(task_model, study.id, workflow.id, + TaskLog().do_task(task_model, study.id, workflow.id, level='debug', code='debug_code', message='This is my debug message.') From 5e569b9167bba57c524aaa9525ae5827439febf0 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Wed, 27 Oct 2021 17:19:27 -0400 Subject: [PATCH 15/21] Unused imports --- crc/scripts/get_logs.py | 1 - crc/scripts/get_logs_for_study.py | 1 - 2 files changed, 2 deletions(-) diff --git a/crc/scripts/get_logs.py b/crc/scripts/get_logs.py index d8b2af77..53e3d667 100644 --- a/crc/scripts/get_logs.py +++ b/crc/scripts/get_logs.py @@ -1,5 +1,4 @@ from crc import session -from crc.api.common import ApiError from crc.models.task_log import TaskLogModel, TaskLogModelSchema from crc.scripts.script import Script diff --git a/crc/scripts/get_logs_for_study.py b/crc/scripts/get_logs_for_study.py index 60e55f25..f02a25ed 100644 --- a/crc/scripts/get_logs_for_study.py +++ b/crc/scripts/get_logs_for_study.py @@ -1,5 +1,4 @@ from crc import session -from crc.api.common import ApiError from crc.models.task_log import TaskLogModel, TaskLogModelSchema from crc.scripts.script import Script From 7722b89faacf6157d29b69fa5ff2db08957635c2 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Fri, 29 Oct 2021 11:52:27 -0400 Subject: [PATCH 16/21] If a file was archived, users could not update the file. In some situations, this kept users from re-uploading files that were previously deleted. We now allow an archived file to be updated and unarchived. --- crc/services/file_service.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/crc/services/file_service.py b/crc/services/file_service.py index aea6ba61..6a148b68 100644 --- a/crc/services/file_service.py +++ b/crc/services/file_service.py @@ -45,13 +45,15 @@ class FileService(object): def add_workflow_spec_file(workflow_spec: WorkflowSpecModel, name, content_type, binary_data, primary=False, is_status=False): """Create a new file and associate it with a workflow spec.""" - # Raise ApiError if the file already exists - if session.query(FileModel)\ + file_model = session.query(FileModel)\ .filter(FileModel.workflow_spec_id == workflow_spec.id)\ - .filter(FileModel.name == name).first(): + .filter(FileModel.name == name).first() - raise ApiError(code="Duplicate File", - message='If you want to replace the file, use the update mechanism.') + if file_model: + if not file_model.archived: + # Raise ApiError if the file already exists and is not archived + raise ApiError(code="duplicate_file", + message='If you want to replace the file, use the update mechanism.') else: file_model = FileModel( workflow_spec_id=workflow_spec.id, @@ -60,7 +62,7 @@ class FileService(object): is_status=is_status, ) - return FileService.update_file(file_model, binary_data, content_type) + return FileService.update_file(file_model, binary_data, content_type) From 85b4c4e89e678883785105f70858589a3b924f99 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Fri, 29 Oct 2021 11:53:03 -0400 Subject: [PATCH 17/21] Test for uploading new version of archived file --- tests/files/test_files_api.py | 49 ++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/tests/files/test_files_api.py b/tests/files/test_files_api.py index 5ffd124b..43bd3d0c 100644 --- a/tests/files/test_files_api.py +++ b/tests/files/test_files_api.py @@ -5,7 +5,7 @@ import os from tests.base_test import BaseTest from crc import session, db, app -from crc.models.file import FileModel, FileType, FileSchema, FileModelSchema +from crc.models.file import FileModel, FileType, FileModelSchema, FileDataModel from crc.models.workflow import WorkflowSpecModel from crc.services.file_service import FileService from crc.services.workflow_processor import WorkflowProcessor @@ -13,6 +13,8 @@ from crc.models.data_store import DataStoreModel from crc.services.document_service import DocumentService from example_data import ExampleDataLoader +from sqlalchemy import desc + class TestFilesApi(BaseTest): @@ -376,3 +378,48 @@ class TestFilesApi(BaseTest): json_data = json.loads(rv.get_data(as_text=True)) self.assertTrue(json_data['primary']) self.assertIsNotNone(json_data['primary_process_id']) + + def test_file_upload_with_previous_name(self): + self.load_example_data() + workflow_spec_model = session.query(WorkflowSpecModel).first() + + # Add file + data = {'file': (io.BytesIO(b'asdf'), 'test_file.xlsx')} + rv = self.app.post('/v1.0/file?workflow_spec_id=%s' % workflow_spec_model.id, + data=data, + follow_redirects=True, + content_type='multipart/form-data', + headers=self.logged_in_headers()) + + self.assert_success(rv) + file_json = json.loads(rv.get_data(as_text=True)) + file_id = file_json['id'] + + # Set file to archived + file_model = session.query(FileModel).filter_by(id=file_id).first() + file_model.archived = True + session.commit() + + # Assert we have the correct file data and the file is archived + file_data_model = session.query(FileDataModel).filter(FileDataModel.file_model_id == file_model.id).first() + self.assertEqual(b'asdf', file_data_model.data) + file_model = session.query(FileModel).filter_by(id=file_model.id).first() + self.assertEqual(True, file_model.archived) + + # Upload file with same name + data = {'file': (io.BytesIO(b'xyzpdq'), 'test_file.xlsx')} + rv = self.app.post('/v1.0/file?workflow_spec_id=%s' % workflow_spec_model.id, + data=data, + follow_redirects=True, + content_type='multipart/form-data', + headers=self.logged_in_headers()) + + self.assert_success(rv) + file_json = json.loads(rv.get_data(as_text=True)) + file_id = file_json['id'] + + # Assert we have the correct file data and the file is *not* archived + file_data_model = session.query(FileDataModel).filter(FileDataModel.file_model_id == file_id).order_by(desc(FileDataModel.version)).first() + self.assertEqual(b'xyzpdq', file_data_model.data) + file_model = session.query(FileModel).filter_by(id=file_id).first() + self.assertEqual(False, file_model.archived) From 1866e6bba948aacd171b00561a0948e2cf8fc5b1 Mon Sep 17 00:00:00 2001 From: alicia pritchett Date: Mon, 1 Nov 2021 14:35:30 -0400 Subject: [PATCH 18/21] Fixes validation for #518 Well really what this does is give every field a default value of None. what that does is allow you to use value expressions even without a given default value. this part in the backend fixes validation. --- crc/services/workflow_service.py | 4 ++++ tests/workflow/test_workflow_value_expression.py | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/crc/services/workflow_service.py b/crc/services/workflow_service.py index 54150ea2..09409445 100755 --- a/crc/services/workflow_service.py +++ b/crc/services/workflow_service.py @@ -363,6 +363,10 @@ class WorkflowService(object): def evaluate_property(property_name, field, task): expression = field.get_property(property_name) data = task.data + # If there's a field key with no initial value, give it one (None) + for field in task.task_spec.form.fields: + if field.id not in data: + data[field.id] = None if field.has_property(Task.FIELD_PROP_REPEAT): # Then you must evaluate the expression based on the data within the group, if that data exists. # There may not be data available in the group, if no groups where added diff --git a/tests/workflow/test_workflow_value_expression.py b/tests/workflow/test_workflow_value_expression.py index 1be42451..af8f8426 100644 --- a/tests/workflow/test_workflow_value_expression.py +++ b/tests/workflow/test_workflow_value_expression.py @@ -3,6 +3,7 @@ from tests.base_test import BaseTest class TestValueExpression(BaseTest): + # If there is no default value, a value of 'None' should be given. def test_value_expression_no_default(self): workflow = self.create_workflow('test_value_expression') @@ -14,7 +15,7 @@ class TestValueExpression(BaseTest): workflow_api = self.get_workflow_api(workflow) second_task = workflow_api.next_task self.assertEqual('', second_task.data['value_expression_value']) - self.assertNotIn('color', second_task.data) + self.assertIn('color', second_task.data) From 47ead5ab4524b55f650262001dd16bf041889456 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Tue, 2 Nov 2021 10:00:41 -0400 Subject: [PATCH 19/21] Fixed validation bug. There is no element documentation available in the task, so we mock up the email content. --- crc/scripts/get_email_data.py | 3 ++- tests/scripts/test_get_email_data.py | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/crc/scripts/get_email_data.py b/crc/scripts/get_email_data.py index ba6c2a39..4f787ddb 100644 --- a/crc/scripts/get_email_data.py +++ b/crc/scripts/get_email_data.py @@ -16,7 +16,8 @@ class EmailData(Script): if 'email_id' in kwargs or 'workflow_spec_id' in kwargs: subject = 'My Test Email' recipients = 'user@example.com' - content, content_html = EmailService().get_rendered_content(task.task_spec.documentation, task.data) + content = "Hello" + content_html = "

Hello

" email_model = EmailModel(subject=subject, recipients=recipients, content=content, diff --git a/tests/scripts/test_get_email_data.py b/tests/scripts/test_get_email_data.py index 8b847b90..b9e45a37 100644 --- a/tests/scripts/test_get_email_data.py +++ b/tests/scripts/test_get_email_data.py @@ -6,6 +6,12 @@ from crc.services.email_service import EmailService class TestGetEmailData(BaseTest): + def test_email_data_validation(self): + self.load_example_data() + spec_model = self.load_test_spec('get_email_data') + rv = self.app.get('/v1.0/workflow-specification/%s/validate' % spec_model.id, headers=self.logged_in_headers()) + self.assertEqual([], rv.json) + def test_get_email_data_by_email_id(self): self.load_example_data() workflow = self.create_workflow('get_email_data') From 1f9c80d70d15f064a3d802f1a3870fac9c232af5 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Wed, 3 Nov 2021 08:36:24 -0400 Subject: [PATCH 20/21] Added ordered arguments to the get_localtime script Modified the workflow so the first task is a form with booleans that allows me to test all the permutations. Modified tests to cover all the permutations for calling the script --- crc/scripts/get_localtime.py | 11 +++- tests/data/get_localtime/get_localtime.bpmn | 68 ++++++++++++++------- tests/scripts/test_get_localtime.py | 47 +++++++++++++- 3 files changed, 98 insertions(+), 28 deletions(-) diff --git a/crc/scripts/get_localtime.py b/crc/scripts/get_localtime.py index c6d120bf..edc912e0 100644 --- a/crc/scripts/get_localtime.py +++ b/crc/scripts/get_localtime.py @@ -12,16 +12,21 @@ class GetLocaltime(Script): Defaults to US/Eastern""" def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): - if 'timestamp' in kwargs: + if len(args) > 0 or 'timestamp' in kwargs: return self.do_task(task, study_id, workflow_id, *args, **kwargs) raise ApiError(code='missing_timestamp', message='You must include a timestamp to convert.') def do_task(self, task, study_id, workflow_id, *args, **kwargs): - if 'timestamp' in kwargs: - timestamp = kwargs['timestamp'] + if len(args) > 0 or 'timestamp' in kwargs: + if 'timestamp' in kwargs: + timestamp = kwargs['timestamp'] + else: + timestamp = args[0] if 'timezone' in kwargs: timezone = kwargs['timezone'] + elif len(args) > 1: + timezone = args[1] else: timezone = 'US/Eastern' parsed_timestamp = dateparser.parse(timestamp) diff --git a/tests/data/get_localtime/get_localtime.bpmn b/tests/data/get_localtime/get_localtime.bpmn index 23ef708f..e8d91c63 100644 --- a/tests/data/get_localtime/get_localtime.bpmn +++ b/tests/data/get_localtime/get_localtime.bpmn @@ -1,43 +1,67 @@ - + Flow_0lnc9x0 - - - + + + Flow_0kgtoh1 - - This is my email - Flow_0lnc9x0 - Flow_0gtgzcf - email_model = email(subject='My Email Subject', recipients='user@example.com') -email_data = get_email_data(email_id=email_model['id']) - - - + timestamp = email_model.timestamp localtime = get_localtime(str(timestamp)) Flow_0gtgzcf Flow_0k1hbif - timestamp=email_model.timestamp -localtime = get_localtime(timestamp=timestamp) - + if with_timestamp: + if with_timezone: + localtime_with = get_localtime(timestamp=timestamp, timezone=timezone) + localtime_without = get_localtime(timestamp, timezone) + else: + localtime_with = get_localtime(timestamp=timestamp) + localtime_without = get_localtime(timestamp) +else: + localtime = get_localtime() # Timestamp {{ timestamp }} +# Timezone +{{ timezone }} -# Localtime -{{ localtime }} +# Localtime With +{{ localtime_with }} + +# Localtime Without +{{ localtime_without }}
Flow_0k1hbif Flow_0kgtoh1
+ + This is my email + + + + + + + + + + + + + + + + + Flow_0lnc9x0 + Flow_0gtgzcf + @@ -63,15 +87,15 @@ localtime = get_localtime(timestamp=timestamp) - - - - + + + + diff --git a/tests/scripts/test_get_localtime.py b/tests/scripts/test_get_localtime.py index 4abac7ea..de54a982 100644 --- a/tests/scripts/test_get_localtime.py +++ b/tests/scripts/test_get_localtime.py @@ -1,6 +1,7 @@ from tests.base_test import BaseTest from crc.scripts.get_localtime import GetLocaltime import dateparser +import datetime class TestGetLocaltime(BaseTest): @@ -8,11 +9,51 @@ class TestGetLocaltime(BaseTest): def test_get_localtime(self): self.load_example_data() + timestamp = datetime.datetime.utcnow() workflow = self.create_workflow('get_localtime') + workflow_api = self.get_workflow_api(workflow) task = workflow_api.next_task - timestamp = task.data['timestamp'] - localtime = task.data['localtime'] + workflow_api = self.complete_form(workflow, task, {'with_timestamp': True, + 'with_timezone': False, + 'timestamp': str(timestamp)}) + task = workflow_api.next_task - self.assertEqual(dateparser.parse(localtime), GetLocaltime().do_task(None, None, None, timestamp=timestamp)) + # The workflow calls get_localtime twice, once with named arguments and once without + localtime_with = task.data['localtime_with'] + localtime_without = task.data['localtime_without'] + + self.assertEqual(dateparser.parse(localtime_with), GetLocaltime().do_task(None, None, None, timestamp=str(timestamp))) + self.assertEqual(dateparser.parse(localtime_without), GetLocaltime().do_task(None, None, None, str(timestamp))) + + def test_get_localtime_with_timezone(self): + self.load_example_data() + + timestamp = datetime.datetime.utcnow() + workflow = self.create_workflow('get_localtime') + + workflow_api = self.get_workflow_api(workflow) + task = workflow_api.next_task + + workflow_api = self.complete_form(workflow, task, {'with_timestamp': True, + 'with_timezone': True, + 'timestamp': str(timestamp), + 'timezone': 'US/Eastern'}) + task = workflow_api.next_task + + # The workflow calls get_localtime twice, once with named arguments and once without + localtime_with = task.data['localtime_with'] + localtime_without = task.data['localtime_without'] + + self.assertEqual(dateparser.parse(localtime_with), GetLocaltime().do_task(None, None, None, timestamp=str(timestamp), timezone='US/Eastern')) + self.assertEqual(dateparser.parse(localtime_without), GetLocaltime().do_task(None, None, None, str(timestamp), 'US/Eastern')) + + def test_get_localtime_no_timestamp(self): + workflow = self.create_workflow('get_localtime') + + workflow_api = self.get_workflow_api(workflow) + task = workflow_api.next_task + + with self.assertRaises(AssertionError): + self.complete_form(workflow, task, {'with_timestamp': False, 'with_timezone': False}) From 9376d3deaf35a43a6f1bbd8aa59a893c7f0644b5 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 4 Nov 2021 13:32:57 -0400 Subject: [PATCH 21/21] Don't error out trying to send an error about invalid review types. --- crc/api/study.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crc/api/study.py b/crc/api/study.py index bbdcad01..593895a1 100644 --- a/crc/api/study.py +++ b/crc/api/study.py @@ -102,7 +102,7 @@ def user_studies(): if len(studies) == 0: studies = StudyService().get_studies_for_user(user, include_invalid=True) if len(studies) > 0: - message = f"All studies associated with User: {user.display_name} failed study validation" + message = f"All studies associated with User: {user.uid} failed study validation" raise ApiError(code="study_integrity_error", message=message) results = StudySchema(many=True).dump(studies)