Run event payloads data migration from background processor (#399)
* move data migration code out of bin so it can be reused in background processor * sleep for 5 minutes and update bpmn js to pull in some fixes from elizabeth * update spiff to pull in parser update to make it act like before --------- Co-authored-by: burnettk <burnettk@users.noreply.github.com>
This commit is contained in:
parent
c4c87855c9
commit
ce23480872
|
@ -1,203 +1,21 @@
|
|||
import copy
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
from hashlib import sha256
|
||||
import time
|
||||
|
||||
from spiffworkflow_backend import create_app
|
||||
from spiffworkflow_backend.models.db import db
|
||||
from spiffworkflow_backend.models.task import Task
|
||||
from spiffworkflow_backend.models.task import TaskModel # noqa: F401
|
||||
from spiffworkflow_backend.models.task_definition import TaskDefinitionModel
|
||||
from sqlalchemy import or_
|
||||
from sqlalchemy.orm.attributes import flag_modified
|
||||
from spiffworkflow_backend.data_migrations.version_1_3 import VersionOneThree
|
||||
|
||||
|
||||
class VersionOneThree:
|
||||
"""Migrates data in the database to be compatible with SpiffWorkflow at git revision ebcdde95.
|
||||
def main() -> None:
|
||||
app = create_app()
|
||||
start_time = time.time()
|
||||
|
||||
Converts migration file from SpiffWorkflow to work with backend's db:
|
||||
https://github.com/sartography/SpiffWorkflow/blob/main/SpiffWorkflow/bpmn/serializer/migration/version_1_3.py
|
||||
"""
|
||||
def run(self) -> None:
|
||||
os.environ["SPIFFWORKFLOW_BACKEND_ENV"] = "local_development"
|
||||
if os.environ.get("SPIFFWORKFLOW_BACKEND_BPMN_SPEC_ABSOLUTE_DIR") is None:
|
||||
os.environ["SPIFFWORKFLOW_BACKEND_BPMN_SPEC_ABSOLUTE_DIR"] = "hey"
|
||||
flask_env_key = "FLASK_SESSION_SECRET_KEY"
|
||||
os.environ[flask_env_key] = "whatevs"
|
||||
app = create_app()
|
||||
with app.app_context():
|
||||
task_definitions = self.get_relevant_task_definitions()
|
||||
for task_definition in task_definitions:
|
||||
self.process_task_definition(task_definition)
|
||||
relating_task_models = TaskModel.query.filter_by(task_definition_id=task_definition.id).all()
|
||||
for task_model in relating_task_models:
|
||||
self.process_task_model(task_model, task_definition)
|
||||
with app.app_context():
|
||||
VersionOneThree().run()
|
||||
|
||||
task_definitions_with_events = TaskDefinitionModel.query.filter(
|
||||
or_(
|
||||
TaskDefinitionModel.typename.like("%Event%"), # type: ignore
|
||||
TaskDefinitionModel.typename.in_(["SendTask", "ReceiveTask"]), # type: ignore
|
||||
)
|
||||
).all()
|
||||
for tdwe in task_definitions_with_events:
|
||||
self.update_event_definitions(tdwe)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
def get_relevant_task_definitions(self) -> list[TaskDefinitionModel]:
|
||||
task_definitions: list[TaskDefinitionModel] = TaskDefinitionModel.query.filter_by(
|
||||
typename="_BoundaryEventParent"
|
||||
).all()
|
||||
return task_definitions
|
||||
|
||||
def process_task_definition(self, task_definition: TaskDefinitionModel) -> None:
|
||||
task_definition.typename = "BoundaryEventSplit"
|
||||
task_definition.bpmn_identifier = task_definition.bpmn_identifier.replace(
|
||||
"BoundaryEventParent", "BoundaryEventSplit"
|
||||
)
|
||||
|
||||
properties_json = copy.copy(task_definition.properties_json)
|
||||
properties_json.pop("main_child_task_spec")
|
||||
properties_json["typename"] = task_definition.typename
|
||||
properties_json["name"] = task_definition.bpmn_identifier
|
||||
task_definition.properties_json = properties_json
|
||||
flag_modified(task_definition, "properties_json") # type: ignore
|
||||
db.session.add(task_definition)
|
||||
|
||||
join_properties_json = {
|
||||
"name": task_definition.bpmn_identifier.replace("BoundaryEventSplit", "BoundaryEventJoin"),
|
||||
"manual": False,
|
||||
"bpmn_id": None,
|
||||
"lookahead": 2,
|
||||
"inputs": properties_json["outputs"],
|
||||
"outputs": [],
|
||||
"split_task": task_definition.bpmn_identifier,
|
||||
"threshold": None,
|
||||
"cancel": True,
|
||||
"typename": "BoundaryEventJoin",
|
||||
}
|
||||
|
||||
join_task_definition = TaskDefinitionModel(
|
||||
bpmn_process_definition_id=task_definition.bpmn_process_definition_id,
|
||||
bpmn_identifier=join_properties_json["name"],
|
||||
typename=join_properties_json["typename"],
|
||||
properties_json=join_properties_json,
|
||||
)
|
||||
db.session.add(join_task_definition)
|
||||
|
||||
for parent_bpmn_identifier in properties_json["inputs"]:
|
||||
parent_task_definition = TaskDefinitionModel.query.filter_by(
|
||||
bpmn_identifier=parent_bpmn_identifier,
|
||||
bpmn_process_definition_id=task_definition.bpmn_process_definition_id,
|
||||
).first()
|
||||
parent_task_definition.properties_json["outputs"] = [
|
||||
name.replace("BoundaryEventParent", "BoundaryEventSplit")
|
||||
for name in parent_task_definition.properties_json["outputs"]
|
||||
]
|
||||
flag_modified(parent_task_definition, "properties_json") # type: ignore
|
||||
db.session.add(parent_task_definition)
|
||||
|
||||
for child_bpmn_identifier in properties_json["outputs"]:
|
||||
child_task_definition = TaskDefinitionModel.query.filter_by(
|
||||
bpmn_identifier=child_bpmn_identifier,
|
||||
bpmn_process_definition_id=task_definition.bpmn_process_definition_id,
|
||||
).first()
|
||||
child_task_definition.properties_json["outputs"].append(join_task_definition.bpmn_identifier)
|
||||
child_task_definition.properties_json["inputs"] = [
|
||||
name.replace("BoundaryEventParent", "BoundaryEventSplit")
|
||||
for name in child_task_definition.properties_json["inputs"]
|
||||
]
|
||||
flag_modified(child_task_definition, "properties_json") # type: ignore
|
||||
db.session.add(child_task_definition)
|
||||
|
||||
def process_task_model(self, task_model: TaskModel, task_definition: TaskDefinitionModel) -> None:
|
||||
task_model.properties_json["task_spec"] = task_definition.bpmn_identifier
|
||||
flag_modified(task_model, "properties_json") # type: ignore
|
||||
db.session.add(task_model)
|
||||
|
||||
child_task_models = []
|
||||
all_children_completed = True
|
||||
|
||||
# Ruff keeps complaining unless it's done like this
|
||||
blank_json = json.dumps({})
|
||||
blank_json_data_hash = sha256(blank_json.encode("utf8")).hexdigest()
|
||||
|
||||
for child_task_guid in task_model.properties_json["children"]:
|
||||
child_task_model = TaskModel.query.filter_by(guid=child_task_guid).first()
|
||||
if child_task_model is None:
|
||||
continue
|
||||
if child_task_model.state not in ["COMPLETED", "CANCELLED"]:
|
||||
all_children_completed = False
|
||||
child_task_models.append(child_task_model)
|
||||
|
||||
for child_task_model in child_task_models:
|
||||
if child_task_model.state == "CANCELLED":
|
||||
# Cancelled tasks don't have children
|
||||
continue
|
||||
|
||||
new_task_state = None
|
||||
start_in_seconds = child_task_model.start_in_seconds
|
||||
end_in_seconds = None
|
||||
|
||||
if child_task_model.state in ["MAYBE", "LIKELY", "FUTURE"]:
|
||||
new_task_state = child_task_model.state
|
||||
elif child_task_model.state in ["WAITING", "READY", "STARTED"]:
|
||||
new_task_state = "FUTURE"
|
||||
elif child_task_model.state == "COMPLETED":
|
||||
if all_children_completed:
|
||||
new_task_state = "COMPLETED"
|
||||
end_in_seconds = child_task_model.end_in_seconds
|
||||
else:
|
||||
new_task_state = "WAITING"
|
||||
elif child_task_model.state == "ERROR":
|
||||
new_task_state = "WAITING"
|
||||
else:
|
||||
raise Exception(f"Unknown state: {child_task_model.state} for {child_task_model.guild}")
|
||||
|
||||
new_task_properties_json = {
|
||||
"id": str(uuid.uuid4()),
|
||||
"parent": child_task_model.guid,
|
||||
"children": [],
|
||||
"state": Task.task_state_name_to_int(new_task_state),
|
||||
"task_spec": task_definition.bpmn_identifier.replace("BoundaryEventSplit", "BoundaryEventJoin"),
|
||||
"last_state_change": None,
|
||||
"triggered": False,
|
||||
"internal_data": {},
|
||||
}
|
||||
|
||||
new_task_model = TaskModel(
|
||||
guid=new_task_properties_json["id"],
|
||||
bpmn_process_id=task_model.bpmn_process_id,
|
||||
process_instance_id=task_model.process_instance_id,
|
||||
task_definition_id=task_model.task_definition_id,
|
||||
state=new_task_state,
|
||||
properties_json=new_task_properties_json,
|
||||
start_in_seconds=start_in_seconds,
|
||||
end_in_seconds=end_in_seconds,
|
||||
json_data_hash=blank_json_data_hash,
|
||||
python_env_data_hash=blank_json_data_hash,
|
||||
)
|
||||
db.session.add(new_task_model)
|
||||
|
||||
child_task_model.properties_json["children"].append(new_task_model.guid)
|
||||
flag_modified(child_task_model, "properties_json") # type: ignore
|
||||
db.session.add(child_task_model)
|
||||
|
||||
def update_event_definitions(self, task_definition: TaskDefinitionModel) -> None:
|
||||
if "event_definition" in task_definition.properties_json:
|
||||
properties_json = copy.copy(task_definition.properties_json)
|
||||
properties_json["event_definition"].pop("internal", None)
|
||||
properties_json["event_definition"].pop("external", None)
|
||||
if "escalation_code" in properties_json["event_definition"]:
|
||||
properties_json["event_definition"]["code"] = properties_json["event_definition"].pop(
|
||||
"escalation_code"
|
||||
)
|
||||
if "error_code" in properties_json["event_definition"]:
|
||||
properties_json["event_definition"]["code"] = properties_json["event_definition"].pop("error_code")
|
||||
task_definition.properties_json = properties_json
|
||||
flag_modified(task_definition, "properties_json") # type: ignore
|
||||
db.session.add(task_definition)
|
||||
end_time = time.time()
|
||||
print(
|
||||
f"done running data migration from ./bin/data_migrations/version_1_3.py. took {end_time - start_time} seconds"
|
||||
)
|
||||
|
||||
|
||||
VersionOneThree().run()
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -4,16 +4,29 @@ import time
|
|||
from apscheduler.schedulers.background import BlockingScheduler # type: ignore
|
||||
from spiffworkflow_backend import create_app
|
||||
from spiffworkflow_backend import start_scheduler
|
||||
from spiffworkflow_backend.data_migrations.version_1_3 import VersionOneThree
|
||||
from spiffworkflow_backend.helpers.db_helper import try_to_connect
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Main."""
|
||||
seconds_to_wait = 300
|
||||
print(f"sleeping for {seconds_to_wait} seconds to give the api container time to run the migration")
|
||||
time.sleep(seconds_to_wait)
|
||||
print("done sleeping")
|
||||
|
||||
print("running data migration from background processor")
|
||||
app = create_app()
|
||||
start_time = time.time()
|
||||
|
||||
with app.app_context():
|
||||
try_to_connect(start_time)
|
||||
VersionOneThree().run()
|
||||
|
||||
end_time = time.time()
|
||||
print(
|
||||
f"done running data migration from background processor. took {end_time - start_time} seconds. starting"
|
||||
" scheduler"
|
||||
)
|
||||
start_scheduler(app, BlockingScheduler)
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "alembic"
|
||||
version = "1.10.3"
|
||||
description = "A database migration tool for SQLAlchemy."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -23,6 +24,7 @@ tz = ["python-dateutil"]
|
|||
name = "aniso8601"
|
||||
version = "9.0.1"
|
||||
description = "A library for parsing ISO 8601 strings."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -37,6 +39,7 @@ dev = ["black", "coverage", "isort", "pre-commit", "pyenchant", "pylint"]
|
|||
name = "apscheduler"
|
||||
version = "3.10.1"
|
||||
description = "In-process task scheduler with Cron-like capabilities"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
|
@ -48,7 +51,7 @@ files = [
|
|||
pytz = "*"
|
||||
setuptools = ">=0.7"
|
||||
six = ">=1.4.0"
|
||||
tzlocal = ">=2.0,<3.dev0 || >=4.dev0"
|
||||
tzlocal = ">=2.0,<3.0.0 || >=4.0.0"
|
||||
|
||||
[package.extras]
|
||||
doc = ["sphinx", "sphinx-rtd-theme"]
|
||||
|
@ -66,6 +69,7 @@ zookeeper = ["kazoo"]
|
|||
name = "attrs"
|
||||
version = "22.2.0"
|
||||
description = "Classes Without Boilerplate"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
|
@ -84,6 +88,7 @@ tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy
|
|||
name = "bandit"
|
||||
version = "1.7.2"
|
||||
description = "Security oriented static analyser for python code."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -106,6 +111,7 @@ yaml = ["PyYAML"]
|
|||
name = "bcrypt"
|
||||
version = "4.0.1"
|
||||
description = "Modern password hashing for your software and your servers"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
|
@ -140,6 +146,7 @@ typecheck = ["mypy"]
|
|||
name = "black"
|
||||
version = "22.12.0"
|
||||
description = "The uncompromising code formatter."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -174,6 +181,7 @@ uvloop = ["uvloop (>=0.15.2)"]
|
|||
name = "blinker"
|
||||
version = "1.6.2"
|
||||
description = "Fast, simple object-to-object and broadcast signaling"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -185,6 +193,7 @@ files = [
|
|||
name = "certifi"
|
||||
version = "2022.12.7"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
|
@ -196,6 +205,7 @@ files = [
|
|||
name = "cffi"
|
||||
version = "1.15.1"
|
||||
description = "Foreign Function Interface for Python calling C code."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -272,6 +282,7 @@ pycparser = "*"
|
|||
name = "cfgv"
|
||||
version = "3.3.1"
|
||||
description = "Validate configuration and produce human readable error messages."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1"
|
||||
files = [
|
||||
|
@ -283,6 +294,7 @@ files = [
|
|||
name = "charset-normalizer"
|
||||
version = "3.1.0"
|
||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
files = [
|
||||
|
@ -367,6 +379,7 @@ files = [
|
|||
name = "click"
|
||||
version = "8.1.3"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -381,6 +394,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
|||
name = "clickclick"
|
||||
version = "20.10.2"
|
||||
description = "Click utility functions"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -396,6 +410,7 @@ PyYAML = ">=3.11"
|
|||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
files = [
|
||||
|
@ -407,6 +422,7 @@ files = [
|
|||
name = "configparser"
|
||||
version = "5.3.0"
|
||||
description = "Updated configparser from stdlib for earlier Pythons."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -422,6 +438,7 @@ testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-chec
|
|||
name = "connexion"
|
||||
version = "2.14.1"
|
||||
description = "Connexion - API first applications with OpenAPI/Swagger and Flask"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
|
@ -452,6 +469,7 @@ tests = ["MarkupSafe (>=0.23)", "aiohttp (>=2.3.10,<4)", "aiohttp-jinja2 (>=0.14
|
|||
name = "coverage"
|
||||
version = "6.5.0"
|
||||
description = "Code coverage measurement for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -517,6 +535,7 @@ toml = ["tomli"]
|
|||
name = "cryptography"
|
||||
version = "41.0.1"
|
||||
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -558,6 +577,7 @@ test-randomorder = ["pytest-randomly"]
|
|||
name = "dateparser"
|
||||
version = "1.1.8"
|
||||
description = "Date parsing library designed to parse dates from HTML pages"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -580,6 +600,7 @@ langdetect = ["langdetect"]
|
|||
name = "distlib"
|
||||
version = "0.3.6"
|
||||
description = "Distribution utilities"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -591,6 +612,7 @@ files = [
|
|||
name = "dparse"
|
||||
version = "0.6.2"
|
||||
description = "A parser for Python dependency files"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
|
@ -610,6 +632,7 @@ pipenv = ["pipenv"]
|
|||
name = "exceptiongroup"
|
||||
version = "1.1.1"
|
||||
description = "Backport of PEP 654 (exception groups)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -624,6 +647,7 @@ test = ["pytest (>=6)"]
|
|||
name = "execnet"
|
||||
version = "1.9.0"
|
||||
description = "execnet: rapid multi-Python deployment"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
files = [
|
||||
|
@ -638,6 +662,7 @@ testing = ["pre-commit"]
|
|||
name = "filelock"
|
||||
version = "3.11.0"
|
||||
description = "A platform independent file lock."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -653,6 +678,7 @@ testing = ["covdefaults (>=2.3)", "coverage (>=7.2.2)", "diff-cover (>=7.5)", "p
|
|||
name = "flask"
|
||||
version = "2.2.5"
|
||||
description = "A simple framework for building complex web applications."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -674,6 +700,7 @@ dotenv = ["python-dotenv"]
|
|||
name = "flask-admin"
|
||||
version = "1.6.1"
|
||||
description = "Simple and extensible admin interface framework for Flask"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
|
@ -693,6 +720,7 @@ azure = ["azure-storage-blob"]
|
|||
name = "flask-bcrypt"
|
||||
version = "1.0.1"
|
||||
description = "Brcrypt hashing for Flask."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -708,6 +736,7 @@ Flask = "*"
|
|||
name = "flask-cors"
|
||||
version = "3.0.10"
|
||||
description = "A Flask extension adding a decorator for CORS support"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -723,6 +752,7 @@ Six = "*"
|
|||
name = "flask-jwt-extended"
|
||||
version = "4.4.4"
|
||||
description = "Extended JWT integration with Flask"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7,<4"
|
||||
files = [
|
||||
|
@ -742,6 +772,7 @@ asymmetric-crypto = ["cryptography (>=3.3.1)"]
|
|||
name = "flask-mail"
|
||||
version = "0.9.1"
|
||||
description = "Flask extension for sending email"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -756,6 +787,7 @@ Flask = "*"
|
|||
name = "flask-marshmallow"
|
||||
version = "0.15.0"
|
||||
description = "Flask + marshmallow for beautiful APIs"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -778,6 +810,7 @@ tests = ["flask-sqlalchemy (>=3.0.0)", "marshmallow-sqlalchemy (>=0.28.2)", "moc
|
|||
name = "flask-migrate"
|
||||
version = "4.0.4"
|
||||
description = "SQLAlchemy database migrations for Flask applications using Alembic."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
|
@ -794,6 +827,7 @@ Flask-SQLAlchemy = ">=1.0"
|
|||
name = "flask-restful"
|
||||
version = "0.3.9"
|
||||
description = "Simple framework for creating REST APIs"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -814,6 +848,7 @@ docs = ["sphinx"]
|
|||
name = "flask-simple-crypt"
|
||||
version = "0.3.3"
|
||||
description = "Flask extension based on simple-crypt that allows simple, secure encryption and decryption for Python."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -829,6 +864,7 @@ pycryptodome = "*"
|
|||
name = "flask-sqlalchemy"
|
||||
version = "3.0.3"
|
||||
description = "Add SQLAlchemy support to your Flask application."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -844,6 +880,7 @@ SQLAlchemy = ">=1.4.18"
|
|||
name = "gitdb"
|
||||
version = "4.0.10"
|
||||
description = "Git Object Database"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -858,6 +895,7 @@ smmap = ">=3.0.1,<6"
|
|||
name = "gitpython"
|
||||
version = "3.1.31"
|
||||
description = "GitPython is a Python library used to interact with Git repositories"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -872,6 +910,7 @@ gitdb = ">=4.0.1,<5"
|
|||
name = "greenlet"
|
||||
version = "2.0.2"
|
||||
description = "Lightweight in-process concurrent programming"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
|
||||
files = [
|
||||
|
@ -945,6 +984,7 @@ test = ["objgraph", "psutil"]
|
|||
name = "gunicorn"
|
||||
version = "20.1.0"
|
||||
description = "WSGI HTTP Server for UNIX"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
|
@ -965,6 +1005,7 @@ tornado = ["tornado (>=0.2)"]
|
|||
name = "identify"
|
||||
version = "2.5.22"
|
||||
description = "File identification library for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -979,6 +1020,7 @@ license = ["ukkonen"]
|
|||
name = "idna"
|
||||
version = "3.4"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
|
@ -990,6 +1032,7 @@ files = [
|
|||
name = "inflection"
|
||||
version = "0.5.1"
|
||||
description = "A port of Ruby on Rails inflector to Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
|
@ -1001,6 +1044,7 @@ files = [
|
|||
name = "iniconfig"
|
||||
version = "2.0.0"
|
||||
description = "brain-dead simple config-ini parsing"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1012,6 +1056,7 @@ files = [
|
|||
name = "itsdangerous"
|
||||
version = "2.1.2"
|
||||
description = "Safely pass data to untrusted environments and back."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1023,6 +1068,7 @@ files = [
|
|||
name = "jinja2"
|
||||
version = "3.1.2"
|
||||
description = "A very fast and expressive template engine."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1040,6 +1086,7 @@ i18n = ["Babel (>=2.7)"]
|
|||
name = "jsonschema"
|
||||
version = "4.17.3"
|
||||
description = "An implementation of JSON Schema validation for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1059,6 +1106,7 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-
|
|||
name = "lxml"
|
||||
version = "4.9.2"
|
||||
description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*"
|
||||
files = [
|
||||
|
@ -1151,6 +1199,7 @@ source = ["Cython (>=0.29.7)"]
|
|||
name = "mako"
|
||||
version = "1.2.4"
|
||||
description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1170,6 +1219,7 @@ testing = ["pytest"]
|
|||
name = "markupsafe"
|
||||
version = "2.1.2"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1229,6 +1279,7 @@ files = [
|
|||
name = "marshmallow"
|
||||
version = "3.19.0"
|
||||
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1249,6 +1300,7 @@ tests = ["pytest", "pytz", "simplejson"]
|
|||
name = "marshmallow-enum"
|
||||
version = "1.5.1"
|
||||
description = "Enum field for Marshmallow"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -1263,6 +1315,7 @@ marshmallow = ">=2.0.0"
|
|||
name = "marshmallow-sqlalchemy"
|
||||
version = "0.29.0"
|
||||
description = "SQLAlchemy integration with the marshmallow (de)serialization library"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1285,6 +1338,7 @@ tests = ["pytest", "pytest-lazy-fixture (>=0.6.2)"]
|
|||
name = "mypy"
|
||||
version = "1.2.0"
|
||||
description = "Optional static typing for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1331,6 +1385,7 @@ reports = ["lxml"]
|
|||
name = "mypy-extensions"
|
||||
version = "1.0.0"
|
||||
description = "Type system extensions for programs checked with the mypy type checker."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
|
@ -1342,6 +1397,7 @@ files = [
|
|||
name = "mysqlclient"
|
||||
version = "2.2.0"
|
||||
description = "Python interface to MySQL"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
|
@ -1358,6 +1414,7 @@ files = [
|
|||
name = "nodeenv"
|
||||
version = "1.7.0"
|
||||
description = "Node.js virtual environment builder"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
|
||||
files = [
|
||||
|
@ -1372,6 +1429,7 @@ setuptools = "*"
|
|||
name = "packaging"
|
||||
version = "21.3"
|
||||
description = "Core utilities for Python packages"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
|
@ -1386,6 +1444,7 @@ pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
|
|||
name = "pathspec"
|
||||
version = "0.11.1"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1397,6 +1456,7 @@ files = [
|
|||
name = "pbr"
|
||||
version = "5.11.1"
|
||||
description = "Python Build Reasonableness"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.6"
|
||||
files = [
|
||||
|
@ -1408,6 +1468,7 @@ files = [
|
|||
name = "platformdirs"
|
||||
version = "3.2.0"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1423,6 +1484,7 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.2.2)", "pytest-
|
|||
name = "pluggy"
|
||||
version = "1.0.0"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
|
@ -1438,6 +1500,7 @@ testing = ["pytest", "pytest-benchmark"]
|
|||
name = "pre-commit"
|
||||
version = "2.21.0"
|
||||
description = "A framework for managing and maintaining multi-language pre-commit hooks."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1456,6 +1519,7 @@ virtualenv = ">=20.10.0"
|
|||
name = "pre-commit-hooks"
|
||||
version = "4.4.0"
|
||||
description = "Some out-of-the-box hooks for pre-commit."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1471,6 +1535,7 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
|||
name = "prometheus-client"
|
||||
version = "0.16.0"
|
||||
description = "Python client for the Prometheus monitoring system."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
|
@ -1485,6 +1550,7 @@ twisted = ["twisted"]
|
|||
name = "prometheus-flask-exporter"
|
||||
version = "0.22.3"
|
||||
description = "Prometheus metrics exporter for Flask"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -1500,6 +1566,7 @@ prometheus-client = "*"
|
|||
name = "psycopg2"
|
||||
version = "2.9.6"
|
||||
description = "psycopg2 - Python-PostgreSQL Database Adapter"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
|
@ -1522,6 +1589,7 @@ files = [
|
|||
name = "pycparser"
|
||||
version = "2.21"
|
||||
description = "C parser in Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
files = [
|
||||
|
@ -1533,6 +1601,7 @@ files = [
|
|||
name = "pycryptodome"
|
||||
version = "3.17"
|
||||
description = "Cryptographic library for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
files = [
|
||||
|
@ -1575,6 +1644,7 @@ files = [
|
|||
name = "pygments"
|
||||
version = "2.15.1"
|
||||
description = "Pygments is a syntax highlighting package written in Python."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1589,6 +1659,7 @@ plugins = ["importlib-metadata"]
|
|||
name = "pyjwt"
|
||||
version = "2.6.0"
|
||||
description = "JSON Web Token implementation in Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1606,6 +1677,7 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
|
|||
name = "pyparsing"
|
||||
version = "3.0.9"
|
||||
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6.8"
|
||||
files = [
|
||||
|
@ -1620,6 +1692,7 @@ diagrams = ["jinja2", "railroad-diagrams"]
|
|||
name = "pyrsistent"
|
||||
version = "0.19.3"
|
||||
description = "Persistent/Functional/Immutable data structures"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1656,6 +1729,7 @@ files = [
|
|||
name = "pytest"
|
||||
version = "7.2.2"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1679,6 +1753,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.
|
|||
name = "pytest-flask"
|
||||
version = "1.2.0"
|
||||
description = "A set of py.test fixtures to test Flask applications."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
|
@ -1698,6 +1773,7 @@ docs = ["Sphinx", "sphinx-rtd-theme"]
|
|||
name = "pytest-flask-sqlalchemy"
|
||||
version = "1.1.0"
|
||||
description = "A pytest plugin for preserving test isolation in Flask-SQlAlchemy using database transactions."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -1719,6 +1795,7 @@ tests = ["psycopg2-binary", "pytest (>=6.0.1)", "pytest-postgresql (>=2.4.0,<4.0
|
|||
name = "pytest-mock"
|
||||
version = "3.10.0"
|
||||
description = "Thin-wrapper around the mock package for easier use with pytest"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1736,6 +1813,7 @@ dev = ["pre-commit", "pytest-asyncio", "tox"]
|
|||
name = "pytest-xdist"
|
||||
version = "3.3.1"
|
||||
description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1756,6 +1834,7 @@ testing = ["filelock"]
|
|||
name = "python-dateutil"
|
||||
version = "2.8.2"
|
||||
description = "Extensions to the standard Python datetime module"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
||||
files = [
|
||||
|
@ -1770,6 +1849,7 @@ six = ">=1.5"
|
|||
name = "pytz"
|
||||
version = "2022.7.1"
|
||||
description = "World timezone definitions, modern and historical"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -1781,6 +1861,7 @@ files = [
|
|||
name = "pytz-deprecation-shim"
|
||||
version = "0.1.0.post0"
|
||||
description = "Shims to make deprecation of pytz easier"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
|
||||
files = [
|
||||
|
@ -1795,6 +1876,7 @@ tzdata = {version = "*", markers = "python_version >= \"3.6\""}
|
|||
name = "pyyaml"
|
||||
version = "6.0"
|
||||
description = "YAML parser and emitter for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
|
@ -1844,6 +1926,7 @@ files = [
|
|||
name = "regex"
|
||||
version = "2023.3.23"
|
||||
description = "Alternative regular expression module, to replace re."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
|
@ -1913,6 +1996,7 @@ files = [
|
|||
name = "requests"
|
||||
version = "2.31.0"
|
||||
description = "Python HTTP for Humans."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -1934,6 +2018,7 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
|||
name = "restrictedpython"
|
||||
version = "6.1"
|
||||
description = "RestrictedPython is a defined subset of the Python language which allows to provide a program input into a trusted environment."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6, <3.12"
|
||||
files = [
|
||||
|
@ -1949,6 +2034,7 @@ test = ["pytest", "pytest-mock"]
|
|||
name = "ruamel-yaml"
|
||||
version = "0.17.21"
|
||||
description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3"
|
||||
files = [
|
||||
|
@ -1967,6 +2053,7 @@ jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"]
|
|||
name = "ruamel-yaml-clib"
|
||||
version = "0.2.7"
|
||||
description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
|
@ -1977,7 +2064,8 @@ files = [
|
|||
{file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win32.whl", hash = "sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231"},
|
||||
{file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a"},
|
||||
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e"},
|
||||
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_12_6_arm64.whl", hash = "sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5"},
|
||||
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:1a6391a7cabb7641c32517539ca42cf84b87b667bad38b78d4d42dd23e957c81"},
|
||||
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:9c7617df90c1365638916b98cdd9be833d31d337dbcd722485597b43c4a215bf"},
|
||||
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94"},
|
||||
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win32.whl", hash = "sha256:f6d3d39611ac2e4f62c3128a9eed45f19a6608670c5a2f4f07f24e8de3441d38"},
|
||||
{file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:da538167284de58a52109a9b89b8f6a53ff8437dd6dc26d33b57bf6699153122"},
|
||||
|
@ -2012,6 +2100,7 @@ files = [
|
|||
name = "ruff"
|
||||
version = "0.0.270"
|
||||
description = "An extremely fast Python linter, written in Rust."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -2038,6 +2127,7 @@ files = [
|
|||
name = "safety"
|
||||
version = "2.3.5"
|
||||
description = "Checks installed dependencies for known vulnerabilities and licenses."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -2061,6 +2151,7 @@ gitlab = ["python-gitlab (>=1.3.0)"]
|
|||
name = "sentry-sdk"
|
||||
version = "1.19.1"
|
||||
description = "Python client for Sentry (https://sentry.io)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -2102,6 +2193,7 @@ tornado = ["tornado (>=5)"]
|
|||
name = "setuptools"
|
||||
version = "65.7.0"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -2118,6 +2210,7 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (
|
|||
name = "simplejson"
|
||||
version = "3.19.1"
|
||||
description = "Simple, fast, extensible JSON encoder/decoder for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
files = [
|
||||
|
@ -2212,6 +2305,7 @@ files = [
|
|||
name = "six"
|
||||
version = "1.16.0"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
files = [
|
||||
|
@ -2223,6 +2317,7 @@ files = [
|
|||
name = "smmap"
|
||||
version = "5.0.0"
|
||||
description = "A pure Python implementation of a sliding window memory map manager"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
|
@ -2234,6 +2329,7 @@ files = [
|
|||
name = "spiff-element-units"
|
||||
version = "0.3.0"
|
||||
description = ""
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
|
@ -2253,7 +2349,8 @@ files = [
|
|||
[[package]]
|
||||
name = "SpiffWorkflow"
|
||||
version = "2.0.0rc0"
|
||||
description = "A workflow framework and BPMN/DMN Processor"
|
||||
description = ""
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = []
|
||||
|
@ -2267,12 +2364,13 @@ lxml = "*"
|
|||
type = "git"
|
||||
url = "https://github.com/sartography/SpiffWorkflow"
|
||||
reference = "main"
|
||||
resolved_reference = "ebcdde95c2f59f67981add1eacf9f5e04520d50f"
|
||||
resolved_reference = "6b22a195b1841be3c8916e7826f8633173ff0a05"
|
||||
|
||||
[[package]]
|
||||
name = "sqlalchemy"
|
||||
version = "2.0.9"
|
||||
description = "Database Abstraction Library"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -2320,7 +2418,7 @@ files = [
|
|||
]
|
||||
|
||||
[package.dependencies]
|
||||
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\""}
|
||||
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""}
|
||||
typing-extensions = ">=4.2.0"
|
||||
|
||||
[package.extras]
|
||||
|
@ -2350,6 +2448,7 @@ sqlcipher = ["sqlcipher3-binary"]
|
|||
name = "sqlalchemy-stubs"
|
||||
version = "0.4"
|
||||
description = "SQLAlchemy stubs and mypy plugin"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = []
|
||||
|
@ -2369,6 +2468,7 @@ resolved_reference = "d1176931684ce5b327539cc9567d4a1cd8ef1efd"
|
|||
name = "stevedore"
|
||||
version = "5.0.0"
|
||||
description = "Manage dynamic plugins for Python applications"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
|
@ -2383,6 +2483,7 @@ pbr = ">=2.0.0,<2.1.0 || >2.1.0"
|
|||
name = "swagger-ui-bundle"
|
||||
version = "0.0.9"
|
||||
description = "swagger_ui_bundle - swagger-ui files in a pip package"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -2397,6 +2498,7 @@ Jinja2 = ">=2.0"
|
|||
name = "toml"
|
||||
version = "0.10.2"
|
||||
description = "Python Library for Tom's Obvious, Minimal Language"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
files = [
|
||||
|
@ -2408,6 +2510,7 @@ files = [
|
|||
name = "tomli"
|
||||
version = "2.0.1"
|
||||
description = "A lil' TOML parser"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -2419,6 +2522,7 @@ files = [
|
|||
name = "typeguard"
|
||||
version = "3.0.2"
|
||||
description = "Run-time type checker for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7.4"
|
||||
files = [
|
||||
|
@ -2437,6 +2541,7 @@ test = ["mypy (>=0.991)", "pytest (>=7)"]
|
|||
name = "types-click"
|
||||
version = "7.1.8"
|
||||
description = "Typing stubs for click"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -2448,6 +2553,7 @@ files = [
|
|||
name = "types-dateparser"
|
||||
version = "1.1.4.9"
|
||||
description = "Typing stubs for dateparser"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -2459,6 +2565,7 @@ files = [
|
|||
name = "types-flask"
|
||||
version = "1.1.6"
|
||||
description = "Typing stubs for Flask"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -2475,6 +2582,7 @@ types-Werkzeug = "*"
|
|||
name = "types-jinja2"
|
||||
version = "2.11.9"
|
||||
description = "Typing stubs for Jinja2"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -2489,6 +2597,7 @@ types-MarkupSafe = "*"
|
|||
name = "types-markupsafe"
|
||||
version = "1.1.10"
|
||||
description = "Typing stubs for MarkupSafe"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -2500,6 +2609,7 @@ files = [
|
|||
name = "types-pytz"
|
||||
version = "2022.7.1.2"
|
||||
description = "Typing stubs for pytz"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -2511,6 +2621,7 @@ files = [
|
|||
name = "types-pyyaml"
|
||||
version = "6.0.12.9"
|
||||
description = "Typing stubs for PyYAML"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -2522,6 +2633,7 @@ files = [
|
|||
name = "types-requests"
|
||||
version = "2.28.11.17"
|
||||
description = "Typing stubs for requests"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -2536,6 +2648,7 @@ types-urllib3 = "<1.27"
|
|||
name = "types-urllib3"
|
||||
version = "1.26.25.10"
|
||||
description = "Typing stubs for urllib3"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -2547,6 +2660,7 @@ files = [
|
|||
name = "types-werkzeug"
|
||||
version = "1.0.9"
|
||||
description = "Typing stubs for Werkzeug"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
|
@ -2558,6 +2672,7 @@ files = [
|
|||
name = "typing-extensions"
|
||||
version = "4.5.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.7+"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -2569,6 +2684,7 @@ files = [
|
|||
name = "tzdata"
|
||||
version = "2023.3"
|
||||
description = "Provider of IANA time zone data"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2"
|
||||
files = [
|
||||
|
@ -2580,6 +2696,7 @@ files = [
|
|||
name = "tzlocal"
|
||||
version = "4.3"
|
||||
description = "tzinfo object for the local timezone"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -2598,6 +2715,7 @@ devenv = ["black", "check-manifest", "flake8", "pyroma", "pytest (>=4.3)", "pyte
|
|||
name = "urllib3"
|
||||
version = "1.26.15"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
|
||||
files = [
|
||||
|
@ -2614,6 +2732,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
|||
name = "virtualenv"
|
||||
version = "20.21.0"
|
||||
description = "Virtual Python Environment builder"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -2634,6 +2753,7 @@ test = ["covdefaults (>=2.2.2)", "coverage (>=7.1)", "coverage-enable-subprocess
|
|||
name = "werkzeug"
|
||||
version = "2.3.4"
|
||||
description = "The comprehensive WSGI web application library."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
|
@ -2651,6 +2771,7 @@ watchdog = ["watchdog (>=2.3)"]
|
|||
name = "wtforms"
|
||||
version = "3.0.1"
|
||||
description = "Form validation and rendering for Python web development."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
|
@ -2668,6 +2789,7 @@ email = ["email-validator"]
|
|||
name = "xdoctest"
|
||||
version = "1.1.1"
|
||||
description = "A rewrite of the builtin doctest module"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
import copy
|
||||
import json
|
||||
import uuid
|
||||
from hashlib import sha256
|
||||
|
||||
from spiffworkflow_backend.models.db import db
|
||||
from spiffworkflow_backend.models.task import Task
|
||||
from spiffworkflow_backend.models.task import TaskModel # noqa: F401
|
||||
from spiffworkflow_backend.models.task_definition import TaskDefinitionModel
|
||||
from sqlalchemy import or_
|
||||
from sqlalchemy.orm.attributes import flag_modified
|
||||
|
||||
|
||||
class VersionOneThree:
|
||||
"""Migrates data in the database to be compatible with SpiffWorkflow at git revision ebcdde95.
|
||||
|
||||
Converts migration file from SpiffWorkflow to work with backend's db:
|
||||
https://github.com/sartography/SpiffWorkflow/blob/main/SpiffWorkflow/bpmn/serializer/migration/version_1_3.py
|
||||
"""
|
||||
|
||||
def run(self) -> None:
|
||||
print("start VersionOneThree.run")
|
||||
task_definitions = self.get_relevant_task_definitions()
|
||||
print(f"found relevant task_definitions: {len(task_definitions)}")
|
||||
for task_definition in task_definitions:
|
||||
self.process_task_definition(task_definition)
|
||||
relating_task_models = TaskModel.query.filter_by(task_definition_id=task_definition.id).all()
|
||||
for task_model in relating_task_models:
|
||||
self.process_task_model(task_model, task_definition)
|
||||
|
||||
task_definitions_with_events = TaskDefinitionModel.query.filter(
|
||||
or_(
|
||||
TaskDefinitionModel.typename.like("%Event%"), # type: ignore
|
||||
TaskDefinitionModel.typename.in_(["SendTask", "ReceiveTask"]), # type: ignore
|
||||
)
|
||||
).all()
|
||||
for tdwe in task_definitions_with_events:
|
||||
self.update_event_definitions(tdwe)
|
||||
|
||||
db.session.commit()
|
||||
print("end VersionOneThree.run")
|
||||
|
||||
def get_relevant_task_definitions(self) -> list[TaskDefinitionModel]:
|
||||
task_definitions: list[TaskDefinitionModel] = TaskDefinitionModel.query.filter_by(
|
||||
typename="_BoundaryEventParent"
|
||||
).all()
|
||||
return task_definitions
|
||||
|
||||
def process_task_definition(self, task_definition: TaskDefinitionModel) -> None:
|
||||
task_definition.typename = "BoundaryEventSplit"
|
||||
task_definition.bpmn_identifier = task_definition.bpmn_identifier.replace(
|
||||
"BoundaryEventParent", "BoundaryEventSplit"
|
||||
)
|
||||
|
||||
properties_json = copy.copy(task_definition.properties_json)
|
||||
properties_json.pop("main_child_task_spec")
|
||||
properties_json["typename"] = task_definition.typename
|
||||
properties_json["name"] = task_definition.bpmn_identifier
|
||||
task_definition.properties_json = properties_json
|
||||
flag_modified(task_definition, "properties_json") # type: ignore
|
||||
db.session.add(task_definition)
|
||||
|
||||
join_properties_json = {
|
||||
"name": task_definition.bpmn_identifier.replace("BoundaryEventSplit", "BoundaryEventJoin"),
|
||||
"manual": False,
|
||||
"bpmn_id": None,
|
||||
"lookahead": 2,
|
||||
"inputs": properties_json["outputs"],
|
||||
"outputs": [],
|
||||
"split_task": task_definition.bpmn_identifier,
|
||||
"threshold": None,
|
||||
"cancel": True,
|
||||
"typename": "BoundaryEventJoin",
|
||||
}
|
||||
|
||||
join_task_definition = TaskDefinitionModel(
|
||||
bpmn_process_definition_id=task_definition.bpmn_process_definition_id,
|
||||
bpmn_identifier=join_properties_json["name"],
|
||||
typename=join_properties_json["typename"],
|
||||
properties_json=join_properties_json,
|
||||
)
|
||||
db.session.add(join_task_definition)
|
||||
|
||||
for parent_bpmn_identifier in properties_json["inputs"]:
|
||||
parent_task_definition = TaskDefinitionModel.query.filter_by(
|
||||
bpmn_identifier=parent_bpmn_identifier,
|
||||
bpmn_process_definition_id=task_definition.bpmn_process_definition_id,
|
||||
).first()
|
||||
parent_task_definition.properties_json["outputs"] = [
|
||||
name.replace("BoundaryEventParent", "BoundaryEventSplit")
|
||||
for name in parent_task_definition.properties_json["outputs"]
|
||||
]
|
||||
flag_modified(parent_task_definition, "properties_json") # type: ignore
|
||||
db.session.add(parent_task_definition)
|
||||
|
||||
for child_bpmn_identifier in properties_json["outputs"]:
|
||||
child_task_definition = TaskDefinitionModel.query.filter_by(
|
||||
bpmn_identifier=child_bpmn_identifier,
|
||||
bpmn_process_definition_id=task_definition.bpmn_process_definition_id,
|
||||
).first()
|
||||
child_task_definition.properties_json["outputs"].append(join_task_definition.bpmn_identifier)
|
||||
child_task_definition.properties_json["inputs"] = [
|
||||
name.replace("BoundaryEventParent", "BoundaryEventSplit")
|
||||
for name in child_task_definition.properties_json["inputs"]
|
||||
]
|
||||
flag_modified(child_task_definition, "properties_json") # type: ignore
|
||||
db.session.add(child_task_definition)
|
||||
|
||||
def process_task_model(self, task_model: TaskModel, task_definition: TaskDefinitionModel) -> None:
|
||||
task_model.properties_json["task_spec"] = task_definition.bpmn_identifier
|
||||
flag_modified(task_model, "properties_json") # type: ignore
|
||||
db.session.add(task_model)
|
||||
|
||||
child_task_models = []
|
||||
all_children_completed = True
|
||||
|
||||
# Ruff keeps complaining unless it's done like this
|
||||
blank_json = json.dumps({})
|
||||
blank_json_data_hash = sha256(blank_json.encode("utf8")).hexdigest()
|
||||
|
||||
for child_task_guid in task_model.properties_json["children"]:
|
||||
child_task_model = TaskModel.query.filter_by(guid=child_task_guid).first()
|
||||
if child_task_model is None:
|
||||
continue
|
||||
if child_task_model.state not in ["COMPLETED", "CANCELLED"]:
|
||||
all_children_completed = False
|
||||
child_task_models.append(child_task_model)
|
||||
|
||||
for child_task_model in child_task_models:
|
||||
if child_task_model.state == "CANCELLED":
|
||||
# Cancelled tasks don't have children
|
||||
continue
|
||||
|
||||
new_task_state = None
|
||||
start_in_seconds = child_task_model.start_in_seconds
|
||||
end_in_seconds = None
|
||||
|
||||
if child_task_model.state in ["MAYBE", "LIKELY", "FUTURE"]:
|
||||
new_task_state = child_task_model.state
|
||||
elif child_task_model.state in ["WAITING", "READY", "STARTED"]:
|
||||
new_task_state = "FUTURE"
|
||||
elif child_task_model.state == "COMPLETED":
|
||||
if all_children_completed:
|
||||
new_task_state = "COMPLETED"
|
||||
end_in_seconds = child_task_model.end_in_seconds
|
||||
else:
|
||||
new_task_state = "WAITING"
|
||||
elif child_task_model.state == "ERROR":
|
||||
new_task_state = "WAITING"
|
||||
else:
|
||||
raise Exception(f"Unknown state: {child_task_model.state} for {child_task_model.guild}")
|
||||
|
||||
new_task_properties_json = {
|
||||
"id": str(uuid.uuid4()),
|
||||
"parent": child_task_model.guid,
|
||||
"children": [],
|
||||
"state": Task.task_state_name_to_int(new_task_state),
|
||||
"task_spec": task_definition.bpmn_identifier.replace("BoundaryEventSplit", "BoundaryEventJoin"),
|
||||
"last_state_change": None,
|
||||
"triggered": False,
|
||||
"internal_data": {},
|
||||
}
|
||||
|
||||
new_task_model = TaskModel(
|
||||
guid=new_task_properties_json["id"],
|
||||
bpmn_process_id=task_model.bpmn_process_id,
|
||||
process_instance_id=task_model.process_instance_id,
|
||||
task_definition_id=task_model.task_definition_id,
|
||||
state=new_task_state,
|
||||
properties_json=new_task_properties_json,
|
||||
start_in_seconds=start_in_seconds,
|
||||
end_in_seconds=end_in_seconds,
|
||||
json_data_hash=blank_json_data_hash,
|
||||
python_env_data_hash=blank_json_data_hash,
|
||||
)
|
||||
db.session.add(new_task_model)
|
||||
|
||||
child_task_model.properties_json["children"].append(new_task_model.guid)
|
||||
flag_modified(child_task_model, "properties_json") # type: ignore
|
||||
db.session.add(child_task_model)
|
||||
|
||||
def update_event_definitions(self, task_definition: TaskDefinitionModel) -> None:
|
||||
if "event_definition" in task_definition.properties_json:
|
||||
properties_json = copy.copy(task_definition.properties_json)
|
||||
properties_json["event_definition"].pop("internal", None)
|
||||
properties_json["event_definition"].pop("external", None)
|
||||
if "escalation_code" in properties_json["event_definition"]:
|
||||
properties_json["event_definition"]["code"] = properties_json["event_definition"].pop(
|
||||
"escalation_code"
|
||||
)
|
||||
if "error_code" in properties_json["event_definition"]:
|
||||
properties_json["event_definition"]["code"] = properties_json["event_definition"].pop("error_code")
|
||||
task_definition.properties_json = properties_json
|
||||
flag_modified(task_definition, "properties_json") # type: ignore
|
||||
db.session.add(task_definition)
|
|
@ -8351,7 +8351,7 @@
|
|||
},
|
||||
"node_modules/bpmn-js-spiffworkflow": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#1bc43155d5f10b69ebfcfeeb136e7badec21a4bf",
|
||||
"resolved": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#877424a55ab5e4f25a06ce480f0f5c0cf72ea150",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.4",
|
||||
|
@ -38314,7 +38314,7 @@
|
|||
}
|
||||
},
|
||||
"bpmn-js-spiffworkflow": {
|
||||
"version": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#1bc43155d5f10b69ebfcfeeb136e7badec21a4bf",
|
||||
"version": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#877424a55ab5e4f25a06ce480f0f5c0cf72ea150",
|
||||
"from": "bpmn-js-spiffworkflow@github:sartography/bpmn-js-spiffworkflow#main",
|
||||
"requires": {
|
||||
"inherits": "^2.0.4",
|
||||
|
|
Loading…
Reference in New Issue