updated typeguard and fixed issues w/ burnettk

This commit is contained in:
jasquat 2023-05-01 15:26:29 -04:00
parent eacc50080c
commit 0811922c2e
No known key found for this signature in database
8 changed files with 87 additions and 137 deletions

View File

@ -23,7 +23,7 @@ from spiffworkflow_backend.services.process_model_service import ProcessModelSer
# We need to call this before importing spiffworkflow_backend
# otherwise typeguard cannot work. hence the noqa: E402
if os.environ.get("RUN_TYPEGUARD") == "true":
from typeguard.importhook import install_import_hook
from typeguard import install_import_hook
install_import_hook(packages="spiffworkflow_backend")

View File

@ -3502,19 +3502,23 @@ files = [
[[package]]
name = "typeguard"
version = "2.13.3"
version = "3.0.2"
description = "Run-time type checker for Python"
category = "dev"
optional = false
python-versions = ">=3.5.3"
python-versions = ">=3.7.4"
files = [
{file = "typeguard-2.13.3-py3-none-any.whl", hash = "sha256:5e3e3be01e887e7eafae5af63d1f36c849aaa94e3a0112097312aabfa16284f1"},
{file = "typeguard-2.13.3.tar.gz", hash = "sha256:00edaa8da3a133674796cf5ea87d9f4b4c367d77476e185e80251cc13dfbb8c4"},
{file = "typeguard-3.0.2-py3-none-any.whl", hash = "sha256:bbe993854385284ab42fd5bd3bee6f6556577ce8b50696d6cb956d704f286c8e"},
{file = "typeguard-3.0.2.tar.gz", hash = "sha256:fee5297fdb28f8e9efcb8142b5ee219e02375509cd77ea9d270b5af826358d5a"},
]
[package.dependencies]
importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""}
typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.11\""}
[package.extras]
doc = ["sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
test = ["mypy", "pytest", "typing-extensions"]
doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
test = ["mypy (>=0.991)", "pytest (>=7)"]
[[package]]
name = "types-click"
@ -3934,4 +3938,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more
[metadata]
lock-version = "2.0"
python-versions = ">=3.9,<3.12"
content-hash = "994c36ab39238500b4fd05bc1ccdd2d729dd5f66749ab77b1028371147bdf753"
content-hash = "53f3340f73de770b4fbebff3fcd396cdf1bc2c082b929ade350f31a9df6c3860"

View File

@ -93,7 +93,7 @@ pytest = "^7.1.2"
coverage = {extras = ["toml"], version = "^6.1"}
safety = "^2.3.1"
mypy = ">=0.961"
typeguard = "^2"
typeguard = "^3"
xdoctest = {extras = ["colors"], version = "^1.0.1"}
sphinx = "^5.0.2"
sphinx-autobuild = ">=2021.3.14"

View File

@ -5,13 +5,11 @@ import sys
import typing
from dataclasses import dataclass
from typing import Any
from typing import cast
if sys.version_info < (3, 11):
from typing_extensions import TypedDict, NotRequired
else:
from typing import TypedDict, NotRequired
from typing import Optional
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship
@ -19,11 +17,7 @@ from sqlalchemy.orm import relationship
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
from spiffworkflow_backend.models.user import UserModel
from spiffworkflow_backend.services.process_instance_processor import (
ProcessInstanceProcessor,
)
class FilterValue(TypedDict):
@ -149,78 +143,3 @@ class ProcessInstanceReportModel(SpiffworkflowBaseDBModel):
if isinstance(value, str) or isinstance(value, int):
field_value = str(field_value).replace("{{" + key + "}}", str(value))
return field_value
# modeled after https://github.com/suyash248/sqlalchemy-json-querybuilder
# just supports "equals" operator for now.
# perhaps we will use the database instead of filtering in memory in the future and then we might use this lib directly.
def passes_filter(self, process_instance_dict: dict, substitution_variables: dict) -> bool:
"""Passes_filter."""
if "filter_by" in self.report_metadata:
for filter_by in self.report_metadata["filter_by"]:
field_name = filter_by["field_name"]
operator = filter_by["operator"]
field_value = self.with_substitutions(filter_by["field_value"], substitution_variables)
if operator == "equals":
if str(process_instance_dict.get(field_name)) != str(field_value):
return False
return True
def order_things(self, process_instance_dicts: list) -> list:
"""Order_things."""
order_by = self.report_metadata["order_by"]
def order_by_function_for_lambda(
process_instance_dict: dict,
) -> list[Reversor | str | None]:
"""Order_by_function_for_lambda."""
comparison_values: list[Reversor | str | None] = []
for order_by_item in order_by:
if order_by_item.startswith("-"):
# remove leading - from order_by_item
order_by_item = order_by_item[1:]
sort_value = process_instance_dict.get(order_by_item)
comparison_values.append(Reversor(sort_value))
else:
sort_value = cast(Optional[str], process_instance_dict.get(order_by_item))
comparison_values.append(sort_value)
return comparison_values
return sorted(process_instance_dicts, key=order_by_function_for_lambda)
def generate_report(
self,
process_instances: list[ProcessInstanceModel],
substitution_variables: dict | None,
) -> ProcessInstanceReportResult:
"""Generate_report."""
if substitution_variables is None:
substitution_variables = {}
def to_serialized(process_instance: ProcessInstanceModel) -> dict:
"""To_serialized."""
processor = ProcessInstanceProcessor(process_instance)
process_instance.data = processor.get_current_data()
return process_instance.serialized_flat
process_instance_dicts = map(to_serialized, process_instances)
results = []
for process_instance_dict in process_instance_dicts:
if self.passes_filter(process_instance_dict, substitution_variables):
results.append(process_instance_dict)
if "order_by" in self.report_metadata:
results = self.order_things(results)
if "columns" in self.report_metadata:
column_keys_to_keep = [c["accessor"] for c in self.report_metadata["columns"]]
pruned_results = []
for result in results:
dict_you_want = {
your_key: result[your_key] for your_key in column_keys_to_keep if result.get(your_key)
}
pruned_results.append(dict_you_want)
results = pruned_results
return ProcessInstanceReportResult(report_metadata=self.report_metadata, results=results)

View File

@ -692,7 +692,7 @@ def _find_process_instance_for_me_or_raise(
process_instance_id: int,
) -> ProcessInstanceModel:
"""_find_process_instance_for_me_or_raise."""
process_instance: ProcessInstanceModel = (
process_instance: Optional[ProcessInstanceModel] = (
ProcessInstanceModel.query.filter_by(id=process_instance_id)
.outerjoin(HumanTaskModel)
.outerjoin(

View File

@ -438,7 +438,7 @@ def process_model_create_with_natural_language(
def _get_file_from_request() -> FileStorage:
"""Get_file_from_request."""
request_file: FileStorage = connexion.request.files.get("file")
request_file: Optional[FileStorage] = connexion.request.files.get("file")
if not request_file:
raise ApiError(
error_code="no_file_given",

View File

@ -53,98 +53,105 @@ class ProcessInstanceReportService:
}
system_report_completed_instances_initiated_by_me: ReportMetadata = {
"columns": [
{"Header": "id", "accessor": "id"},
{"Header": "id", "accessor": "id", "filterable": False},
{
"Header": "process_model_display_name",
"accessor": "process_model_display_name",
"filterable": False,
},
{"Header": "start_in_seconds", "accessor": "start_in_seconds"},
{"Header": "end_in_seconds", "accessor": "end_in_seconds"},
{"Header": "status", "accessor": "status"},
{"Header": "start_in_seconds", "accessor": "start_in_seconds", "filterable": False},
{"Header": "end_in_seconds", "accessor": "end_in_seconds", "filterable": False},
{"Header": "status", "accessor": "status", "filterable": False},
],
"filter_by": [
{"field_name": "initiated_by_me", "field_value": True},
{"field_name": "process_status", "field_value": terminal_status_values},
{"field_name": "initiated_by_me", "field_value": True, "operator": "equals"},
{"field_name": "process_status", "field_value": terminal_status_values, "operator": "equals"},
],
"order_by": ["-start_in_seconds", "-id"],
}
system_report_completed_instances_with_tasks_completed_by_me: ReportMetadata = {
"columns": cls.builtin_column_options(),
"filter_by": [
{"field_name": "with_tasks_completed_by_me", "field_value": True},
{"field_name": "process_status", "field_value": terminal_status_values},
{"field_name": "with_tasks_completed_by_me", "field_value": True, "operator": "equals"},
{"field_name": "process_status", "field_value": terminal_status_values, "operator": "equals"},
],
"order_by": ["-start_in_seconds", "-id"],
}
system_report_completed_instances: ReportMetadata = {
"columns": cls.builtin_column_options(),
"filter_by": [
{"field_name": "process_status", "field_value": terminal_status_values},
{"field_name": "process_status", "field_value": terminal_status_values, "operator": "equals"},
],
"order_by": ["-start_in_seconds", "-id"],
}
system_report_in_progress_instances_initiated_by_me: ReportMetadata = {
"columns": [
{"Header": "id", "accessor": "id"},
{"Header": "id", "accessor": "id", "filterable": False},
{
"Header": "process_model_display_name",
"accessor": "process_model_display_name",
"filterable": False,
},
{"Header": "Task", "accessor": "task_title"},
{"Header": "Waiting For", "accessor": "waiting_for"},
{"Header": "Started", "accessor": "start_in_seconds"},
{"Header": "Last Updated", "accessor": "task_updated_at_in_seconds"},
{"Header": "status", "accessor": "status"},
{"Header": "Task", "accessor": "task_title", "filterable": False},
{"Header": "Waiting For", "accessor": "waiting_for", "filterable": False},
{"Header": "Started", "accessor": "start_in_seconds", "filterable": False},
{"Header": "Last Updated", "accessor": "task_updated_at_in_seconds", "filterable": False},
{"Header": "status", "accessor": "status", "filterable": False},
],
"filter_by": [
{"field_name": "initiated_by_me", "field_value": True},
{"field_name": "process_status", "field_value": non_terminal_status_values},
{"field_name": "initiated_by_me", "field_value": True, "operator": "equals"},
{"field_name": "process_status", "field_value": non_terminal_status_values, "operator": "equals"},
{
"field_name": "with_oldest_open_task",
"field_value": True,
"operator": "equals",
},
],
"order_by": ["-start_in_seconds", "-id"],
}
system_report_in_progress_instances_with_tasks_for_me: ReportMetadata = {
"columns": [
{"Header": "id", "accessor": "id"},
{"Header": "id", "accessor": "id", "filterable": False},
{
"Header": "process_model_display_name",
"accessor": "process_model_display_name",
"filterable": False,
},
{"Header": "Task", "accessor": "task_title"},
{"Header": "Started By", "accessor": "process_initiator_username"},
{"Header": "Started", "accessor": "start_in_seconds"},
{"Header": "Last Updated", "accessor": "task_updated_at_in_seconds"},
{"Header": "Task", "accessor": "task_title", "filterable": False},
{"Header": "Started By", "accessor": "process_initiator_username", "filterable": False},
{"Header": "Started", "accessor": "start_in_seconds", "filterable": False},
{"Header": "Last Updated", "accessor": "task_updated_at_in_seconds", "filterable": False},
],
"filter_by": [
{"field_name": "with_tasks_i_can_complete", "field_value": True},
{"field_name": "process_status", "field_value": active_status_values},
{"field_name": "with_tasks_i_can_complete", "field_value": True, "operator": "equals"},
{"field_name": "process_status", "field_value": active_status_values, "operator": "equals"},
{
"field_name": "with_oldest_open_task",
"field_value": True,
"operator": "equals",
},
],
"order_by": ["-start_in_seconds", "-id"],
}
system_report_in_progress_instances_with_tasks: ReportMetadata = {
"columns": [
{"Header": "id", "accessor": "id"},
{"Header": "id", "accessor": "id", "filterable": False},
{
"Header": "process_model_display_name",
"accessor": "process_model_display_name",
"filterable": False,
},
{"Header": "Task", "accessor": "task_title"},
{"Header": "Started By", "accessor": "process_initiator_username"},
{"Header": "Started", "accessor": "start_in_seconds"},
{"Header": "Last Updated", "accessor": "task_updated_at_in_seconds"},
{"Header": "Task", "accessor": "task_title", "filterable": False},
{"Header": "Started By", "accessor": "process_initiator_username", "filterable": False},
{"Header": "Started", "accessor": "start_in_seconds", "filterable": False},
{"Header": "Last Updated", "accessor": "task_updated_at_in_seconds", "filterable": False},
],
"filter_by": [
{"field_name": "process_status", "field_value": active_status_values},
{"field_name": "process_status", "field_value": active_status_values, "operator": "equals"},
{
"field_name": "with_oldest_open_task",
"field_value": True,
"operator": "equals",
},
],
"order_by": ["-start_in_seconds", "-id"],
@ -165,7 +172,8 @@ class ProcessInstanceReportService:
}
if metadata_key not in temp_system_metadata_map:
return None
return temp_system_metadata_map[metadata_key]
return_value: ReportMetadata = temp_system_metadata_map[metadata_key]
return return_value
@classmethod
def compile_report(cls, report_metadata: ReportMetadata, user: UserModel) -> None:
@ -173,7 +181,9 @@ class ProcessInstanceReportService:
old_filters = copy.deepcopy(report_metadata["filter_by"])
for filter in old_filters:
if filter["field_name"] == "initiated_by_me":
compiled_filters.append({"field_name": "process_initiator_username", "field_value": user.username})
compiled_filters.append(
{"field_name": "process_initiator_username", "field_value": user.username, "operator": "equals"}
)
else:
compiled_filters.append(filter)

View File

@ -792,7 +792,6 @@ class TestProcessApi(BaseTest):
content_type="multipart/form-data",
headers=self.logged_in_headers(with_super_admin_user),
)
assert response.status_code == 400
assert response.json is not None
assert response.json["error_code"] == "no_file_given"
@ -1853,7 +1852,13 @@ class TestProcessApi(BaseTest):
# Without filtering we should get all 5 instances
report_metadata_body: ReportMetadata = {
"filter_by": [{"field_name": "process_model_identifier", "field_value": process_model_identifier}],
"filter_by": [
{
"field_name": "process_model_identifier",
"field_value": process_model_identifier,
"operator": "equals",
}
],
"columns": [],
"order_by": [],
}
@ -1868,8 +1873,16 @@ class TestProcessApi(BaseTest):
for i in range(5):
report_metadata_body = {
"filter_by": [
{"field_name": "process_model_identifier", "field_value": process_model_identifier},
{"field_name": "process_status", "field_value": ProcessInstanceStatus[statuses[i]].value},
{
"field_name": "process_model_identifier",
"field_value": process_model_identifier,
"operator": "equals",
},
{
"field_name": "process_status",
"field_value": ProcessInstanceStatus[statuses[i]].value,
"operator": "equals",
},
],
"columns": [],
"order_by": [],
@ -1883,8 +1896,12 @@ class TestProcessApi(BaseTest):
report_metadata_body = {
"filter_by": [
{"field_name": "process_model_identifier", "field_value": process_model_identifier},
{"field_name": "process_status", "field_value": "not_started,complete"},
{
"field_name": "process_model_identifier",
"field_value": process_model_identifier,
"operator": "equals",
},
{"field_name": "process_status", "field_value": "not_started,complete", "operator": "equals"},
],
"columns": [],
"order_by": [],
@ -1900,7 +1917,7 @@ class TestProcessApi(BaseTest):
# filter by start/end seconds
# start > 1000 - this should eliminate the first
report_metadata_body = {
"filter_by": [{"field_name": "start_from", "field_value": 1001}],
"filter_by": [{"field_name": "start_from", "field_value": 1001, "operator": "equals"}],
"columns": [],
"order_by": [],
}
@ -1920,8 +1937,8 @@ class TestProcessApi(BaseTest):
# start > 2000, end < 5000 - this should eliminate the first 2 and the last
report_metadata_body = {
"filter_by": [
{"field_name": "start_from", "field_value": 2001},
{"field_name": "end_to", "field_value": 5999},
{"field_name": "start_from", "field_value": 2001, "operator": "equals"},
{"field_name": "end_to", "field_value": 5999, "operator": "equals"},
],
"columns": [],
"order_by": [],
@ -1937,8 +1954,8 @@ class TestProcessApi(BaseTest):
# start > 1000, start < 4000 - this should eliminate the first and the last 2
report_metadata_body = {
"filter_by": [
{"field_name": "start_from", "field_value": 1001},
{"field_name": "start_to", "field_value": 3999},
{"field_name": "start_from", "field_value": 1001, "operator": "equals"},
{"field_name": "start_to", "field_value": 3999, "operator": "equals"},
],
"columns": [],
"order_by": [],
@ -1954,8 +1971,8 @@ class TestProcessApi(BaseTest):
# end > 2000, end < 6000 - this should eliminate the first and the last
report_metadata_body = {
"filter_by": [
{"field_name": "end_from", "field_value": 2001},
{"field_name": "end_to", "field_value": 5999},
{"field_name": "end_from", "field_value": 2001, "operator": "equals"},
{"field_name": "end_to", "field_value": 5999, "operator": "equals"},
],
"columns": [],
"order_by": [],