Merge branch 'dev' into file-refactor-705

This commit is contained in:
mike cullerton 2022-04-14 11:40:08 -04:00
commit b304b4725b
20 changed files with 317 additions and 311 deletions

View File

@ -86,9 +86,12 @@ GIT_REMOTE_SERVER = environ.get('GIT_REMOTE_SERVER', None) # example: 'github.c
GIT_REMOTE_PATH = environ.get('GIT_REMOTE_PATH', None) # example: 'sartography/crconnect-workflow-specs
GIT_BRANCH = environ.get('GIT_BRANCH', None) # example: 'main'
GIT_MERGE_BRANCH = environ.get('GIT_MERGE_BRANCH', None) # Example: 'staging'
GIT_USER_NAME = environ.get('GIT_USER_NAME', None)
GIT_USER_PASS = environ.get('GIT_USER_PASS', None)
GIT_DISPLAY_PUSH = environ.get('GIT_DISPLAY_PUSH', False)
GIT_DISPLAY_MERGE = environ.get('GIT_DISPLAY_MERGE', False)
# Email configuration
DEFAULT_SENDER = 'crconnect2@virginia.edu'
FALLBACK_EMAILS = ['askresearch@virginia.edu', 'sartographysupport@googlegroups.com']

View File

@ -0,0 +1,75 @@
from crc.api.common import ApiError
from crc.scripts.script import Script
from crc.services.data_store_service import DataStoreBase
from flask import g
class ScriptTemplate(Script):
script_name = None
data_store_args = None
data_store_type = None
data_store_file_id = None
data_store_study_id = None
data_store_user_id = None
def set_args(self, study_id, **kwargs):
self.validate_kw_args(**kwargs)
self.data_store_args = [kwargs['key']]
if 'default' in kwargs.keys():
self.data_store_args.append(kwargs['default'])
self.data_store_type = kwargs['type']
if self.data_store_type == 'file':
try:
file_id = int(kwargs['file_id'])
except Exception:
raise ApiError("invalid_file_id",
f"The file_id must be an integer. You passed {kwargs['file_id']}.")
self.script_name = 'file_data_get'
self.data_store_file_id = file_id
self.data_store_study_id = None
self.data_store_user_id = None
elif self.data_store_type == 'study':
self.script_name = 'study_data_get'
self.data_store_study_id = study_id
self.data_store_file_id = None
self.data_store_user_id = None
elif self.data_store_type == 'user':
self.script_name = 'user_data_get'
self.data_store_user_id = g.user.uid
self.data_store_file_id = None
self.data_store_study_id = None
def get_description(self):
return """Returns a value from the data store. Requires 2 keyword arguments; `type` and `key`.
Type is one of `file`, `study`, or `user`.
Key is the key of the record you want returned.
If type is `file`, then the script expects a third keyword argument of `file_id`."""
def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
self.set_args(study_id, **kwargs)
result = DataStoreBase().get_validate_common(self.script_name,
self.data_store_study_id,
self.data_store_user_id,
self.data_store_file_id,
*self.data_store_args)
return result
def do_task(self, task, study_id, workflow_id, *args, **kwargs):
self.set_args(study_id, **kwargs)
result = DataStoreBase().get_data_common(self.data_store_study_id,
self.data_store_user_id,
self.script_name,
self.data_store_file_id,
*self.data_store_args)
return result
@staticmethod
def validate_kw_args(**kwargs):
if 'type' not in kwargs or 'key' not in kwargs:
raise ApiError(code='missing_arguments',
message='The data_store_set script requires 2 keyword arguments; `type` and `key`.')
if kwargs['type'] == 'file' and 'file_id' not in kwargs:
raise ApiError(code='missing_arguments',
message='If `type` is `file`, the data_store_set script requires a third keyword argument of `file_id`.')
return True

View File

