Squashed 'spiffworkflow-backend/' changes from 5c6601237e..c354b846ca
c354b846ca Merge remote-tracking branch 'origin/main' into feature/carbon_ui ad13a41127 pyl is passing w/ burnettk 2366b93b73 Merge commit 'a0b923c9ad98d07d3cf46dca689a01b13d41aa02' bdc8121211 some more updates for group forms w/ burnettk 8483b98788 ergeremote-tracking branch 'origin/main' into feature/carbon_ui 623144aad2 add date ranges for process instances search w/ burnettk git-subtree-dir: spiffworkflow-backend git-subtree-split: c354b846cab6ac27d117ce9c10f9f56aec35df75
This commit is contained in:
parent
a0b923c9ad
commit
12cc510fe9
|
@ -1865,9 +1865,7 @@ develop = false
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
celery = "*"
|
celery = "*"
|
||||||
configparser = "*"
|
configparser = "*"
|
||||||
dateparser = "*"
|
|
||||||
lxml = "*"
|
lxml = "*"
|
||||||
pytz = "*"
|
|
||||||
|
|
||||||
[package.source]
|
[package.source]
|
||||||
type = "git"
|
type = "git"
|
||||||
|
@ -2621,7 +2619,6 @@ greenlet = [
|
||||||
{file = "greenlet-1.1.3.post0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a954002064ee919b444b19c1185e8cce307a1f20600f47d6f4b6d336972c809"},
|
{file = "greenlet-1.1.3.post0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a954002064ee919b444b19c1185e8cce307a1f20600f47d6f4b6d336972c809"},
|
||||||
{file = "greenlet-1.1.3.post0-cp39-cp39-win32.whl", hash = "sha256:2ccdc818cc106cc238ff7eba0d71b9c77be868fdca31d6c3b1347a54c9b187b2"},
|
{file = "greenlet-1.1.3.post0-cp39-cp39-win32.whl", hash = "sha256:2ccdc818cc106cc238ff7eba0d71b9c77be868fdca31d6c3b1347a54c9b187b2"},
|
||||||
{file = "greenlet-1.1.3.post0-cp39-cp39-win_amd64.whl", hash = "sha256:91a84faf718e6f8b888ca63d0b2d6d185c8e2a198d2a7322d75c303e7097c8b7"},
|
{file = "greenlet-1.1.3.post0-cp39-cp39-win_amd64.whl", hash = "sha256:91a84faf718e6f8b888ca63d0b2d6d185c8e2a198d2a7322d75c303e7097c8b7"},
|
||||||
{file = "greenlet-1.1.3.post0.tar.gz", hash = "sha256:f5e09dc5c6e1796969fd4b775ea1417d70e49a5df29aaa8e5d10675d9e11872c"},
|
|
||||||
]
|
]
|
||||||
gunicorn = [
|
gunicorn = [
|
||||||
{file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"},
|
{file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"},
|
||||||
|
|
|
@ -153,7 +153,6 @@ paths:
|
||||||
description: The number of groups to show per page. Defaults to page 10.
|
description: The number of groups to show per page. Defaults to page 10.
|
||||||
schema:
|
schema:
|
||||||
type: integer
|
type: integer
|
||||||
# process_groups_list
|
|
||||||
get:
|
get:
|
||||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_groups_list
|
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_groups_list
|
||||||
summary: get list
|
summary: get list
|
||||||
|
@ -168,7 +167,6 @@ paths:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/ProcessModelCategory"
|
$ref: "#/components/schemas/ProcessModelCategory"
|
||||||
# process_group_add
|
|
||||||
post:
|
post:
|
||||||
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_group_add
|
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_group_add
|
||||||
summary: Add process group
|
summary: Add process group
|
||||||
|
@ -429,7 +427,7 @@ paths:
|
||||||
description: For filtering - beginning of start window - in seconds since epoch
|
description: For filtering - beginning of start window - in seconds since epoch
|
||||||
schema:
|
schema:
|
||||||
type: integer
|
type: integer
|
||||||
- name: start_till
|
- name: start_to
|
||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
description: For filtering - end of start window - in seconds since epoch
|
description: For filtering - end of start window - in seconds since epoch
|
||||||
|
@ -441,7 +439,7 @@ paths:
|
||||||
description: For filtering - beginning of end window - in seconds since epoch
|
description: For filtering - beginning of end window - in seconds since epoch
|
||||||
schema:
|
schema:
|
||||||
type: integer
|
type: integer
|
||||||
- name: end_till
|
- name: end_to
|
||||||
in: query
|
in: query
|
||||||
required: false
|
required: false
|
||||||
description: For filtering - end of end window - in seconds since epoch
|
description: For filtering - end of end window - in seconds since epoch
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""Process_group."""
|
"""Process_group."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import dataclasses
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from dataclasses import field
|
from dataclasses import field
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
@ -20,6 +21,7 @@ class ProcessGroup:
|
||||||
|
|
||||||
id: str # A unique string name, lower case, under scores (ie, 'my_group')
|
id: str # A unique string name, lower case, under scores (ie, 'my_group')
|
||||||
display_name: str
|
display_name: str
|
||||||
|
description: str | None = None
|
||||||
display_order: int | None = 0
|
display_order: int | None = 0
|
||||||
admin: bool | None = False
|
admin: bool | None = False
|
||||||
process_models: list[ProcessModelInfo] = field(
|
process_models: list[ProcessModelInfo] = field(
|
||||||
|
@ -38,6 +40,12 @@ class ProcessGroup:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def serialized(self) -> dict:
|
||||||
|
"""Serialized."""
|
||||||
|
original_dict = dataclasses.asdict(self)
|
||||||
|
return {x: original_dict[x] for x in original_dict if x not in ["sort_index"]}
|
||||||
|
|
||||||
|
|
||||||
class ProcessGroupSchema(Schema):
|
class ProcessGroupSchema(Schema):
|
||||||
"""ProcessGroupSchema."""
|
"""ProcessGroupSchema."""
|
||||||
|
|
|
@ -30,7 +30,6 @@ class ProcessModelInfo:
|
||||||
display_name: str
|
display_name: str
|
||||||
description: str
|
description: str
|
||||||
process_group_id: str = ""
|
process_group_id: str = ""
|
||||||
process_group: Any | None = None
|
|
||||||
primary_file_name: str | None = None
|
primary_file_name: str | None = None
|
||||||
primary_process_id: str | None = None
|
primary_process_id: str | None = None
|
||||||
display_order: int | None = 0
|
display_order: int | None = 0
|
||||||
|
|
|
@ -43,6 +43,7 @@ from spiffworkflow_backend.models.message_triggerable_process_model import (
|
||||||
MessageTriggerableProcessModel,
|
MessageTriggerableProcessModel,
|
||||||
)
|
)
|
||||||
from spiffworkflow_backend.models.principal import PrincipalModel
|
from spiffworkflow_backend.models.principal import PrincipalModel
|
||||||
|
from spiffworkflow_backend.models.process_group import ProcessGroup
|
||||||
from spiffworkflow_backend.models.process_group import ProcessGroupSchema
|
from spiffworkflow_backend.models.process_group import ProcessGroupSchema
|
||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceApiSchema
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceApiSchema
|
||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||||
|
@ -135,18 +136,12 @@ def permissions_check(body: Dict[str, Dict[str, list[str]]]) -> flask.wrappers.R
|
||||||
return make_response(jsonify({"results": response_dict}), 200)
|
return make_response(jsonify({"results": response_dict}), 200)
|
||||||
|
|
||||||
|
|
||||||
def process_group_add(
|
def process_group_add(body: dict) -> flask.wrappers.Response:
|
||||||
body: Dict[str, Union[str, bool, int]]
|
|
||||||
) -> flask.wrappers.Response:
|
|
||||||
"""Add_process_group."""
|
"""Add_process_group."""
|
||||||
process_model_service = ProcessModelService()
|
process_model_service = ProcessModelService()
|
||||||
process_group = ProcessGroupSchema().load(body)
|
process_group = ProcessGroup(**body)
|
||||||
process_model_service.add_process_group(process_group)
|
process_model_service.add_process_group(process_group)
|
||||||
return Response(
|
return make_response(jsonify(process_group), 201)
|
||||||
json.dumps(ProcessGroupSchema().dump(process_group)),
|
|
||||||
status=201,
|
|
||||||
mimetype="application/json",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def process_group_delete(process_group_id: str) -> flask.wrappers.Response:
|
def process_group_delete(process_group_id: str) -> flask.wrappers.Response:
|
||||||
|
@ -155,13 +150,18 @@ def process_group_delete(process_group_id: str) -> flask.wrappers.Response:
|
||||||
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
|
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
|
||||||
|
|
||||||
|
|
||||||
def process_group_update(
|
def process_group_update(process_group_id: str, body: dict) -> flask.wrappers.Response:
|
||||||
process_group_id: str, body: Dict[str, Union[str, bool, int]]
|
|
||||||
) -> Dict[str, Union[str, bool, int]]:
|
|
||||||
"""Process Group Update."""
|
"""Process Group Update."""
|
||||||
process_group = ProcessGroupSchema().load(body)
|
body_include_list = ["display_name", "description"]
|
||||||
|
body_filtered = {
|
||||||
|
include_item: body[include_item]
|
||||||
|
for include_item in body_include_list
|
||||||
|
if include_item in body
|
||||||
|
}
|
||||||
|
|
||||||
|
process_group = ProcessGroup(id=process_group_id, **body_filtered)
|
||||||
ProcessModelService().update_process_group(process_group)
|
ProcessModelService().update_process_group(process_group)
|
||||||
return ProcessGroupSchema().dump(process_group) # type: ignore
|
return make_response(jsonify(process_group), 200)
|
||||||
|
|
||||||
|
|
||||||
def process_groups_list(page: int = 1, per_page: int = 100) -> flask.wrappers.Response:
|
def process_groups_list(page: int = 1, per_page: int = 100) -> flask.wrappers.Response:
|
||||||
|
@ -174,6 +174,7 @@ def process_groups_list(page: int = 1, per_page: int = 100) -> flask.wrappers.Re
|
||||||
remainder = len(process_groups) % per_page
|
remainder = len(process_groups) % per_page
|
||||||
if remainder > 0:
|
if remainder > 0:
|
||||||
pages += 1
|
pages += 1
|
||||||
|
|
||||||
response_json = {
|
response_json = {
|
||||||
"results": ProcessGroupSchema(many=True).dump(batch),
|
"results": ProcessGroupSchema(many=True).dump(batch),
|
||||||
"pagination": {
|
"pagination": {
|
||||||
|
@ -199,7 +200,7 @@ def process_group_show(
|
||||||
status_code=400,
|
status_code=400,
|
||||||
)
|
)
|
||||||
) from exception
|
) from exception
|
||||||
return ProcessGroupSchema().dump(process_group)
|
return make_response(jsonify(process_group), 200)
|
||||||
|
|
||||||
|
|
||||||
def process_model_add(
|
def process_model_add(
|
||||||
|
@ -225,7 +226,6 @@ def process_model_add(
|
||||||
status_code=400,
|
status_code=400,
|
||||||
)
|
)
|
||||||
|
|
||||||
process_model_info.process_group = process_group
|
|
||||||
process_model_service.add_spec(process_model_info)
|
process_model_service.add_spec(process_model_info)
|
||||||
return Response(
|
return Response(
|
||||||
json.dumps(ProcessModelInfoSchema().dump(process_model_info)),
|
json.dumps(ProcessModelInfoSchema().dump(process_model_info)),
|
||||||
|
@ -651,9 +651,9 @@ def process_instance_list(
|
||||||
page: int = 1,
|
page: int = 1,
|
||||||
per_page: int = 100,
|
per_page: int = 100,
|
||||||
start_from: Optional[int] = None,
|
start_from: Optional[int] = None,
|
||||||
start_till: Optional[int] = None,
|
start_to: Optional[int] = None,
|
||||||
end_from: Optional[int] = None,
|
end_from: Optional[int] = None,
|
||||||
end_till: Optional[int] = None,
|
end_to: Optional[int] = None,
|
||||||
process_status: Optional[str] = None,
|
process_status: Optional[str] = None,
|
||||||
) -> flask.wrappers.Response:
|
) -> flask.wrappers.Response:
|
||||||
"""Process_instance_list."""
|
"""Process_instance_list."""
|
||||||
|
@ -684,17 +684,17 @@ def process_instance_list(
|
||||||
process_instance_query = process_instance_query.filter(
|
process_instance_query = process_instance_query.filter(
|
||||||
ProcessInstanceModel.start_in_seconds >= start_from
|
ProcessInstanceModel.start_in_seconds >= start_from
|
||||||
)
|
)
|
||||||
if start_till is not None:
|
if start_to is not None:
|
||||||
process_instance_query = process_instance_query.filter(
|
process_instance_query = process_instance_query.filter(
|
||||||
ProcessInstanceModel.start_in_seconds <= start_till
|
ProcessInstanceModel.start_in_seconds <= start_to
|
||||||
)
|
)
|
||||||
if end_from is not None:
|
if end_from is not None:
|
||||||
process_instance_query = process_instance_query.filter(
|
process_instance_query = process_instance_query.filter(
|
||||||
ProcessInstanceModel.end_in_seconds >= end_from
|
ProcessInstanceModel.end_in_seconds >= end_from
|
||||||
)
|
)
|
||||||
if end_till is not None:
|
if end_to is not None:
|
||||||
process_instance_query = process_instance_query.filter(
|
process_instance_query = process_instance_query.filter(
|
||||||
ProcessInstanceModel.end_in_seconds <= end_till
|
ProcessInstanceModel.end_in_seconds <= end_to
|
||||||
)
|
)
|
||||||
if process_status is not None:
|
if process_status is not None:
|
||||||
process_status_array = process_status.split(",")
|
process_status_array = process_status.split(",")
|
||||||
|
|
|
@ -170,7 +170,7 @@ class ProcessModelService(FileSystemService):
|
||||||
json_path = os.path.join(cat_path, self.CAT_JSON_FILE)
|
json_path = os.path.join(cat_path, self.CAT_JSON_FILE)
|
||||||
with open(json_path, "w") as cat_json:
|
with open(json_path, "w") as cat_json:
|
||||||
json.dump(
|
json.dump(
|
||||||
self.GROUP_SCHEMA.dump(process_group),
|
process_group.serialized,
|
||||||
cat_json,
|
cat_json,
|
||||||
indent=4,
|
indent=4,
|
||||||
sort_keys=True,
|
sort_keys=True,
|
||||||
|
@ -274,6 +274,5 @@ class ProcessModelService(FileSystemService):
|
||||||
with open(spec_path, "w") as wf_json:
|
with open(spec_path, "w") as wf_json:
|
||||||
json.dump(self.WF_SCHEMA.dump(spec), wf_json, indent=4)
|
json.dump(self.WF_SCHEMA.dump(spec), wf_json, indent=4)
|
||||||
if process_group:
|
if process_group:
|
||||||
spec.process_group = process_group
|
|
||||||
spec.process_group_id = process_group.id
|
spec.process_group_id = process_group.id
|
||||||
return spec
|
return spec
|
||||||
|
|
|
@ -17,7 +17,6 @@ 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.group import GroupModel
|
from spiffworkflow_backend.models.group import GroupModel
|
||||||
from spiffworkflow_backend.models.process_group import ProcessGroup
|
from spiffworkflow_backend.models.process_group import ProcessGroup
|
||||||
from spiffworkflow_backend.models.process_group import ProcessGroupSchema
|
|
||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
|
||||||
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
|
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
|
||||||
from spiffworkflow_backend.models.process_instance_report import (
|
from spiffworkflow_backend.models.process_instance_report import (
|
||||||
|
@ -388,25 +387,29 @@ class TestProcessApi(BaseTest):
|
||||||
display_name="Another Test Category",
|
display_name="Another Test Category",
|
||||||
display_order=0,
|
display_order=0,
|
||||||
admin=False,
|
admin=False,
|
||||||
|
description="Test Description",
|
||||||
)
|
)
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/v1.0/process-groups",
|
"/v1.0/process-groups",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
content_type="application/json",
|
content_type="application/json",
|
||||||
data=json.dumps(ProcessGroupSchema().dump(process_group)),
|
data=json.dumps(process_group.serialized),
|
||||||
)
|
)
|
||||||
assert response.status_code == 201
|
assert response.status_code == 201
|
||||||
|
assert response.json
|
||||||
|
|
||||||
# Check what is returned
|
# Check what is returned
|
||||||
result = ProcessGroupSchema().loads(response.get_data(as_text=True))
|
result = ProcessGroup(**response.json)
|
||||||
assert result is not None
|
assert result is not None
|
||||||
assert result.display_name == "Another Test Category"
|
assert result.display_name == "Another Test Category"
|
||||||
assert result.id == "test"
|
assert result.id == "test"
|
||||||
|
assert result.description == "Test Description"
|
||||||
|
|
||||||
# Check what is persisted
|
# Check what is persisted
|
||||||
persisted = ProcessModelService().get_process_group("test")
|
persisted = ProcessModelService().get_process_group("test")
|
||||||
assert persisted.display_name == "Another Test Category"
|
assert persisted.display_name == "Another Test Category"
|
||||||
assert persisted.id == "test"
|
assert persisted.id == "test"
|
||||||
|
assert persisted.description == "Test Description"
|
||||||
|
|
||||||
def test_process_group_delete(
|
def test_process_group_delete(
|
||||||
self,
|
self,
|
||||||
|
@ -461,7 +464,7 @@ class TestProcessApi(BaseTest):
|
||||||
f"/v1.0/process-groups/{group_id}",
|
f"/v1.0/process-groups/{group_id}",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
content_type="application/json",
|
content_type="application/json",
|
||||||
data=json.dumps(ProcessGroupSchema().dump(process_group)),
|
data=json.dumps(process_group.serialized),
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
@ -788,6 +791,7 @@ class TestProcessApi(BaseTest):
|
||||||
f"/v1.0/process-groups/{test_process_group_id}",
|
f"/v1.0/process-groups/{test_process_group_id}",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.json is not None
|
assert response.json is not None
|
||||||
assert response.json["id"] == test_process_group_id
|
assert response.json["id"] == test_process_group_id
|
||||||
|
@ -1299,7 +1303,7 @@ class TestProcessApi(BaseTest):
|
||||||
|
|
||||||
# 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(
|
||||||
"/v1.0/process-instances?start_from=2001&end_till=5999",
|
"/v1.0/process-instances?start_from=2001&end_to=5999",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
assert response.json is not None
|
assert response.json is not None
|
||||||
|
@ -1310,7 +1314,7 @@ class TestProcessApi(BaseTest):
|
||||||
|
|
||||||
# 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(
|
||||||
"/v1.0/process-instances?start_from=1001&start_till=3999",
|
"/v1.0/process-instances?start_from=1001&start_to=3999",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
assert response.json is not None
|
assert response.json is not None
|
||||||
|
@ -1321,7 +1325,7 @@ class TestProcessApi(BaseTest):
|
||||||
|
|
||||||
# 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(
|
||||||
"/v1.0/process-instances?end_from=2001&end_till=5999",
|
"/v1.0/process-instances?end_from=2001&end_to=5999",
|
||||||
headers=self.logged_in_headers(with_super_admin_user),
|
headers=self.logged_in_headers(with_super_admin_user),
|
||||||
)
|
)
|
||||||
assert response.json is not None
|
assert response.json is not None
|
||||||
|
|
Loading…
Reference in New Issue