Refactor models into seperate directories

This commit is contained in:
Dan Funk 2019-12-31 16:32:47 -05:00
parent cc33f686e5
commit c4cbaeb64e
14 changed files with 173 additions and 128 deletions

View File

@ -147,7 +147,7 @@ paths:
operationId: crc.api.workflow.all_specifications
summary: Provides a list of workflows specifications that can be added to a study manually. Please note that Protocol Builder will handle this most of the time.
tags:
- Workflow
- Workflows and Tasks
responses:
'200':
description: An array of workflow specifications
@ -424,6 +424,11 @@ paths:
components:
schemas:
DataModel:
properties:
id:
type: string
Study:
properties:
id:

View File

@ -7,7 +7,7 @@ from flask import send_file
from crc import db
from crc.api.common import ApiErrorSchema, ApiError
from crc.models import FileSchema, FileModel, FileDataModel, FileType
from crc.models.file import FileSchema, FileModel, FileDataModel, FileType
def update_file_from_request(file_model):

View File

@ -1,14 +1,9 @@
import os
from datetime import datetime
import connexion
from connexion import NoContent
from flask_marshmallow import Schema
from crc import db, ma, connexion_app
from crc import db
from crc.api.common import ApiError, ApiErrorSchema
from crc.models import WorkflowModel, WorkflowSchema, StudySchema, StudyModel, WorkflowSpecSchema, WorkflowSpecModel, \
WorkflowStatus, Task, TaskSchema, FileSchema, FileModel, FileDataModel, FileType
from crc.models.study import StudySchema, StudyModel
from crc.models.workflow import WorkflowModel, WorkflowSchema, WorkflowSpecModel
from crc.workflow_processor import WorkflowProcessor

View File

@ -1,5 +1,5 @@
from crc import db
from crc.models import WorkflowModel, WorkflowSchema, WorkflowSpecSchema, WorkflowSpecModel, \
from crc.models.workflow import WorkflowModel, WorkflowSchema, WorkflowSpecSchema, WorkflowSpecModel, \
Task, TaskSchema
from crc.workflow_processor import WorkflowProcessor
@ -36,7 +36,9 @@ def get_task(workflow_id, task_id):
def update_task(workflow_id, task_id, body):
global bpmn_workflow
for field in body["task"]["form"]:
print("Setting " + field["id"] + " to " + field["value"])
workflow_spec_model = db.session.query(WorkflowSpecModel).filter_by(id=body["id"]).first()
task = TaskSchema().load(body)
if task.form:
for field in task.form["fields"]:
print("Setting " + field["id"] + " to " + field["value"])
return body

0
crc/models/__init__.py Normal file
View File

40
crc/models/file.py Normal file
View File

@ -0,0 +1,40 @@
import enum
from flask_marshmallow.sqla import ModelSchema
from marshmallow_enum import EnumField
from sqlalchemy import func
from crc import db
class FileType(enum.Enum):
bpmn = "bpmm"
svg = "svg"
dmn = "dmn"
class FileDataModel(db.Model):
__tablename__ = 'file_data'
id = db.Column(db.Integer, primary_key=True)
data = db.Column(db.LargeBinary)
file_model_id = db.Column(db.Integer, db.ForeignKey('file.id'))
file_model = db.relationship("FileModel")
class FileModel(db.Model):
__tablename__ = 'file'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
version = db.Column(db.Integer, default=0)
last_updated = db.Column(db.DateTime(timezone=True), default=func.now())
type = db.Column(db.Enum(FileType))
primary = db.Column(db.Boolean)
content_type = db.Column(db.String)
workflow_spec_id = db.Column(db.Integer, db.ForeignKey('workflow_spec.id'))
class FileSchema(ModelSchema):
class Meta:
model = FileModel
type = EnumField(FileType)

32
crc/models/study.py Normal file
View File