@ -0,0 +1,89 @@
from crc.api.common import ApiError
from crc.scripts.script import Script
from crc.services.data_store_service import DataStoreBase
from crc.services.document_service import DocumentService
from crc.services.user_file_service import UserFileService
from flask import g
class DataStoreSet(Script):
script_name = None
data_store_args = None
data_store_type = None
data_store_file_id = None
data_store_study_id = None
data_store_user_id = None
def set_args(self, study_id, **kwargs):
self.validate_kw_args(**kwargs)
self.data_store_args = [kwargs['key'], kwargs['value']]
self.data_store_type = kwargs['type']
if self.data_store_type == 'file':
try:
file_id = int(kwargs['file_id'])
except Exception:
raise ApiError("invalid_file_id",
f"The file_id must be an integer. You passed {kwargs['file_id']}.")
self.script_name = 'file_data_set'
self.data_store_file_id = file_id
self.data_store_study_id = None
self.data_store_user_id = None
elif self.data_store_type == 'study':
self.script_name = 'study_data_set'
self.data_store_study_id = study_id
self.data_store_file_id = None
self.data_store_user_id = None
elif self.data_store_type == 'user':
self.script_name = 'user_data_set'
self.data_store_user_id = g.user.uid
self.data_store_file_id = None
self.data_store_study_id = None
def get_description(self):
return """Sets a data store. Takes 3 mandatory keyword arguments; `type`, `key`, and `value`.
Type is one of `file`, `study`, or `user`.
Key and value are defined by the user.
If type is `file`, then the script expects a fourth keyword argument of `file_id`."""
def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
self.set_args(study_id, **kwargs)
result = DataStoreBase().set_validate_common(task.id,
self.data_store_study_id,
workflow_id,
self.script_name,
self.data_store_user_id,
self.data_store_file_id,
*self.data_store_args)
return result
def do_task(self, task, study_id, workflow_id, *args, **kwargs):
self.set_args(study_id, **kwargs)
if self.data_store_type == 'file' and self.data_store_args[0] == 'irb_code':
irb_doc_code = kwargs['value']
UserFileService.update_irb_code(self.data_store_file_id, irb_doc_code)
return DataStoreBase().set_data_common(task.id,
self.data_store_study_id,
self.data_store_user_id,
workflow_id,
self.script_name,
self.data_store_file_id,
*self.data_store_args)
@staticmethod
def validate_kw_args(**kwargs):
if 'type' not in kwargs or 'key' not in kwargs or 'value' not in kwargs:
raise ApiError(code='missing_arguments',
message='The data_store_set script requires 3 keyword arguments; `type`, `key`, and `value`.')
if kwargs['type'] == 'file' and 'file_id' not in kwargs:
raise ApiError(code='missing_arguments',
message='If `type` is `file`, the data_store_set script requires a fourth keyword argument of `file_id`.')
if kwargs['type'] == 'file' \
and kwargs['key'] == 'irb_code' \
and not DocumentService.is_allowed_document(kwargs.get('value')):
raise ApiError(code="invalid_form_field_key",
message="When setting an irb_code, the value must be a valid document code. "
f"The value {kwargs.get('value')} is not a valid document code.")
return True

View File

@ -1,39 +0,0 @@
from crc.api.common import ApiError
from crc.services.data_store_service import DataStoreBase
from crc.scripts.script import Script
class FileDataGet(Script, DataStoreBase):
def get_description(self):
return """Gets user data from the data store - takes two keyword arguments arguments: 'file_id' and 'key' """
def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
self.validate_kw_args(**kwargs)
my_args = [kwargs['key']]
if 'default' in kwargs.keys():
my_args.append(kwargs['default'])
result = self.get_validate_common('file_data_get', None, None, kwargs['file_id'], *my_args)
return result
@staticmethod
def validate_kw_args(**kwargs):
if kwargs.get('key', None) is None:
raise ApiError(code="missing_argument",
message=f"The 'file_data_get' script requires a keyword argument of 'key'")
if kwargs.get('file_id', None) is None:
raise ApiError(code="missing_argument",
message=f"The 'file_data_get' script requires a keyword argument of 'file_id'")
return True
def do_task(self, task, study_id, workflow_id, *args, **kwargs):
self.validate_kw_args(**kwargs)
my_args = [kwargs['key']]
if 'default' in kwargs.keys():
my_args.append(kwargs['default'])
return self.get_data_common(None,
None,
'file_data_get',
kwargs['file_id'],
*my_args)

View File

