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
|
@ -156,7 +156,7 @@ jobs:
|
|||
|
||||
- name: Upload coverage data
|
||||
# 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"
|
||||
with:
|
||||
name: coverage-data
|
||||
|
|
|
@ -39,11 +39,14 @@ class MyJSONEncoder(DefaultJSONProvider):
|
|||
return_dict = {}
|
||||
for row_key in obj.keys():
|
||||
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__)
|
||||
else:
|
||||
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 super().default(obj)
|
||||
|
||||
|
|
|
@ -872,6 +872,64 @@ paths:
|
|||
items:
|
||||
$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:
|
||||
parameters:
|
||||
- name: process_instance_id
|
||||
|
|
|
@ -100,17 +100,17 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel):
|
|||
local_bpmn_xml_file_contents = ""
|
||||
if self.bpmn_xml_file_contents:
|
||||
local_bpmn_xml_file_contents = self.bpmn_xml_file_contents.decode("utf-8")
|
||||
|
||||
return {
|
||||
"id": self.id,
|
||||
"process_model_identifier": self.process_model_identifier,
|
||||
"process_group_identifier": self.process_group_identifier,
|
||||
"status": self.status,
|
||||
"bpmn_json": self.bpmn_json,
|
||||
"start_in_seconds": self.start_in_seconds,
|
||||
"end_in_seconds": self.end_in_seconds,
|
||||
"process_initiator_id": self.process_initiator_id,
|
||||
"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,
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import enum
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import field
|
||||
from typing import Any
|
||||
|
@ -50,6 +51,11 @@ class ProcessModelInfo:
|
|||
return True
|
||||
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):
|
||||
"""ProcessModelInfoSchema."""
|
||||
|
|
|
@ -28,6 +28,7 @@ from lxml import etree # type: ignore
|
|||
from lxml.builder import ElementMaker # type: ignore
|
||||
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
|
||||
from SpiffWorkflow.task import TaskState
|
||||
from sqlalchemy import and_
|
||||
from sqlalchemy import asc
|
||||
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_user import ActiveTaskUserModel
|
||||
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_model import MessageModel
|
||||
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)
|
||||
|
||||
|
||||
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(
|
||||
process_instance_id: int, all_tasks: bool = False, spiff_step: int = 0
|
||||
) -> flask.wrappers.Response:
|
||||
|
@ -1354,9 +1417,18 @@ def find_process_instance_by_id_or_raise(
|
|||
process_instance_id: int,
|
||||
) -> ProcessInstanceModel:
|
||||
"""Find_process_instance_by_id_or_raise."""
|
||||
process_instance = ProcessInstanceModel.query.filter_by(
|
||||
process_instance_query = ProcessInstanceModel.query.filter_by(
|
||||
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:
|
||||
raise (
|
||||
ApiError(
|
||||
|
|
|
@ -218,7 +218,7 @@ class ProcessModelService(FileSystemService):
|
|||
def __get_all_nested_models(self, group_path: str) -> list:
|
||||
"""__get_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:
|
||||
model_dir = os.path.join(group_path, 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
|
||||
) -> None:
|
||||
"""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_process_model_path, bpmn_file_name
|
||||
|
|
|
@ -1441,7 +1441,7 @@ class TestProcessApi(BaseTest):
|
|||
updated_at_in_seconds=round(time.time()),
|
||||
start_in_seconds=(1000 * i) + 1000,
|
||||
end_in_seconds=(1000 * i) + 2000,
|
||||
bpmn_json=json.dumps({"i": i}),
|
||||
bpmn_version_control_identifier=i,
|
||||
)
|
||||
db.session.add(process_instance)
|
||||
db.session.commit()
|
||||
|
@ -1487,7 +1487,12 @@ class TestProcessApi(BaseTest):
|
|||
results = response.json["results"]
|
||||
assert len(results) == 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
|
||||
response = client.get(
|
||||
|
@ -1497,8 +1502,8 @@ class TestProcessApi(BaseTest):
|
|||
assert response.json is not None
|
||||
results = response.json["results"]
|
||||
assert len(results) == 2
|
||||
assert json.loads(results[0]["bpmn_json"])["i"] in (2, 3)
|
||||
assert json.loads(results[1]["bpmn_json"])["i"] in (2, 3)
|
||||
assert json.loads(results[0]["bpmn_version_control_identifier"]) 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
|
||||
response = client.get(
|
||||
|
@ -1508,8 +1513,8 @@ class TestProcessApi(BaseTest):
|
|||
assert response.json is not None
|
||||
results = response.json["results"]
|
||||
assert len(results) == 2
|
||||
assert json.loads(results[0]["bpmn_json"])["i"] in (1, 2)
|
||||
assert json.loads(results[1]["bpmn_json"])["i"] in (1, 2)
|
||||
assert json.loads(results[0]["bpmn_version_control_identifier"]) 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
|
||||
response = client.get(
|
||||
|
@ -1520,7 +1525,11 @@ class TestProcessApi(BaseTest):
|
|||
results = response.json["results"]
|
||||
assert len(results) == 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(
|
||||
self,
|
||||
|
|
Loading…
Reference in New Issue