@ -0,0 +1,32 @@
import enum
from marshmallow_enum import EnumField
from marshmallow_sqlalchemy import ModelSchema
from sqlalchemy import func
from crc import db
class ProtocolBuilderStatus(enum.Enum):
out_of_date = "out_of_date"
in_process = "in_process"
complete = "complete"
updating = "updating"
class StudyModel(db.Model):
__tablename__ = 'study'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
last_updated = db.Column(db.DateTime(timezone=True), default=func.now())
protocol_builder_status = db.Column(db.Enum(ProtocolBuilderStatus))
primary_investigator_id = db.Column(db.String)
sponsor = db.Column(db.String)
ind_number = db.Column(db.String)
class StudySchema(ModelSchema):
class Meta:
model = StudyModel
protocol_builder_status = EnumField(ProtocolBuilderStatus)

View File

@ -1,39 +1,12 @@
import enum
from SpiffWorkflow import Task
from flask_marshmallow.sqla import ModelSchema
from marshmallow import post_load, fields
import marshmallow
from marshmallow_enum import EnumField
from sqlalchemy import func
from marshmallow_sqlalchemy import ModelSchema
from crc import db, ma
class ProtocolBuilderStatus(enum.Enum):
out_of_date = "out_of_date"
in_process = "in_process"
complete = "complete"
updating = "updating"
class StudyModel(db.Model):
__tablename__ = 'study'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
last_updated = db.Column(db.DateTime(timezone=True), default=func.now())
protocol_builder_status = db.Column(db.Enum(ProtocolBuilderStatus))
primary_investigator_id = db.Column(db.String)
sponsor = db.Column(db.String)
ind_number = db.Column(db.String)
class StudySchema(ModelSchema):
class Meta:
model = StudyModel
protocol_builder_status = EnumField(ProtocolBuilderStatus)
class WorkflowSpecModel(db.Model):
__tablename__ = 'workflow_spec'
id = db.Column(db.String, primary_key=True)
@ -45,38 +18,6 @@ class WorkflowSpecSchema(ModelSchema):
class Meta:
model = WorkflowSpecModel
class FileType(enum.Enum):
bpmn = "bpmm"
svg = "svg"
dmn = "dmn"
class FileDataModel(db.Model):
__tablename__ = 'file_data'
id = db.Column(db.Integer, primary_key=True)
data = db.Column(db.LargeBinary)
file_model_id = db.Column(db.Integer, db.ForeignKey('file.id'))
file_model = db.relationship("FileModel")
class FileModel(db.Model):
__tablename__ = 'file'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
version = db.Column(db.Integer, default=0)
last_updated = db.Column(db.DateTime(timezone=True), default=func.now())
type = db.Column(db.Enum(FileType))
primary = db.Column(db.Boolean)
content_type = db.Column(db.String)
workflow_spec_id = db.Column(db.Integer, db.ForeignKey('workflow_spec.id'))
class FileSchema(ModelSchema):
class Meta:
model = FileModel
type = EnumField(FileType)
class WorkflowStatus(enum.Enum):
new = "new"
user_input_required = "user_input_required"
@ -140,30 +81,28 @@ class PropertiesSchema(ma.Schema):
fields = ["id", "value"]
class FieldSchema(ma.Schema):
class FormFieldSchema(ma.Schema):
class Meta:
fields = [
"id", "type", "label", "defaultValue", "options", "validation", "properties"
"id", "type", "label", "defaultValue", "options", "validation", "properties", "value"
]
options = fields.List(fields.Nested(OptionSchema))
validation = fields.List(fields.Nested(ValidationSchema))
properties = fields.List(fields.Nested(PropertiesSchema))
options = marshmallow.fields.List(marshmallow.fields.Nested(OptionSchema))
validation = marshmallow.fields.List(marshmallow.fields.Nested(ValidationSchema))
properties = marshmallow.fields.List(marshmallow.fields.Nested(PropertiesSchema))
class FormSchema(ma.Schema):
class Meta:
fields = ["key", "fields"]
fields = fields.List(fields.Nested(FieldSchema))
key = marshmallow.fields.String(required=True, allow_none=False)
fields = marshmallow.fields.List(marshmallow.fields.Nested(FormFieldSchema))
class TaskSchema(ma.Schema):
class Meta:
fields = ["id", "name", "title", "type", "state", "form", "documentation"]
documentation = marshmallow.fields.String(required=False, allow_none=True)
form = marshmallow.fields.Nested(FormSchema)
form = fields.Nested(FormSchema)
@post_load
@marshmallow.post_load
def make_task(self, data, **kwargs):
return Task(**data)

View File