@ -1,68 +0,0 @@
from crc.api.common import ApiError
from crc.services.data_store_service import DataStoreBase
from crc.scripts.script import Script
from crc.services.document_service import DocumentService
from crc.services.user_file_service import UserFileService
class FileDataSet(Script, DataStoreBase):
def get_description(self):
return """Sets data the data store - takes three keyword arguments arguments: 'file_id', 'key' and 'value'"""
def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
self.validate_kw_args(**kwargs)
my_args = [kwargs['key'], kwargs['value']]
file_id = kwargs['file_id']
result = self.set_validate_common(task.id,
study_id,
workflow_id,
'file_data_set',
None,
file_id,
*my_args)
return result
@staticmethod
def validate_kw_args(**kwargs):
if kwargs.get('key', None) is None:
raise ApiError(code="missing_argument",
message=f"The 'file_data_get' script requires a keyword argument of 'key'")
if kwargs.get('file_id', None) is None:
raise ApiError(code="missing_argument",
message=f"The 'file_data_get' script requires a keyword argument of 'file_id'")
if kwargs.get('value', None) is None:
raise ApiError(code="missing_argument",
message=f"The 'file_data_get' script requires a keyword argument of 'value'")
if kwargs['key'] == 'irb_code' and not DocumentService.is_allowed_document(kwargs.get('value')):
raise ApiError("invalid_form_field_key",
"When setting an irb_code, the form field id must match a known document in the "
"irb_documents.xlsx reference file. This code is not found in that file '%s'" %
kwargs.get('value'))
return True
def do_task(self, task, study_id, workflow_id, *args, **kwargs):
self.validate_kw_args(**kwargs)
my_args = [kwargs['key'], kwargs['value']]
try:
fileid = int(kwargs['file_id'])
except Exception:
raise ApiError("invalid_file_id",
"Attempting to update DataStore for an invalid file_id '%s'" % kwargs['file_id'])
del(kwargs['file_id'])
if kwargs['key'] == 'irb_code':
irb_doc_code = kwargs['value']
UserFileService.update_irb_code(fileid, irb_doc_code)
return self.set_data_common(task.id,
None,
None,
workflow_id,
None,
'file_data_set',
fileid,
*my_args,
**kwargs)

View File

@ -1,17 +0,0 @@
from crc.services.data_store_service import DataStoreBase
from crc.scripts.script import Script
class StudyDataGet(Script, DataStoreBase):
def get_description(self):
return """Gets study data from the data store."""
def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
return self.get_validate_common('study_data_get', study_id, None, None, *args)
def do_task(self, task, study_id, workflow_id, *args, **kwargs):
return self.get_data_common(study_id,
None,
'study_data_get',
None,
*args)

View File

@ -1,28 +0,0 @@
from crc.services.data_store_service import DataStoreBase
from crc.scripts.script import Script
class StudyDataSet(Script, DataStoreBase):
def get_description(self):
return """Sets study data from the data store. Takes two positional arguments key and value"""
def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
result = self.set_validate_common(task.id,
study_id,
workflow_id,
'study_data_set',
None,
None,
*args)
return result
def do_task(self, task, study_id, workflow_id, *args, **kwargs):
return self.set_data_common(task.id,
study_id,
None,
workflow_id,
None,
'study_data_set',
None,
*args,
**kwargs)

View File

@ -1,19 +0,0 @@
from flask import g
from crc.services.data_store_service import DataStoreBase
from crc.scripts.script import Script
class UserDataGet(Script, DataStoreBase):
def get_description(self):
return """Gets user data from the data store - takes only one argument 'key' """
def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
return self.get_validate_common('user_data_get', None, g.user.uid, None, *args)
def do_task(self, task, study_id, workflow_id, *args, **kwargs):
return self.get_data_common(None,
g.user.uid,
'user_data_get',
None,
*args)

View File

@ -1,31 +0,0 @@
from flask import g
from crc.services.data_store_service import DataStoreBase
from crc.scripts.script import Script
class UserDataSet(Script, DataStoreBase):
def get_description(self):
return """Sets user data to the data store these are positional arguments key and value.
example: user_data_set('my_key','my_value')
"""
def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs):
self.set_validate_common(task.id,
study_id,
workflow_id,
'user_data_set',
g.user.uid,
None,
*args)
def do_task(self, task, study_id, workflow_id, *args, **kwargs):
return self.set_data_common(task.id,
None,
g.user.uid,
workflow_id,
None,
'user_data_set',
None,
*args,
**kwargs)

View File

