mirror of
https://github.com/status-im/spiff-arena.git
synced 2025-02-12 01:36:58 +00:00
Squashed 'spiffworkflow-backend/' changes from 2cb3fb27e2..55f5c8113e
55f5c8113e arena github actions a06427482b reduce matrix f5f88926a6 debug 23fbe1b2e9 lint and mypy 30ee07700f Merge remote-tracking branch 'origin/main' into feature/home_page_redesign bd38d90600 lint 201655fc0f add the username to the task list w/ burnettk e069906394 merged in main and resolved conflicts w/ burnettk 9135f74a03 added more task tables w/ burnettk 352353e48b store bpmn_file_relative_path using correct slashes w/ burnettk d02a6610bb underscore unused vars a9ecaa0431 added tasks for my open processes page w/ burnettk 74af6a4ad1 Merge remote-tracking branch 'origin/main' into feature/home_page_redesign d5330d6031 Merge remote-tracking branch 'origin/main' into feature/home_page_redesign 37d9ca8dea added home page routes and some tab stuff w/ burnettk git-subtree-dir: spiffworkflow-backend git-subtree-split: 55f5c8113e1189888672992d109e80a3d51dfa1a
This commit is contained in:
parent
a1d7c2b6cb
commit
ac6706197c
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@ -156,7 +156,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload coverage data
|
- name: Upload coverage data
|
||||||
# pin to upload coverage from only one matrix entry, otherwise coverage gets confused later
|
# pin to upload coverage from only one matrix entry, otherwise coverage gets confused later
|
||||||
if: always() && matrix.session == 'tests' && matrix.python == '3.11' && matrix.os == 'ubuntu-latest'
|
if: always() && matrix.session == 'tests' && matrix.python == '3.11' && matrix.os == 'ubuntu-latest' && matrix.database == 'mysql'
|
||||||
uses: "actions/upload-artifact@v3.0.0"
|
uses: "actions/upload-artifact@v3.0.0"
|
||||||
with:
|
with:
|
||||||
name: coverage-data
|
name: coverage-data
|
||||||
|
@ -39,11 +39,14 @@ class MyJSONEncoder(DefaultJSONProvider):
|
|||||||
return_dict = {}
|
return_dict = {}
|
||||||
for row_key in obj.keys():
|
for row_key in obj.keys():
|
||||||
row_value = obj[row_key]
|
row_value = obj[row_key]
|
||||||
if hasattr(row_value, "__dict__"):
|
if hasattr(row_value, "serialized"):
|
||||||
|
return_dict.update(row_value.serialized)
|
||||||
|
elif hasattr(row_value, "__dict__"):
|
||||||
return_dict.update(row_value.__dict__)
|
return_dict.update(row_value.__dict__)
|
||||||
else:
|
else:
|
||||||
return_dict.update({row_key: row_value})
|
return_dict.update({row_key: row_value})
|
||||||
return_dict.pop("_sa_instance_state")
|
if "_sa_instance_state" in return_dict:
|
||||||
|
return_dict.pop("_sa_instance_state")
|
||||||
return return_dict
|
return return_dict
|
||||||
return super().default(obj)
|
return super().default(obj)
|
||||||
|
|
||||||
|
@ -872,6 +872,64 @@ paths:
|
|||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/Task"
|
$ref: "#/components/schemas/Task"
|
||||||
|
|
||||||
|
/tasks/for-my-open-processes:
|
||||||
|
parameters:
|
||||||
|
- name: page
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: The page number to return. Defaults to page 1.
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
- name: per_page
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: The page number to return. Defaults to page 1.
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Process Instances
|
||||||
|
operationId: spiffworkflow_backend.routes.process_api_blueprint.task_list_for_my_open_processes
|
||||||
|
summary: returns the list of tasks for given user's open process instances
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: list of tasks
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/Task"
|
||||||
|
|
||||||
|
/tasks/for-processes-started-by-others:
|
||||||
|
parameters:
|
||||||
|
- name: page
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: The page number to return. Defaults to page 1.
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
- name: per_page
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
description: The page number to return. Defaults to page 1.
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Process Instances
|
||||||
|
operationId: spiffworkflow_backend.routes.process_api_blueprint.task_list_for_processes_started_by_others
|
||||||
|
summary: returns the list of tasks for given user's open process instances
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: list of tasks
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/Task"
|
||||||
|
|
||||||
/process-instance/{process_instance_id}/tasks:
|
/process-instance/{process_instance_id}/tasks:
|
||||||
parameters:
|
parameters:
|
||||||
- name: process_instance_id
|
- name: process_instance_id
|
||||||
|
@ -100,17 +100,17 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel):
|
|||||||
local_bpmn_xml_file_contents = ""
|
local_bpmn_xml_file_contents = ""
|
||||||
if self.bpmn_xml_file_contents:
|
if self.bpmn_xml_file_contents:
|
||||||
local_bpmn_xml_file_contents = self.bpmn_xml_file_contents.decode("utf-8")
|
local_bpmn_xml_file_contents = self.bpmn_xml_file_contents.decode("utf-8")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"id": self.id,
|
"id": self.id,
|
||||||
"process_model_identifier": self.process_model_identifier,
|
"process_model_identifier": self.process_model_identifier,
|
||||||
"process_group_identifier": self.process_group_identifier,
|
"process_group_identifier": self.process_group_identifier,
|
||||||
"status": self.status,
|
"status": self.status,
|
||||||
"bpmn_json": self.bpmn_json,
|
|
||||||
"start_in_seconds": self.start_in_seconds,
|
"start_in_seconds": self.start_in_seconds,
|
||||||
"end_in_seconds": self.end_in_seconds,
|
"end_in_seconds": self.end_in_seconds,
|
||||||
"process_initiator_id": self.process_initiator_id,
|
"process_initiator_id": self.process_initiator_id,
|
||||||
"bpmn_xml_file_contents": local_bpmn_xml_file_contents,
|
"bpmn_xml_file_contents": local_bpmn_xml_file_contents,
|
||||||
|
"bpmn_version_control_identifier": self.bpmn_version_control_identifier,
|
||||||
|
"bpmn_version_control_type": self.bpmn_version_control_type,
|
||||||
"spiff_step": self.spiff_step,
|
"spiff_step": self.spiff_step,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import enum
|
import enum
|
||||||
|
import os
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from dataclasses import field
|
from dataclasses import field
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@ -50,6 +51,11 @@ class ProcessModelInfo:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# for use with os.path.join so it can work on windows
|
||||||
|
def id_for_file_path(self) -> str:
|
||||||
|
"""Id_for_file_path."""
|
||||||
|
return self.id.replace("/", os.sep)
|
||||||
|
|
||||||
|
|
||||||
class ProcessModelInfoSchema(Schema):
|
class ProcessModelInfoSchema(Schema):
|
||||||
"""ProcessModelInfoSchema."""
|
"""ProcessModelInfoSchema."""
|
||||||
|
@ -28,6 +28,7 @@ from lxml import etree # type: ignore
|
|||||||
from lxml.builder import ElementMaker # type: ignore
|
from lxml.builder import ElementMaker # type: ignore
|
||||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||||
from SpiffWorkflow.task import TaskState
|
from SpiffWorkflow.task import TaskState
|
||||||
|
from sqlalchemy import and_
|
||||||
from sqlalchemy import asc
|
from sqlalchemy import asc
|
||||||
from sqlalchemy import desc
|
from sqlalchemy import desc
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ from spiffworkflow_backend.exceptions.process_entity_not_found_error import (
|
|||||||
from spiffworkflow_backend.models.active_task import ActiveTaskModel
|
from spiffworkflow_backend.models.active_task import ActiveTaskModel
|
||||||
from spiffworkflow_backend.models.active_task_user import ActiveTaskUserModel
|
from spiffworkflow_backend.models.active_task_user import ActiveTaskUserModel
|
||||||
from spiffworkflow_backend.models.file import FileSchema
|
from spiffworkflow_backend.models.file import FileSchema
|
||||||
|
from spiffworkflow_backend.models.group import GroupModel
|
||||||
from spiffworkflow_backend.models.message_instance import MessageInstanceModel
|
from spiffworkflow_backend.models.message_instance import MessageInstanceModel
|
||||||
from spiffworkflow_backend.models.message_model import MessageModel
|
from spiffworkflow_backend.models.message_model import MessageModel
|
||||||
from spiffworkflow_backend.models.message_triggerable_process_model import (
|
from spiffworkflow_backend.models.message_triggerable_process_model import (
|
||||||
@ -1000,6 +1002,67 @@ def task_list_my_tasks(page: int = 1, per_page: int = 100) -> flask.wrappers.Res
|
|||||||
return make_response(jsonify(response_json), 200)
|
return make_response(jsonify(response_json), 200)
|
||||||
|
|
||||||
|
|
||||||
|
def task_list_for_my_open_processes(
|
||||||
|
page: int = 1, per_page: int = 100
|
||||||
|
) -> flask.wrappers.Response:
|
||||||
|
"""Task_list_for_my_open_processes."""
|
||||||
|
return get_tasks(page=page, per_page=per_page)
|
||||||
|
|
||||||
|
|
||||||
|
def task_list_for_processes_started_by_others(
|
||||||
|
page: int = 1, per_page: int = 100
|
||||||
|
) -> flask.wrappers.Response:
|
||||||
|
"""Task_list_for_processes_started_by_others."""
|
||||||
|
return get_tasks(processes_started_by_user=False, page=page, per_page=per_page)
|
||||||
|
|
||||||
|
|
||||||
|
def get_tasks(
|
||||||
|
processes_started_by_user: bool = True, page: int = 1, per_page: int = 100
|
||||||
|
) -> flask.wrappers.Response:
|
||||||
|
"""Get_tasks."""
|
||||||
|
user_id = g.user.id
|
||||||
|
active_tasks_query = (
|
||||||
|
ActiveTaskModel.query.outerjoin(
|
||||||
|
GroupModel, GroupModel.id == ActiveTaskModel.lane_assignment_id
|
||||||
|
)
|
||||||
|
.join(ProcessInstanceModel)
|
||||||
|
.join(UserModel, UserModel.id == ProcessInstanceModel.process_initiator_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
if processes_started_by_user:
|
||||||
|
active_tasks_query = active_tasks_query.filter(
|
||||||
|
ProcessInstanceModel.process_initiator_id == user_id
|
||||||
|
).outerjoin(ActiveTaskUserModel, and_(ActiveTaskUserModel.user_id == user_id))
|
||||||
|
else:
|
||||||
|
active_tasks_query = active_tasks_query.filter(
|
||||||
|
ProcessInstanceModel.process_initiator_id != user_id
|
||||||
|
).join(ActiveTaskUserModel, and_(ActiveTaskUserModel.user_id == user_id))
|
||||||
|
|
||||||
|
active_tasks = active_tasks_query.add_columns(
|
||||||
|
ProcessInstanceModel.process_model_identifier,
|
||||||
|
ProcessInstanceModel.status.label("process_instance_status"), # type: ignore
|
||||||
|
ProcessInstanceModel.updated_at_in_seconds,
|
||||||
|
ProcessInstanceModel.created_at_in_seconds,
|
||||||
|
UserModel.username,
|
||||||
|
GroupModel.identifier.label("group_identifier"),
|
||||||
|
ActiveTaskModel.task_name,
|
||||||
|
ActiveTaskModel.task_title,
|
||||||
|
ActiveTaskModel.process_model_display_name,
|
||||||
|
ActiveTaskModel.process_instance_id,
|
||||||
|
ActiveTaskUserModel.user_id.label("current_user_is_potential_owner"),
|
||||||
|
).paginate(page=page, per_page=per_page, error_out=False)
|
||||||
|
|
||||||
|
response_json = {
|
||||||
|
"results": active_tasks.items,
|
||||||
|
"pagination": {
|
||||||
|
"count": len(active_tasks.items),
|
||||||
|
"total": active_tasks.total,
|
||||||
|
"pages": active_tasks.pages,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return make_response(jsonify(response_json), 200)
|
||||||
|
|
||||||
|
|
||||||
def process_instance_task_list(
|
def process_instance_task_list(
|
||||||
process_instance_id: int, all_tasks: bool = False, spiff_step: int = 0
|
process_instance_id: int, all_tasks: bool = False, spiff_step: int = 0
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
@ -1354,9 +1417,18 @@ def find_process_instance_by_id_or_raise(
|
|||||||
process_instance_id: int,
|
process_instance_id: int,
|
||||||
) -> ProcessInstanceModel:
|
) -> ProcessInstanceModel:
|
||||||
"""Find_process_instance_by_id_or_raise."""
|
"""Find_process_instance_by_id_or_raise."""
|
||||||
process_instance = ProcessInstanceModel.query.filter_by(
|
process_instance_query = ProcessInstanceModel.query.filter_by(
|
||||||
id=process_instance_id
|
id=process_instance_id
|
||||||
).first()
|
)
|
||||||
|
|
||||||
|
# we had a frustrating session trying to do joins and access columns from two tables. here's some notes for our future selves:
|
||||||
|
# this returns an object that allows you to do: process_instance.UserModel.username
|
||||||
|
# process_instance = db.session.query(ProcessInstanceModel, UserModel).filter_by(id=process_instance_id).first()
|
||||||
|
# you can also use splat with add_columns, but it still didn't ultimately give us access to the process instance
|
||||||
|
# attributes or username like we wanted:
|
||||||
|
# process_instance_query.join(UserModel).add_columns(*ProcessInstanceModel.__table__.columns, UserModel.username)
|
||||||
|
|
||||||
|
process_instance = process_instance_query.first()
|
||||||
if process_instance is None:
|
if process_instance is None:
|
||||||
raise (
|
raise (
|
||||||
ApiError(
|
ApiError(
|
||||||
|
@ -218,7 +218,7 @@ class ProcessModelService(FileSystemService):
|
|||||||
def __get_all_nested_models(self, group_path: str) -> list:
|
def __get_all_nested_models(self, group_path: str) -> list:
|
||||||
"""__get_all_nested_models."""
|
"""__get_all_nested_models."""
|
||||||
all_nested_models = []
|
all_nested_models = []
|
||||||
for root, dirs, files in os.walk(group_path):
|
for _root, dirs, _files in os.walk(group_path):
|
||||||
for dir in dirs:
|
for dir in dirs:
|
||||||
model_dir = os.path.join(group_path, dir)
|
model_dir = os.path.join(group_path, dir)
|
||||||
if ProcessModelService().is_model(model_dir):
|
if ProcessModelService().is_model(model_dir):
|
||||||
|
@ -375,7 +375,7 @@ class SpecFileService(FileSystemService):
|
|||||||
process_model_info: ProcessModelInfo, bpmn_file_name: str, et_root: _Element
|
process_model_info: ProcessModelInfo, bpmn_file_name: str, et_root: _Element
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Store_bpmn_process_identifiers."""
|
"""Store_bpmn_process_identifiers."""
|
||||||
relative_process_model_path = process_model_info.id
|
relative_process_model_path = process_model_info.id_for_file_path()
|
||||||
|
|
||||||
relative_bpmn_file_path = os.path.join(
|
relative_bpmn_file_path = os.path.join(
|
||||||
relative_process_model_path, bpmn_file_name
|
relative_process_model_path, bpmn_file_name
|
||||||
|
@ -1441,7 +1441,7 @@ class TestProcessApi(BaseTest):
|
|||||||
updated_at_in_seconds=round(time.time()),
|
updated_at_in_seconds=round(time.time()),
|
||||||
start_in_seconds=(1000 * i) + 1000,
|
start_in_seconds=(1000 * i) + 1000,
|
||||||
end_in_seconds=(1000 * i) + 2000,
|
end_in_seconds=(1000 * i) + 2000,
|
||||||
bpmn_json=json.dumps({"i": i}),
|
bpmn_version_control_identifier=i,
|
||||||
)
|
)
|
||||||
db.session.add(process_instance)
|
db.session.add(process_instance)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@ -1487,7 +1487,12 @@ class TestProcessApi(BaseTest):
|
|||||||
results = response.json["results"]
|
results = response.json["results"]
|
||||||
assert len(results) == 4
|
assert len(results) == 4
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
assert json.loads(results[i]["bpmn_json"])["i"] in (1, 2, 3, 4)
|
assert json.loads(results[i]["bpmn_version_control_identifier"]) in (
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
)
|
||||||
|
|
||||||
# start > 2000, end < 5000 - this should eliminate the first 2 and the last
|
# start > 2000, end < 5000 - this should eliminate the first 2 and the last
|
||||||
response = client.get(
|
response = client.get(
|
||||||
@ -1497,8 +1502,8 @@ class TestProcessApi(BaseTest):
|
|||||||
assert response.json is not None
|
assert response.json is not None
|
||||||
results = response.json["results"]
|
results = response.json["results"]
|
||||||
assert len(results) == 2
|
assert len(results) == 2
|
||||||
assert json.loads(results[0]["bpmn_json"])["i"] in (2, 3)
|
assert json.loads(results[0]["bpmn_version_control_identifier"]) in (2, 3)
|
||||||
assert json.loads(results[1]["bpmn_json"])["i"] in (2, 3)
|
assert json.loads(results[1]["bpmn_version_control_identifier"]) in (2, 3)
|
||||||
|
|
||||||
# start > 1000, start < 4000 - this should eliminate the first and the last 2
|
# start > 1000, start < 4000 - this should eliminate the first and the last 2
|
||||||
response = client.get(
|
response = client.get(
|
||||||
@ -1508,8 +1513,8 @@ class TestProcessApi(BaseTest):
|
|||||||
assert response.json is not None
|
assert response.json is not None
|
||||||
results = response.json["results"]
|
results = response.json["results"]
|
||||||
assert len(results) == 2
|
assert len(results) == 2
|
||||||
assert json.loads(results[0]["bpmn_json"])["i"] in (1, 2)
|
assert json.loads(results[0]["bpmn_version_control_identifier"]) in (1, 2)
|
||||||
assert json.loads(results[1]["bpmn_json"])["i"] in (1, 2)
|
assert json.loads(results[1]["bpmn_version_control_identifier"]) in (1, 2)
|
||||||
|
|
||||||
# end > 2000, end < 6000 - this should eliminate the first and the last
|
# end > 2000, end < 6000 - this should eliminate the first and the last
|
||||||
response = client.get(
|
response = client.get(
|
||||||
@ -1520,7 +1525,11 @@ class TestProcessApi(BaseTest):
|
|||||||
results = response.json["results"]
|
results = response.json["results"]
|
||||||
assert len(results) == 3
|
assert len(results) == 3
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
assert json.loads(results[i]["bpmn_json"])["i"] in (1, 2, 3)
|
assert json.loads(results[i]["bpmn_version_control_identifier"]) in (
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
)
|
||||||
|
|
||||||
def test_process_instance_report_list(
|
def test_process_instance_report_list(
|
||||||
self,
|
self,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user