@ -95,7 +95,7 @@
<dc:Bounds x="270" y="210" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="ScriptTask_10keafb_di" bpmnElement="Task_Get_Fact_From_API">
<dc:Bounds x="480" y="210" width="100" height="80" />
<dc:Bounds x="470" y="210" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="TextAnnotation_09fq7kh_di" bpmnElement="TextAnnotation_09fq7kh">
<dc:Bounds x="330" y="116" width="99.99202297383536" height="68.28334396936822" />
@ -108,19 +108,19 @@
<dc:Bounds x="570" y="120" width="100" height="68" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Association_1qirnyy_di" bpmnElement="Association_1qirnyy">
<di:waypoint x="567" y="210" />
<di:waypoint x="588" y="188" />
<di:waypoint x="561" y="210" />
<di:waypoint x="584" y="188" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="EndEvent_0u1cgrf_di" bpmnElement="EndEvent_0u1cgrf">
<dc:Bounds x="692" y="232" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_0am07in_di" bpmnElement="SequenceFlow_0am07in">
<di:waypoint x="580" y="250" />
<di:waypoint x="570" y="250" />
<di:waypoint x="692" y="250" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="SequenceFlow_1291h6i_di" bpmnElement="SequenceFlow_1291h6i">
<di:waypoint x="370" y="250" />
<di:waypoint x="480" y="250" />
<di:waypoint x="470" y="250" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>

View File