@ -64,33 +64,33 @@ class DataStoreBase(object):
study_id,
user_id,
workflow_id,
workflow_spec_id,
script_name,
file_id,
*args,
**kwargs):
*args):
self.check_args_2(args, script_name=script_name)
if not args[1]:
key = args[0]
value = args[1]
if value == '' or value is None:
# We delete the data store if the value is empty
self.delete_data_store(study_id, user_id, file_id, *args)
return
if workflow_spec_id is None and workflow_id is not None:
return self.delete_data_store(study_id, user_id, file_id, *args)
workflow_spec_id = None
if workflow_id is not None:
workflow = session.query(WorkflowModel).filter(WorkflowModel.id == workflow_id).first()
workflow_spec_id = workflow.workflow_spec_id
# Check if this data store is previously set
query = session.query(DataStoreModel).filter(DataStoreModel.key == args[0])
query = session.query(DataStoreModel).filter(DataStoreModel.key == key)
if study_id:
query = query.filter(DataStoreModel.study_id == study_id)
if file_id:
elif file_id:
query = query.filter(DataStoreModel.file_id == file_id)
if user_id:
elif user_id:
query = query.filter(DataStoreModel.user_id == user_id)
result = query.order_by(desc(DataStoreModel.last_updated)).all()
if result:
dsm = result[0]
dsm.value = args[1]
dsm.value = value
if task_spec:
dsm.task_spec = task_spec
if workflow_id:
@ -102,8 +102,8 @@ class DataStoreBase(object):
# This just gets rid of all the old unused records
self.delete_extra_data_stores(result[1:])
else:
dsm = DataStoreModel(key=args[0],
value=args[1],
dsm = DataStoreModel(key=key,
value=value,
study_id=study_id,
task_spec=task_spec,
user_id=user_id, # Make this available to any User

View File

@ -106,9 +106,11 @@ class GitService(object):
# get list of untracked files
untracked_files = repo.untracked_files
repo.index.add(modified)
if len(modified) > 0:
repo.index.add(modified)
repo.index.add(untracked_files)
repo.index.remove(deleted)
if len(deleted) > 0:
repo.index.remove(deleted)
repo.index.commit(comment)
repo.remotes.origin.push()

View File

@ -470,7 +470,7 @@ class WorkflowService(object):
field.id in data and data[field.id]:
file_id = data[field.get_property(Task.FIELD_PROP_FILE_DATA)]["id"]
data_args = (field.id, data[field.id])
DataStoreBase().set_data_common(task.id, None, None, None, None, None, file_id, *data_args)
DataStoreBase().set_data_common(task.id, None, None, None, 'file_data_set', file_id, *data_args)
@staticmethod
def evaluate_property(property_name, field, task, task_data=None):

View File

@ -13,13 +13,13 @@
<bpmn:userTask id="Activity_GetData" name="Get Data" camunda:formKey="DataForm">
<bpmn:extensionElements>
<camunda:formData>
<camunda:formField id="key" label="Key" type="string">
<camunda:formField id="key" label="&#39;Key&#39;" type="string">
<camunda:validation>
<camunda:constraint name="required" config="True" />
</camunda:validation>
</camunda:formField>
<camunda:formField id="value" label="Value" type="string" />
<camunda:formField id="file_id" label="File ID" type="string" defaultValue="None" />
<camunda:formField id="value" label="&#39;Value&#39;" type="string" />
<camunda:formField id="file_id" label="&#39;File ID&#39;" type="string" defaultValue="None" />
</camunda:formData>
</bpmn:extensionElements>
<bpmn:incoming>Flow_0cb7y6c</bpmn:incoming>
@ -28,11 +28,11 @@
<bpmn:scriptTask id="Activity_SetDataStore" name="Set Data Store">
<bpmn:incoming>Flow_0cnvihm</bpmn:incoming>
<bpmn:outgoing>Flow_10t9bdk</bpmn:outgoing>
<bpmn:script>study_data_set(key, value)
user_data_set(key, value)
if file_id:
file_data_set(file_id=file_id, key=key, value=value)
</bpmn:script>
<bpmn:script>if value == 'None':
value = None
data_store_set(type='study', key=key, value=value)
data_store_set(type='user', key=key, value=value)
data_store_set(type='file', key=key, value=value, file_id=file_id)</bpmn:script>
</bpmn:scriptTask>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">

View File

@ -8,10 +8,9 @@
<bpmn:scriptTask id="Activity_SetDataStore" name="Set Data Store">
<bpmn:incoming>Flow_1er0zqt</bpmn:incoming>
<bpmn:outgoing>Flow_1vocvzo</bpmn:outgoing>
<bpmn:script>study_data_set(key, value)
user_data_set(key, value)
if file_id:
file_data_set(file_id=file_id, key=key, value=value)</bpmn:script>
<bpmn:script>data_store_set(type='study', key=key, value=value)
data_store_set(type='user', key=key, value=value)
data_store_set(type='file', key=key, value=value, file_id=file_id)</bpmn:script>
</bpmn:scriptTask>
<bpmn:endEvent id="Event_1r0sf3h">
<bpmn:incoming>Flow_1vocvzo</bpmn:incoming>
@ -38,10 +37,10 @@ if file_id:
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_DataStoreSet">
<bpmndi:BPMNEdge id="Flow_1er0zqt_di" bpmnElement="Flow_1er0zqt">
<di:waypoint x="382" y="117" />
<di:waypoint x="454" y="117" />
<di:waypoint x="460" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1vocvzo_di" bpmnElement="Flow_1vocvzo">
<di:waypoint x="554" y="117" />
<di:waypoint x="560" y="117" />
<di:waypoint x="622" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_11kii8u_di" bpmnElement="Flow_11kii8u">
@ -51,15 +50,15 @@ if file_id:
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0b1x0oc_di" bpmnElement="Activity_SetDataStore">
<dc:Bounds x="454" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1r0sf3h_di" bpmnElement="Event_1r0sf3h">
<dc:Bounds x="622" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0v2v553_di" bpmnElement="Activity_GetData">
<dc:Bounds x="282" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0b1x0oc_di" bpmnElement="Activity_SetDataStore">
<dc:Bounds x="460" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -8,27 +8,25 @@
<bpmn:scriptTask id="Activity_SetDataStore" name="Set Data Store">
<bpmn:incoming>Flow_0nstzm8</bpmn:incoming>
<bpmn:outgoing>Flow_08r3ga0</bpmn:outgoing>
<bpmn:script>study_data_set('study_data_key', 'study_data_value')
file_data_set(file_id=1, key='file_data_key', value='file_data_value')
user_data_set('user_data_key', 'user_data_value')</bpmn:script>
<bpmn:script>data_store_set(type='study', key='study_data_key', value='study_data_value')
data_store_set(type='user', key='user_data_key', value='user_data_value')
data_store_set(type='file', key='file_data_key', value='file_data_value', file_id=1)</bpmn:script>
</bpmn:scriptTask>
<bpmn:sequenceFlow id="Flow_08r3ga0" sourceRef="Activity_SetDataStore" targetRef="Activity_GetDataStore" />
<bpmn:scriptTask id="Activity_GetDataStore" name="Get Data Store">
<bpmn:incoming>Flow_08r3ga0</bpmn:incoming>
<bpmn:outgoing>Flow_02l0u2v</bpmn:outgoing>
<bpmn:script>
previous_study_data_value = study_data_get('previous_study_data_key')
previous_file_data_value = file_data_get(file_id=1, key='previous_file_data_key')
previous_user_data_value = user_data_get('previous_user_data_key')
<bpmn:script>previous_study_data_value = data_store_get(type='study', key='previous_study_data_key')
previous_file_data_value = data_store_get(type='file', file_id=1, key='previous_file_data_key')
previous_user_data_value = data_store_get(type='user', key='previous_user_data_key')
study_data_value = study_data_get('study_data_key')
file_data_value = file_data_get(file_id=1, key='file_data_key')
user_data_value = user_data_get('user_data_key')
study_data_value = data_store_get(type='study', key='study_data_key')
file_data_value = data_store_get(type='file', file_id=1, key='file_data_key')
user_data_value = data_store_get(type='user', key='user_data_key')
bad_study_data_value = study_data_get('bad_study_data_key', 'bad_study_data_value')
bad_file_data_value = file_data_get(file_id=1, key='bad_file_data_key', default='bad_file_data_value')
bad_user_data_value = user_data_get('bad_user_data_key', 'bad_user_data_value')
</bpmn:script>
bad_study_data_value = data_store_get(type='study', key='bad_study_data_key', default='bad_study_data_value')
bad_file_data_value = data_store_get(type='file', file_id=1, key='bad_file_data_key', default='bad_file_data_value')
bad_user_data_value = data_store_get(type='user', key='bad_user_data_key', default='bad_user_data_value')</bpmn:script>
</bpmn:scriptTask>
<bpmn:sequenceFlow id="Flow_02l0u2v" sourceRef="Activity_GetDataStore" targetRef="Activity_DisplayDataStore" />
<bpmn:manualTask id="Activity_DisplayDataStore" name="Display Data Store">

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1j7idla" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.10.0">
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1j7idla" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.2.0">
<bpmn:process id="Process_18biih5" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>SequenceFlow_1pnq3kg</bpmn:outgoing>
@ -22,7 +22,7 @@ fileurl = documents['UVACompl_PRCAppr'].files[0]['url']
filename = documents['UVACompl_PRCAppr'].files[0]['name']
file_data_set(file_id=fileid,key='test',value='me')</bpmn:script>
data_store_set(type='file', file_id=fileid, key='test', value='me')</bpmn:script>
</bpmn:scriptTask>
<bpmn:endEvent id="Event_1pdyoyv">
<bpmn:incoming>Flow_02bgcrp</bpmn:incoming>
@ -32,54 +32,54 @@ file_data_set(file_id=fileid,key='test',value='me')</bpmn:script>
<bpmn:scriptTask id="Activity_19x6e2e" name="get output">
<bpmn:incoming>Flow_0z7kamo</bpmn:incoming>
<bpmn:outgoing>Flow_15mmymi</bpmn:outgoing>
<bpmn:script>output=file_data_get(file_id=fileid,key='test')</bpmn:script>
<bpmn:script>output=data_store_get(type='file', file_id=fileid, key='test')</bpmn:script>
</bpmn:scriptTask>
<bpmn:scriptTask id="Activity_0oaeqxs" name="get output2">
<bpmn:incoming>Flow_15mmymi</bpmn:incoming>
<bpmn:outgoing>Flow_02bgcrp</bpmn:outgoing>
<bpmn:script>output2=file_data_get(file_id=fileid,key='unobtainium',default='nope')</bpmn:script>
<bpmn:script>output2=data_store_get(type='file', file_id=fileid, key='unobtainium', default='nope')</bpmn:script>
</bpmn:scriptTask>
<bpmn:sequenceFlow id="Flow_02bgcrp" sourceRef="Activity_0oaeqxs" targetRef="Event_1pdyoyv" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_18biih5">
<bpmndi:BPMNEdge id="Flow_02bgcrp_di" bpmnElement="Flow_02bgcrp">
<di:waypoint x="860" y="117" />
<di:waypoint x="894" y="117" />
<di:waypoint x="962" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_15mmymi_di" bpmnElement="Flow_15mmymi">
<di:waypoint x="690" y="117" />
<di:waypoint x="760" y="117" />
<di:waypoint x="723" y="117" />
<di:waypoint x="794" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0z7kamo_di" bpmnElement="Flow_0z7kamo">
<di:waypoint x="530" y="117" />
<di:waypoint x="590" y="117" />
<di:waypoint x="552" y="117" />
<di:waypoint x="623" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1xqewuk_di" bpmnElement="Flow_1xqewuk">
<di:waypoint x="370" y="117" />
<di:waypoint x="430" y="117" />
<di:waypoint x="381" y="117" />
<di:waypoint x="452" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="SequenceFlow_1pnq3kg_di" bpmnElement="SequenceFlow_1pnq3kg">
<di:waypoint x="215" y="117" />
<di:waypoint x="270" y="117" />
<di:waypoint x="281" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_01ekdl8_di" bpmnElement="Task_Has_Bananas">
<dc:Bounds x="270" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0g5namy_di" bpmnElement="Activity_0yikdu7">
<dc:Bounds x="430" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1pdyoyv_di" bpmnElement="Event_1pdyoyv">
<dc:Bounds x="962" y="99" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_01ekdl8_di" bpmnElement="Task_Has_Bananas">
<dc:Bounds x="281" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0g5namy_di" bpmnElement="Activity_0yikdu7">
<dc:Bounds x="452" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0ma7ela_di" bpmnElement="Activity_19x6e2e">
<dc:Bounds x="590" y="77" width="100" height="80" />
<dc:Bounds x="623" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0oaeqxs_di" bpmnElement="Activity_0oaeqxs">
<dc:Bounds x="760" y="77" width="100" height="80" />
<dc:Bounds x="794" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>

View File

@ -32,7 +32,7 @@ for assoc in assoc_list:
assoc_info = assoc
assoc_status = "Found"
assoc_cid = assoc_info["uid"]
if assoc_cid != study_data_get("sdsPI_ComputingID", False):
if assoc_cid != data_store_get(type='study', key="sdsPI_ComputingID", value=False):
assoc_status = "New"
</bpmn:script>
</bpmn:scriptTask>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_0kmksnn" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.7.0">
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_0kmksnn" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.2.0">
<bpmn:process id="Process_0exnnpv" isExecutable="true">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>SequenceFlow_1nfe5m9</bpmn:outgoing>
@ -18,24 +18,24 @@
<bpmn:scriptTask id="Activity_0cm6tn2" name="setval">
<bpmn:incoming>SequenceFlow_1bqiin0</bpmn:incoming>
<bpmn:outgoing>Flow_09cika8</bpmn:outgoing>
<bpmn:script>study_data_set('testme','newval')</bpmn:script>
<bpmn:script>data_store_set(type='study', key='testme', value='newval')</bpmn:script>
</bpmn:scriptTask>
<bpmn:sequenceFlow id="Flow_1oeqjuy" sourceRef="Activity_0d8iftx" targetRef="Activity_1yup9u7" />
<bpmn:scriptTask id="Activity_0d8iftx" name="getval">
<bpmn:incoming>Flow_09cika8</bpmn:incoming>
<bpmn:outgoing>Flow_1oeqjuy</bpmn:outgoing>
<bpmn:script>out = study_data_get('testme','bogus')</bpmn:script>
<bpmn:script>out = data_store_get(type='study', key='testme', default='bogus')</bpmn:script>
</bpmn:scriptTask>
<bpmn:sequenceFlow id="Flow_0g9waf3" sourceRef="Activity_1yup9u7" targetRef="Activity_0xw717o" />
<bpmn:scriptTask id="Activity_1yup9u7" name="reset value">
<bpmn:incoming>Flow_1oeqjuy</bpmn:incoming>
<bpmn:outgoing>Flow_0g9waf3</bpmn:outgoing>
<bpmn:script>study_data_set('testme','badval')</bpmn:script>
<bpmn:script>data_store_set(type='study', key='testme', value='badval')</bpmn:script>
</bpmn:scriptTask>
<bpmn:scriptTask id="Activity_0xw717o" name="Make sure user_data_get doesn&#39;t get the study_data_set variable">
<bpmn:scriptTask id="Activity_0xw717o" name="Make sure user data_store_get doesn&#39;t get the study data_store_set variable">
<bpmn:incoming>Flow_0g9waf3</bpmn:incoming>
<bpmn:outgoing>Flow_05136ua</bpmn:outgoing>
<bpmn:script>empty = user_data_get('testme','empty')</bpmn:script>
<bpmn:script>empty = data_store_get(type='user', key='testme', default='empty')</bpmn:script>
</bpmn:scriptTask>
<bpmn:sequenceFlow id="Flow_05136ua" sourceRef="Activity_0xw717o" targetRef="Gateway_06osfqz" />
<bpmn:exclusiveGateway id="Gateway_06osfqz" default="Flow_00s638e">
@ -44,7 +44,7 @@
<bpmn:outgoing>Flow_00s638e</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:sequenceFlow id="Flow_1efanns" name="test flow expression" sourceRef="Gateway_06osfqz" targetRef="EndEvent_171dj09">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">user_data_get('nothing','default') == 'default'</bpmn:conditionExpression>
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">data_store_get(type='user', key='nothing', default='default') == 'default'</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:endEvent id="Event_1yebjqg">
<bpmn:incoming>Flow_00s638e</bpmn:incoming>
@ -53,6 +53,33 @@
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_0exnnpv">
<bpmndi:BPMNEdge id="Flow_00s638e_di" bpmnElement="Flow_00s638e">
<di:waypoint x="835" y="410" />
<di:waypoint x="1012" y="410" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1efanns_di" bpmnElement="Flow_1efanns">
<di:waypoint x="810" y="435" />
<di:waypoint x="810" y="672" />
<bpmndi:BPMNLabel>
<dc:Bounds x="798" y="551" width="54" height="27" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_05136ua_di" bpmnElement="Flow_05136ua">
<di:waypoint x="810" y="330" />
<di:waypoint x="810" y="385" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0g9waf3_di" bpmnElement="Flow_0g9waf3">
<di:waypoint x="810" y="157" />
<di:waypoint x="810" y="250" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1oeqjuy_di" bpmnElement="Flow_1oeqjuy">
<di:waypoint x="700" y="117" />
<di:waypoint x="760" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_09cika8_di" bpmnElement="Flow_09cika8">
<di:waypoint x="540" y="117" />
<di:waypoint x="600" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="SequenceFlow_1bqiin0_di" bpmnElement="SequenceFlow_1bqiin0">
<di:waypoint x="370" y="117" />
<di:waypoint x="440" y="117" />
@ -70,51 +97,24 @@
<bpmndi:BPMNShape id="EndEvent_171dj09_di" bpmnElement="EndEvent_171dj09">
<dc:Bounds x="792" y="672" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_09cika8_di" bpmnElement="Flow_09cika8">
<di:waypoint x="540" y="117" />
<di:waypoint x="600" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Activity_0wnwluq_di" bpmnElement="Activity_0cm6tn2">
<dc:Bounds x="440" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1oeqjuy_di" bpmnElement="Flow_1oeqjuy">
<di:waypoint x="700" y="117" />
<di:waypoint x="760" y="117" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Activity_0cq37mm_di" bpmnElement="Activity_0d8iftx">
<dc:Bounds x="600" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0g9waf3_di" bpmnElement="Flow_0g9waf3">
<di:waypoint x="810" y="157" />
<di:waypoint x="810" y="250" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Activity_0cj83fx_di" bpmnElement="Activity_1yup9u7">
<dc:Bounds x="760" y="77" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0pqth07_di" bpmnElement="Activity_0xw717o">
<dc:Bounds x="760" y="250" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_05136ua_di" bpmnElement="Flow_05136ua">
<di:waypoint x="810" y="330" />
<di:waypoint x="810" y="385" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Gateway_06osfqz_di" bpmnElement="Gateway_06osfqz" isMarkerVisible="true">
<dc:Bounds x="785" y="385" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1efanns_di" bpmnElement="Flow_1efanns">
<di:waypoint x="810" y="435" />
<di:waypoint x="810" y="672" />
<bpmndi:BPMNLabel>
<dc:Bounds x="798" y="551" width="54" height="27" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Event_1yebjqg_di" bpmnElement="Event_1yebjqg">
<dc:Bounds x="1012" y="392" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_00s638e_di" bpmnElement="Flow_00s638e">
<di:waypoint x="835" y="410" />
<di:waypoint x="1012" y="410" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -11,7 +11,7 @@ from crc.models.study import StudyModel
from crc.scripts.study_info import StudyInfo
from crc.services.study_service import StudyService
from crc.services.workflow_processor import WorkflowProcessor
from crc.scripts.file_data_set import FileDataSet
from crc.scripts.data_store_set import DataStoreSet
from crc.services.document_service import DocumentService
from crc.services.user_file_service import UserFileService
from crc.services.reference_file_service import ReferenceFileService
@ -103,7 +103,7 @@ class TestStudyDetailsDocumentsScript(BaseTest):
binary_data=b'1234', irb_doc_code=irb_code)
processor = WorkflowProcessor(workflow_model)
task = processor.next_task()
FileDataSet().do_task(task, study.id, workflow_model.id, key="ginger", value="doodle", file_id=file.id)
DataStoreSet().do_task(task, study.id, workflow_model.id, type='file', key="ginger", value="doodle", file_id=file.id)
docs = StudyInfo().do_task(task, study.id, workflow_model.id, "documents")
self.assertTrue(isinstance(docs, Box))
docs = StudyService.get_documents_status(study.id, force=True)
@ -126,7 +126,7 @@ class TestStudyDetailsDocumentsScript(BaseTest):
binary_data=b'1234', irb_doc_code=irb_code)
processor = WorkflowProcessor(workflow_model)
task = processor.next_task()
FileDataSet().do_task(task, study.id, workflow_model.id, key="irb_code", value="Study_App_Doc", file_id=file.id)
DataStoreSet().do_task(task, study.id, workflow_model.id, type='file', key="irb_code", value="Study_App_Doc", file_id=file.id)
docs = StudyInfo().do_task(task, study.id, workflow_model.id, "documents")
self.assertTrue(isinstance(docs, Box))
self.assertEqual(1, len(docs.Study_App_Doc.files))
@ -150,5 +150,5 @@ class TestStudyDetailsDocumentsScript(BaseTest):
processor = WorkflowProcessor(workflow_model)
task = processor.next_task()
with self.assertRaises(ApiError):
FileDataSet().do_task(task, study.id, workflow_model.id, key="irb_code", value="My_Pretty_Pony",
file_id=file.id)
DataStoreSet().do_task(task, study.id, workflow_model.id, type='file',
key="irb_code", value="My_Pretty_Pony", file_id=file.id)