@ -1,14 +1,14 @@
import os
import xml.etree.ElementTree as ElementTree
from SpiffWorkflow.bpmn.BpmnScriptEngine import BpmnScriptEngine
from SpiffWorkflow.bpmn.serializer.CompactWorkflowSerializer import CompactWorkflowSerializer
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow
from SpiffWorkflow.camunda.parser.CamundaParser import CamundaParser
from SpiffWorkflow.camunda.serializer.CamundaSerializer import CamundaSerializer
from crc import app, db
from crc.models import WorkflowStatus, WorkflowSpecModel, FileDataModel, FileModel
import xml.etree.ElementTree as ElementTree
from crc import db
from crc.models.file import FileDataModel, FileModel
from crc.models.workflow import WorkflowStatus
class CustomBpmnScriptEngine(BpmnScriptEngine):
"""This is a custom script processor that can be easily injected into Spiff Workflow.

View File

@ -2,7 +2,9 @@ import datetime
import os
from crc import db, app
from crc.models import StudyModel, WorkflowSpecModel, FileType, FileModel, FileDataModel
from crc.models.study import StudyModel
from crc.models.workflow import WorkflowSpecModel
from crc.models.file import FileType, FileModel, FileDataModel
class ExampleDataLoader:
@ -28,31 +30,33 @@ class ExampleDataLoader:
),
]
workflow_specs = [WorkflowSpecModel(
id="random_fact",
display_name="Random Fact Generator",
description='Displays a random fact about a topic of your choosing.',
)]
workflow_specifications = \
self.create_spec(id="random_fact",
display_name="Random Fact Generator",
description='Displays a random fact about a topic of your choosing.')
workflow_specifications += \
self.create_spec(id="two_forms",
display_name="Two dump questions on two seperate tasks",
description='Displays a random fact about a topic of your choosing.')
workflow_spec_files = [WorkflowSpecModel(
id="random_fact",
display_name="Random Fact Generator",
description='Displays a random fact about a topic of your choosing.',
)]
workflow_spec_files = [FileModel(name="random_fact.bpmn",
type=FileType.bpmn,
version="1",
last_updated=datetime.datetime.now(),
primary=True,
workflow_spec_id=workflow_specs[0].id)]
filename = os.path.join(app.root_path, 'static', 'bpmn', 'random_fact', 'random_fact.bpmn')
file = open(filename, "rb")
workflow_data = [FileDataModel(data=file.read(), file_model=workflow_spec_files[0])]
all_data = studies+workflow_specs+workflow_spec_files+workflow_data
all_data = studies + workflow_specifications
return all_data
def create_spec(self, id, display_name, description):
"""Assumes that a file exists in static/bpmn with the same name as the given id.
returns an array of data models to be added to the database."""
spec = WorkflowSpecModel(id=id,
display_name=display_name,
description=description)
file_model = FileModel(name=id + ".bpmn", type=FileType.bpmn, version="1",
last_updated=datetime.datetime.now(), primary=True,
workflow_spec_id=id)
filename = os.path.join(app.root_path, 'static', 'bpmn', id + ".bpmn")
file = open(filename, "rb")
workflow_data = FileDataModel(data=file.read(), file_model=file_model)
file.close()
return [spec, file_model, workflow_data]
@staticmethod
def clean_db():
db.session.flush() # Clear out any transactions before deleting it all to avoid spurious errors.

View File

@ -1,12 +1,10 @@
import json
import unittest
from sqlalchemy import func
from crc import db
from crc.models import StudyModel, StudySchema, WorkflowSpecModel, WorkflowSpecSchema, WorkflowModel, WorkflowStatus, \
from crc.models.study import StudyModel, StudySchema
from crc.models.workflow import WorkflowSpecModel, WorkflowSpecSchema, WorkflowModel, WorkflowStatus, \
WorkflowSchema, TaskSchema
from crc.workflow_processor import WorkflowProcessor
from tests.base_test import BaseTest
@ -81,7 +79,6 @@ class TestStudy(BaseTest, unittest.TestCase):
self.assert_success(rv)
self.assertEqual(0, db.session.query(WorkflowModel).count())
def test_get_current_user_tasks(self):
self.load_example_data()
study = db.session.query(StudyModel).first()
@ -93,4 +90,24 @@ class TestStudy(BaseTest, unittest.TestCase):
json_data = json.loads(rv.get_data(as_text=True))
tasks = TaskSchema(many=True).load(json_data)
self.assertEqual("Task_User_Select_Type", tasks[0].name)
self.assertEqual(3, len(tasks[0].form["fields"][0]["options"]))
self.assertEqual(3, len(tasks[0].form["fields"][0]["options"]))
def test_two_forms_task(self):
self.load_example_data()
study = db.session.query(StudyModel).first()
spec = db.session.query(WorkflowSpecModel).filter_by(id='two_forms').first()
rv = self.app.post('/v1.0/study/%i/workflows' % study.id, content_type="application/json",
data=json.dumps(WorkflowSpecSchema().dump(spec)))
json_data = json.loads(rv.get_data(as_text=True))
workflow = WorkflowSchema().load(json_data, session=db.session)
rv = self.app.get('/v1.0/workflow/%i/tasks' % workflow.id, content_type="application/json")
json_data = json.loads(rv.get_data(as_text=True))
tasks = TaskSchema(many=True).load(json_data)
self.assertEqual(1, len(tasks))
self.assertIsNotNone(tasks[0].form)
self.assertEqual(1, len(tasks[0].form['fields']))
tasks[0].form['fields'][0]['value']="Blue"
rv = self.app.put('/v1.0/workflow/%i/task/%s' % (workflow.id, tasks[0].id), content_type="application/json",
data=json.dumps(TaskSchema().dump(tasks[0])))
self.assert_success(rv)

View File

@ -4,7 +4,8 @@ import unittest
from datetime import datetime
from crc import db
from crc.models import WorkflowSpecModel, FileModel, FileType, FileSchema, FileDataModel
from crc.models.workflow import WorkflowSpecModel
from crc.models.file import FileModel, FileType, FileSchema, FileDataModel
from tests.base_test import BaseTest

View File

@ -1,7 +1,7 @@
import unittest
from crc import db
from crc.models import WorkflowSpecModel, WorkflowStatus
from crc.models.workflow import WorkflowSpecModel, WorkflowStatus
from crc.workflow_processor import WorkflowProcessor
from tests.base_test import BaseTest
@ -31,3 +31,13 @@ class TestWorkflowProcessor(BaseTest, unittest.TestCase):
data = processor.get_data()
self.assertIsNotNone(data)
self.assertIn("details", data)
def test_two_forms(self):
self.load_example_data()
workflow_spec_model = db.session.query(WorkflowSpecModel).filter_by(id="two_forms").first()
processor = WorkflowProcessor.create(workflow_spec_model.id)
self.assertEqual(WorkflowStatus.user_input_required, processor.get_status())
next_user_tasks = processor.next_user_tasks()
task = next_user_tasks[0]
data = processor.get_data()