View File

@ -212,11 +212,12 @@ class TestDataStoreValidation(BaseTest):
for record in result:
self.assertEqual('some_value', record.value)
def test_delete_record_on_empty_value(self):
"""If we set a data store with an empty string,
def test_delete_record_on_none_or_empty_string(self):
"""If we set a data store with None or an empty string,
assert that we delete the record."""
file_id = self.add_test_file()
# Test for empty string
form_data = {'key': 'my_key', 'value': 'my_value', 'file_id': file_id}
result = self.run_data_store_set(form_data)
@ -233,3 +234,44 @@ class TestDataStoreValidation(BaseTest):
result = session.query(DataStoreModel).all()
self.assertEqual(0, len(result))
# Test for None
form_data = {'key': 'my_second_key', 'value': 'my_second_value', 'file_id': file_id}
result = self.run_data_store_set(form_data)
self.assertEqual(3, len(result))
for record in result:
self.assertEqual('my_second_value', record.value)
workflow = self.create_workflow('data_store_set')
workflow_api = self.get_workflow_api(workflow)
task = workflow_api.next_task
# The workflow turns the string 'None' into the value None
re_form_data = {'key': 'my_second_key', 'value': 'None', 'file_id': file_id}
self.complete_form(workflow, task, re_form_data)
result = session.query(DataStoreModel).all()
self.assertEqual(0, len(result))
def test_do_not_delete_record_on_false_value(self):
file_id = self.add_test_file()
form_data = {'key': 'my_key', 'value': 'my_value', 'file_id': file_id}
result = self.run_data_store_set(form_data)
self.assertEqual(3, len(result))
for record in result:
self.assertEqual('my_value', record.value)
workflow = self.create_workflow('data_store_set')
workflow_api = self.get_workflow_api(workflow)
task = workflow_api.next_task
re_form_data = {'key': 'my_key', 'value': False, 'file_id': file_id}
self.complete_form(workflow, task, re_form_data)
result = session.query(DataStoreModel).all()
self.assertEqual(3, len(result))
for record in result:
self.assertEqual('false', record.value)