From 942ffff41c65be0d33957cdc578123c5e601c7c4 Mon Sep 17 00:00:00 2001 From: burnettk Date: Thu, 9 Mar 2023 10:34:34 -0500 Subject: [PATCH 01/48] ignore ATTEMPTS when command is open, as it only makes sense for run --- spiffworkflow-frontend/bin/cypress_pilot | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spiffworkflow-frontend/bin/cypress_pilot b/spiffworkflow-frontend/bin/cypress_pilot index 102411ad..650368bf 100755 --- a/spiffworkflow-frontend/bin/cypress_pilot +++ b/spiffworkflow-frontend/bin/cypress_pilot @@ -17,7 +17,12 @@ else shift fi -if [[ -z "${ATTEMPTS:-}" ]]; then +if [[ -n "${ATTEMPTS:-}" ]]; then + if [[ "$command" == "open" ]]; then + echo "ATTEMPTS is ignored when running cypress open" + ATTEMPTS=1 + fi +else ATTEMPTS=1 fi From 2464ad9a26edff4a17d9755aa845e3317ed18245 Mon Sep 17 00:00:00 2001 From: jasquat Date: Thu, 9 Mar 2023 10:59:20 -0500 Subject: [PATCH 02/48] some updates for cypress pp1 test w/ burnettk --- .../keycloak/bin/add_test_users_to_keycloak | 1 + .../cypress/pilot/pp1.cy.js | 20 +++++++++---------- .../cypress/support/commands.js | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/spiffworkflow-backend/keycloak/bin/add_test_users_to_keycloak b/spiffworkflow-backend/keycloak/bin/add_test_users_to_keycloak index 08dd5177..4196add0 100755 --- a/spiffworkflow-backend/keycloak/bin/add_test_users_to_keycloak +++ b/spiffworkflow-backend/keycloak/bin/add_test_users_to_keycloak @@ -86,6 +86,7 @@ while read -r input_line; do custom_attribute_one=$(awk -F ',' '{print $2}' <<<"$input_line") first_line_processed="true" elif [[ -n "$input_line" ]]; then + echo "Importing: $input_line" user_email=$(awk -F ',' '{print $1}' <<<"$input_line") username=$(awk -F '@' '{print $1}' <<<"$user_email") user_attribute_one=$(awk -F ',' '{print $2}' <<<"$input_line") diff --git a/spiffworkflow-frontend/cypress/pilot/pp1.cy.js b/spiffworkflow-frontend/cypress/pilot/pp1.cy.js index 7f38e37d..f0e670de 100644 --- a/spiffworkflow-frontend/cypress/pilot/pp1.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/pp1.cy.js @@ -10,7 +10,7 @@ const approveWithUser = ( .contains(/^Submit$/) .click(); - cy.contains('Tasks I can complete', { timeout: 30000 }); + cy.contains('Tasks I can complete', { timeout: 60000 }); cy.get('.cds--btn').contains(/^Go$/).click(); // approve! @@ -19,12 +19,12 @@ const approveWithUser = ( .contains(/^Submit$/) .click(); if (expectAdditionalApprovalInfoPage) { - cy.contains(expectAdditionalApprovalInfoPage, { timeout: 30000 }); + cy.contains(expectAdditionalApprovalInfoPage, { timeout: 60000 }); cy.get('button') .contains(/^Continue$/) .click(); } - cy.location({ timeout: 30000 }).should((loc) => { + cy.location({ timeout: 60000 }).should((loc) => { expect(loc.pathname).to.eq('/tasks'); }); cy.logout(); @@ -39,15 +39,15 @@ describe('pp1', () => { cy.runPrimaryBpmnFile(true); cy.contains('Please select the type of request to start the process.'); // wait a second to ensure we can click the radio button - cy.wait(1000); + cy.wait(2000); cy.get('input#root-procurement').click(); - cy.wait(1000); + cy.wait(2000); cy.get('button') .contains(/^Submit$/) .click(); cy.contains( 'Submit a new demand request for the procurement of needed items', - { timeout: 30000 } + { timeout: 60000 } ); cy.url().then((currentUrl) => { @@ -68,7 +68,7 @@ describe('pp1', () => { .contains(/^Submit$/) .click(); - cy.contains('Task: Enter NDR Items', { timeout: 30000 }); + cy.contains('Task: Enter NDR Items', { timeout: 60000 }); cy.get('#root_0_sub_category').select('op_src'); cy.get('#root_0_item').clear().type('spiffworkflow'); cy.get('#root_0_qty').clear().type('1'); @@ -81,14 +81,14 @@ describe('pp1', () => { cy.contains( 'Review and provide any supporting information or files for your request.', - { timeout: 30000 } + { timeout: 60000 } ); cy.contains('Submit the Request').click(); cy.get('input[value="Submit the Request"]').click(); cy.get('button') .contains(/^Submit$/) .click(); - cy.contains('Tasks for my open instances', { timeout: 30000 }); + cy.contains('Tasks for my open instances', { timeout: 60000 }); cy.logout(); approveWithUser( @@ -103,7 +103,7 @@ describe('pp1', () => { processInstanceId, 'Task: Update Application Landscape' ); - approveWithUser('legal-a1.sme', processInstanceId); + // approveWithUser('legal-a1.sme', processInstanceId); }); }); }); diff --git a/spiffworkflow-frontend/cypress/support/commands.js b/spiffworkflow-frontend/cypress/support/commands.js index 5fe791d1..404c9af7 100644 --- a/spiffworkflow-frontend/cypress/support/commands.js +++ b/spiffworkflow-frontend/cypress/support/commands.js @@ -109,7 +109,7 @@ Cypress.Commands.add( if (expectAutoRedirectToHumanTask) { // the url changes immediately, so also make sure we get some content from the next page, "Task:", or else when we try to interact with the page, it'll re-render and we'll get an error with cypress. cy.url().should('include', `/tasks/`); - cy.contains('Task: ', { timeout: 10000 }); + cy.contains('Task: ', { timeout: 30000 }); } else { cy.contains(/Process Instance.*[kK]icked [oO]ff/); cy.reload(true); From 4ce715fec8a1d019b7d2b6944da3e0ed59dd3fb2 Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Thu, 9 Mar 2023 11:38:18 -0500 Subject: [PATCH 03/48] Strategy based engine step execution (#168) --- .../services/process_instance_processor.py | 118 +++----- .../services/workflow_execution_service.py | 277 ++++++++++++++++++ 2 files changed, 312 insertions(+), 83 deletions(-) create mode 100644 spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 67c786b1..30f8a481 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -44,8 +44,7 @@ from SpiffWorkflow.bpmn.specs.SubWorkflowTask import SubWorkflowTask # type: ig from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore from SpiffWorkflow.dmn.parser.BpmnDmnParser import BpmnDmnParser # type: ignore from SpiffWorkflow.dmn.serializer.task_spec import BusinessRuleTaskConverter # type: ignore -from SpiffWorkflow.exceptions import SpiffWorkflowException # type: ignore -from SpiffWorkflow.exceptions import WorkflowException +from SpiffWorkflow.exceptions import WorkflowException # type: ignore from SpiffWorkflow.exceptions import WorkflowTaskException from SpiffWorkflow.serializer.exceptions import MissingSpecError # type: ignore from SpiffWorkflow.spiff.serializer.config import SPIFF_SPEC_CONFIG # type: ignore @@ -95,6 +94,15 @@ from spiffworkflow_backend.services.process_model_service import ProcessModelSer from spiffworkflow_backend.services.service_task_service import ServiceTaskDelegate from spiffworkflow_backend.services.spec_file_service import SpecFileService from spiffworkflow_backend.services.user_service import UserService +from spiffworkflow_backend.services.workflow_execution_service import ( + execution_strategy_named, +) +from spiffworkflow_backend.services.workflow_execution_service import ( + StepDetailLoggingDelegate, +) +from spiffworkflow_backend.services.workflow_execution_service import ( + WorkflowExecutionService, +) SPIFF_SPEC_CONFIG["task_specs"].append(BusinessRuleTaskConverter) @@ -1666,90 +1674,34 @@ class ProcessInstanceProcessor: current_app.config["THREAD_LOCAL_DATA"].spiff_step = spiff_step db.session.add(self.process_instance_model) - # TODO remove after done with the performance improvements - # to use delete the _ prefix here and add it to the real def below - def _do_engine_steps(self, exit_at: None = None, save: bool = False) -> None: - """__do_engine_steps.""" - import cProfile - from pstats import SortKey - - with cProfile.Profile() as pr: - self._do_engine_steps(exit_at=exit_at, save=save) - pr.print_stats(sort=SortKey.CUMULATIVE) - - def do_engine_steps(self, exit_at: None = None, save: bool = False) -> None: + def do_engine_steps( + self, + exit_at: None = None, + save: bool = False, + execution_strategy_name: str = "greedy", + ) -> None: """Do_engine_steps.""" - step_details = [] - tasks_to_log = { - "BPMN Task", - "Script Task", - "Service Task", - "Default Start Event", - "Exclusive Gateway", - "Call Activity", - # "End Join", - "End Event", - "Default Throwing Event", - "Subprocess", - "Transactional Subprocess", - } + def spiff_step_details_mapping_builder( + task: SpiffTask, start: float, end: float + ) -> dict: + self._script_engine.environment.revise_state_with_task_data(task) + return self.spiff_step_details_mapping(task, start, end) - # making a dictionary to ensure we are not shadowing variables in the other methods - current_task_start_in_seconds = {} - - def should_log(task: SpiffTask) -> bool: - if ( - task.task_spec.spec_type in tasks_to_log - and not task.task_spec.name.endswith(".EndJoin") - ): - return True - return False - - def will_complete_task(task: SpiffTask) -> None: - if should_log(task): - current_task_start_in_seconds["time"] = time.time() - self.increment_spiff_step() - - def did_complete_task(task: SpiffTask) -> None: - if should_log(task): - self._script_engine.environment.revise_state_with_task_data(task) - step_details.append( - self.spiff_step_details_mapping( - task, current_task_start_in_seconds["time"], time.time() - ) - ) - - try: - self.bpmn_process_instance.refresh_waiting_tasks() - - self.bpmn_process_instance.do_engine_steps( - exit_at=exit_at, - will_complete_task=will_complete_task, - did_complete_task=did_complete_task, - ) - - if self.bpmn_process_instance.is_completed(): - self._script_engine.environment.finalize_result( - self.bpmn_process_instance - ) - - self.process_bpmn_messages() - self.queue_waiting_receive_messages() - except SpiffWorkflowException as swe: - raise ApiError.from_workflow_exception("task_error", str(swe), swe) from swe - - finally: - # self.log_spiff_step_details(step_details) - db.session.bulk_insert_mappings(SpiffStepDetailsModel, step_details) - spiff_logger = logging.getLogger("spiff") - for handler in spiff_logger.handlers: - if hasattr(handler, "bulk_insert_logs"): - handler.bulk_insert_logs() # type: ignore - db.session.commit() - - if save: - self.save() + step_delegate = StepDetailLoggingDelegate( + self.increment_spiff_step, spiff_step_details_mapping_builder + ) + execution_strategy = execution_strategy_named( + execution_strategy_name, step_delegate + ) + execution_service = WorkflowExecutionService( + self.bpmn_process_instance, + self.process_instance_model, + execution_strategy, + self._script_engine.environment.finalize_result, + self.save, + ) + execution_service.do_engine_steps(exit_at, save) # log the spiff step details so we know what is processing the process # instance when a human task has a timer event. diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py new file mode 100644 index 00000000..576dee1b --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -0,0 +1,277 @@ +import logging +import time +from typing import Callable +from typing import List + +from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore +from SpiffWorkflow.exceptions import SpiffWorkflowException # type: ignore +from SpiffWorkflow.task import Task as SpiffTask # type: ignore +from SpiffWorkflow.task import TaskState + +from spiffworkflow_backend.exceptions.api_error import ApiError +from spiffworkflow_backend.models.db import db +from spiffworkflow_backend.models.message_instance import MessageInstanceModel +from spiffworkflow_backend.models.message_instance_correlation import ( + MessageInstanceCorrelationRuleModel, +) +from spiffworkflow_backend.models.process_instance import ProcessInstanceModel +from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel + + +class EngineStepDelegate: + """Interface of sorts for a concrete engine step delegate.""" + + def will_complete_task(self, task: SpiffTask) -> None: + pass + + def did_complete_task(self, task: SpiffTask) -> None: + pass + + def save(self) -> None: + pass + + +SpiffStepIncrementer = Callable[[], None] +SpiffStepDetailsMappingBuilder = Callable[[SpiffTask, float, float], dict] + + +class StepDetailLoggingDelegate(EngineStepDelegate): + """Engine step delegate that takes care of logging spiff step details. + + This separates the concerns of step execution and step logging. + """ + + def __init__( + self, + increment_spiff_step: SpiffStepIncrementer, + spiff_step_details_mapping: SpiffStepDetailsMappingBuilder, + ): + """__init__.""" + self.increment_spiff_step = increment_spiff_step + self.spiff_step_details_mapping = spiff_step_details_mapping + self.step_details: List[dict] = [] + self.current_task_start_in_seconds = 0.0 + self.tasks_to_log = { + "BPMN Task", + "Script Task", + "Service Task", + "Default Start Event", + "Exclusive Gateway", + "Call Activity", + # "End Join", + "End Event", + "Default Throwing Event", + "Subprocess", + "Transactional Subprocess", + } + + def should_log(self, task: SpiffTask) -> bool: + return ( + task.task_spec.spec_type in self.tasks_to_log + and not task.task_spec.name.endswith(".EndJoin") + ) + + def will_complete_task(self, task: SpiffTask) -> None: + if self.should_log(task): + self.current_task_start_in_seconds = time.time() + self.increment_spiff_step() + + def did_complete_task(self, task: SpiffTask) -> None: + if self.should_log(task): + self.step_details.append( + self.spiff_step_details_mapping( + task, self.current_task_start_in_seconds, time.time() + ) + ) + + def save(self) -> None: + db.session.bulk_insert_mappings(SpiffStepDetailsModel, self.step_details) + db.session.commit() + + +class ExecutionStrategy: + """Interface of sorts for a concrete execution strategy.""" + + def __init__(self, delegate: EngineStepDelegate): + """__init__.""" + self.delegate = delegate + + def do_engine_steps( + self, bpmn_process_instance: BpmnWorkflow, exit_at: None = None + ) -> None: + pass + + def save(self) -> None: + self.delegate.save() + + +class GreedyExecutionStrategy(ExecutionStrategy): + """The common execution strategy. This will greedily run all engine step without stopping.""" + + def do_engine_steps( + self, bpmn_process_instance: BpmnWorkflow, exit_at: None = None + ) -> None: + bpmn_process_instance.do_engine_steps( + exit_at=exit_at, + will_complete_task=self.delegate.will_complete_task, + did_complete_task=self.delegate.did_complete_task, + ) + + +class RunUntilServiceTaskExecutionStrategy(ExecutionStrategy): + """For illustration purposes, not currently integrated. + + Would allow the `run` from the UI to execute until a service task then + return (to an interstitial page). The background processor would then take over. + """ + + def do_engine_steps( + self, bpmn_process_instance: BpmnWorkflow, exit_at: None = None + ) -> None: + engine_steps = list( + [ + t + for t in bpmn_process_instance.get_tasks(TaskState.READY) + if bpmn_process_instance._is_engine_task(t.task_spec) + ] + ) + while engine_steps: + for task in engine_steps: + if task.task_spec.spec_type == "Service Task": + return + self.delegate.will_complete_task(task) + task.complete() + self.delegate.did_complete_task(task) + + engine_steps = list( + [ + t + for t in bpmn_process_instance.get_tasks(TaskState.READY) + if bpmn_process_instance._is_engine_task(t.task_spec) + ] + ) + + +def execution_strategy_named( + name: str, delegate: EngineStepDelegate +) -> ExecutionStrategy: + cls = { + "greedy": GreedyExecutionStrategy, + "run_until_service_task": RunUntilServiceTaskExecutionStrategy, + }[name] + + return cls(delegate) + + +ProcessInstanceCompleter = Callable[[BpmnWorkflow], None] +ProcessInstanceSaver = Callable[[], None] + + +class WorkflowExecutionService: + """Provides the driver code for workflow execution.""" + + def __init__( + self, + bpmn_process_instance: BpmnWorkflow, + process_instance_model: ProcessInstanceModel, + execution_strategy: ExecutionStrategy, + process_instance_completer: ProcessInstanceCompleter, + process_instance_saver: ProcessInstanceSaver, + ): + """__init__.""" + self.bpmn_process_instance = bpmn_process_instance + self.process_instance_model = process_instance_model + self.execution_strategy = execution_strategy + self.process_instance_completer = process_instance_completer + self.process_instance_saver = process_instance_saver + + def do_engine_steps(self, exit_at: None = None, save: bool = False) -> None: + """Do_engine_steps.""" + try: + self.bpmn_process_instance.refresh_waiting_tasks() + + self.execution_strategy.do_engine_steps(self.bpmn_process_instance, exit_at) + + if self.bpmn_process_instance.is_completed(): + self.process_instance_completer(self.bpmn_process_instance) + + self.process_bpmn_messages() + self.queue_waiting_receive_messages() + except SpiffWorkflowException as swe: + raise ApiError.from_workflow_exception("task_error", str(swe), swe) from swe + + finally: + self.execution_strategy.save() + spiff_logger = logging.getLogger("spiff") + for handler in spiff_logger.handlers: + if hasattr(handler, "bulk_insert_logs"): + handler.bulk_insert_logs() # type: ignore + db.session.commit() + + if save: + self.process_instance_saver() + + def process_bpmn_messages(self) -> None: + """Process_bpmn_messages.""" + bpmn_messages = self.bpmn_process_instance.get_bpmn_messages() + for bpmn_message in bpmn_messages: + message_instance = MessageInstanceModel( + process_instance_id=self.process_instance_model.id, + user_id=self.process_instance_model.process_initiator_id, # TODO: use the correct swimlane user when that is set up + message_type="send", + name=bpmn_message.name, + payload=bpmn_message.payload, + correlation_keys=self.bpmn_process_instance.correlations, + ) + db.session.add(message_instance) + db.session.commit() + + def queue_waiting_receive_messages(self) -> None: + """Queue_waiting_receive_messages.""" + waiting_events = self.bpmn_process_instance.waiting_events() + waiting_message_events = filter( + lambda e: e["event_type"] == "Message", waiting_events + ) + + for event in waiting_message_events: + # Ensure we are only creating one message instance for each waiting message + if ( + MessageInstanceModel.query.filter_by( + process_instance_id=self.process_instance_model.id, + message_type="receive", + name=event["name"], + ).count() + > 0 + ): + continue + + # Create a new Message Instance + message_instance = MessageInstanceModel( + process_instance_id=self.process_instance_model.id, + user_id=self.process_instance_model.process_initiator_id, + message_type="receive", + name=event["name"], + correlation_keys=self.bpmn_process_instance.correlations, + ) + for correlation_property in event["value"]: + message_correlation = MessageInstanceCorrelationRuleModel( + message_instance_id=message_instance.id, + name=correlation_property.name, + retrieval_expression=correlation_property.retrieval_expression, + ) + message_instance.correlation_rules.append(message_correlation) + db.session.add(message_instance) + db.session.commit() + + +class ProfiledWorkflowExecutionService(WorkflowExecutionService): + """A profiled version of the workflow execution service.""" + + def do_engine_steps(self, exit_at: None = None, save: bool = False) -> None: + """__do_engine_steps.""" + import cProfile + from pstats import SortKey + + with cProfile.Profile() as pr: + super().do_engine_steps(exit_at=exit_at, save=save) + pr.print_stats(sort=SortKey.CUMULATIVE) From 79a17ec8293bf1f711c22f8ef91cee19c113a37f Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 9 Mar 2023 15:27:35 -0500 Subject: [PATCH 04/48] Adding a new test for error handing to assure this doesn't break in the future, and cleaning up the message call event. Will also need to update the error handling BPMN process so it provides correlation keys. We should add a task that will alert you when you create a message object without setting correlation keys - as they are required per the specification. --- .../models/message_instance.py | 2 +- .../services/error_handling_service.py | 104 ++++-------------- .../tests/data/error/error_handler.bpmn | 77 +++++++++++++ .../unit/test_error_handling.py | 87 +++++++++++++++ 4 files changed, 186 insertions(+), 84 deletions(-) create mode 100644 spiffworkflow-backend/tests/data/error/error_handler.bpmn create mode 100644 spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_error_handling.py diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/message_instance.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/message_instance.py index 5985e3ae..9cf4ad98 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/message_instance.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/message_instance.py @@ -55,7 +55,7 @@ class MessageInstanceModel(SpiffworkflowBaseDBModel): # The correlation keys of the process at the time the message was created. correlation_keys: dict = db.Column(db.JSON) status: str = db.Column(db.String(20), nullable=False, default="ready") - user_id: int = db.Column(ForeignKey(UserModel.id), nullable=False) # type: ignore + user_id: int = db.Column(ForeignKey(UserModel.id), nullable=True) # type: ignore user = relationship("UserModel") counterpart_id: int = db.Column( db.Integer diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/error_handling_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/error_handling_service.py index f1d7f1bc..c59eba4b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/error_handling_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/error_handling_service.py @@ -26,6 +26,8 @@ from spiffworkflow_backend.services.process_model_service import ProcessModelSer class ErrorHandlingService: """ErrorHandlingService.""" + MESSAGE_NAME = "SystemErrorMessage" + @staticmethod def set_instance_status(instance_id: int, status: str) -> None: """Set_instance_status.""" @@ -58,106 +60,42 @@ class ErrorHandlingService: ProcessInstanceStatus.error.value, ) - # Second, call the System Notification Process - # Note that this isn't the best way to do this. - # The configs are all in the model. - # Maybe we can move some of this to the notification process, or dmn tables. + # Second, send a bpmn message out, but only if an exception notification address is provided + # This will create a new Send Message with correlation keys on the recipients and the message + # body. if len(process_model.exception_notification_addresses) > 0: try: - self.handle_system_notification(_error, process_model) + self.handle_system_notification(_error, process_model, _processor) except Exception as e: # hmm... what to do if a notification method fails. Probably log, at least current_app.logger.error(e) @staticmethod def handle_system_notification( - error: Union[ApiError, Exception], process_model: ProcessModelInfo + error: Union[ApiError, Exception], + process_model: ProcessModelInfo, + _processor: ProcessInstanceProcessor ) -> Response: - """Handle_system_notification.""" - recipients = process_model.exception_notification_addresses + """Send a BPMN Message - which may kick off a waiting process. """ message_text = ( f"There was an exception running process {process_model.id}.\nOriginal" f" Error:\n{error.__repr__()}" ) - message_payload = {"message_text": message_text, "recipients": recipients} - message_name = current_app.config[ - "SPIFFWORKFLOW_BACKEND_SYSTEM_NOTIFICATION_PROCESS_MODEL_MESSAGE_ID" - ] - message_triggerable_process_model = ( - MessageTriggerableProcessModel.query.filter_by( - message_name=message_name - ).first() - ) + message_payload = {"message_text": message_text, + "recipients": process_model.exception_notification_addresses + } + user_id = None + if "user" in g: + user_id = g.user.id + else: + user_id = _processor.process_instance_model.process_initiator_id - # Create the send message message_instance = MessageInstanceModel( message_type="send", - name=message_name, + name=ErrorHandlingService.MESSAGE_NAME, payload=message_payload, - user_id=g.user.id, + user_id=user_id, ) db.session.add(message_instance) db.session.commit() - - process_instance = MessageService.start_process_with_message( - message_triggerable_process_model, message_instance - ) - - return Response( - json.dumps(ProcessInstanceModelSchema().dump(process_instance)), - status=200, - mimetype="application/json", - ) - - # @staticmethod - # def handle_sentry_notification(_error: ApiError, _recipients: List) -> None: - # """SentryHandler.""" - # ... - # - # @staticmethod - # def handle_email_notification( - # processor: ProcessInstanceProcessor, - # error: Union[ApiError, Exception], - # recipients: List, - # ) -> None: - # """EmailHandler.""" - # subject = "Unexpected error in app" - # if isinstance(error, ApiError): - # content = f"{error.message}" - # else: - # content = str(error) - # content_html = content - # - # EmailService.add_email( - # subject, - # "sender@company.com", - # recipients, - # content, - # content_html, - # cc=None, - # bcc=None, - # reply_to=None, - # attachment_files=None, - # ) - # - # @staticmethod - # def handle_waku_notification(_error: ApiError, _recipients: List) -> Any: - # """WakuHandler.""" - # # class WakuMessage: - # # """WakuMessage.""" - # # - # # payload: str - # # contentTopic: str # Optional - # # version: int # Optional - # # timestamp: int # Optional - - -class FailingService: - """FailingService.""" - - @staticmethod - def fail_as_service() -> None: - """It fails.""" - raise ApiError( - error_code="failing_service", message="This is my failing service" - ) + MessageService.correlate_send_message(message_instance) diff --git a/spiffworkflow-backend/tests/data/error/error_handler.bpmn b/spiffworkflow-backend/tests/data/error/error_handler.bpmn new file mode 100644 index 00000000..ccb9ecf7 --- /dev/null +++ b/spiffworkflow-backend/tests/data/error/error_handler.bpmn @@ -0,0 +1,77 @@ + + + + + + + + message_text + recipients + + + + + + Flow_1wwg6l1 + + + + Flow_1hpekd5 + + + + Flow_1wwg6l1 + Flow_1hpekd5 + x = 1 + + + + + system_message + + + + + message_text + + + + + recipients + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_error_handling.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_error_handling.py new file mode 100644 index 00000000..424aa3a1 --- /dev/null +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_error_handling.py @@ -0,0 +1,87 @@ +import pytest +from flask import Flask +from flask.testing import FlaskClient + +from conftest import with_super_admin_user +from spiffworkflow_backend import db +from spiffworkflow_backend.exceptions.api_error import ApiError +from spiffworkflow_backend.models.message_instance import MessageInstanceModel +from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus +from spiffworkflow_backend.models.process_model import ProcessModelInfo +from spiffworkflow_backend.models.user import UserModel +from spiffworkflow_backend.services.error_handling_service import ErrorHandlingService +from spiffworkflow_backend.services.process_instance_processor import ProcessInstanceProcessor +from spiffworkflow_backend.services.process_instance_service import ProcessInstanceService +from spiffworkflow_backend.services.process_model_service import ProcessModelService +from tests.spiffworkflow_backend.helpers.base_test import BaseTest +from tests.spiffworkflow_backend.helpers.test_data import load_test_spec + + +class TestErrorHandlingService(BaseTest): + """Error Handling does some crazy stuff man, like even firing off + messages in case a BPMN Task is waiting for that message. """ + + def run_process_model_and_handle_error(self, process_model: ProcessModelInfo, user: UserModel) -> Exception: + process_instance = ProcessInstanceService.create_process_instance_from_process_model_identifier( + process_model.id, user) + pip = ProcessInstanceProcessor(process_instance) + with pytest.raises(ApiError) as e: + pip.do_engine_steps(save=True) + ErrorHandlingService().handle_error(pip, e) + return process_instance + + + def test_handle_error_suspends_or_faults_process( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + """ Process Model in DB marked as suspended when error occurs""" + process_model = load_test_spec( + "test_group/error_suspend", + process_model_source_directory="error", + bpmn_file_name="error.bpmn", # Slightly misnamed, it sends and receives + ) + + # Process instance should be marked as errored by default. + process_instance = self.run_process_model_and_handle_error(process_model, with_super_admin_user) + assert(ProcessInstanceStatus.error.value == process_instance.status) + + # If process model should be suspended on error, then that is what should happen. + process_model.fault_or_suspend_on_exception = "suspend" + ProcessModelService.save_process_model(process_model) + process_instance = self.run_process_model_and_handle_error(process_model, with_super_admin_user) + assert(ProcessInstanceStatus.suspended.value == process_instance.status) + + def test_error_sends_bpmn_message( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel): + """ Process Model in DB marked as suspended when error occurs""" + process_model = load_test_spec( + "test_group/error_send_message_bpmn", + process_model_source_directory="error", + bpmn_file_name="error.bpmn", # Slightly misnamed, it sends and receives + ) + """ Process Model that will listen for errors sent.""" + process_model_error_handler = load_test_spec( + "test_group/admin_tools/error_handler", + process_model_source_directory="error", + bpmn_file_name="error_handler.bpmn", # Slightly misnamed, it sends and receives + ) + process_model.exception_notification_addresses=["dan@ILoveToReadErrorsInMyEmails.com"] + ProcessModelService.save_process_model(process_model) + # kick off the process and assure it got marked as an error. + process_instance = self.run_process_model_and_handle_error(process_model, with_super_admin_user) + assert(ProcessInstanceStatus.error.value == process_instance.status) + + # Both send and receive messages should be generated, matched + # and considered complete. + messages = db.session.query(MessageInstanceModel).all() + assert(2 == len(messages)) + assert("completed", messages[0].status) + assert("completed", messages[1].status) \ No newline at end of file From 3879ea4f3a4a6d11472956e54a78a816818570e6 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 9 Mar 2023 16:10:31 -0500 Subject: [PATCH 05/48] run_pyl --- .../services/error_handling_service.py | 23 ++--- .../unit/test_error_handling.py | 87 +++++++++++-------- 2 files changed, 61 insertions(+), 49 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/error_handling_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/error_handling_service.py index c59eba4b..53ceef2c 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/error_handling_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/error_handling_service.py @@ -1,19 +1,13 @@ """Error_handling_service.""" -import json from typing import Union from flask import current_app from flask import g -from flask.wrappers import Response from spiffworkflow_backend.exceptions.api_error import ApiError from spiffworkflow_backend.models.db import db from spiffworkflow_backend.models.message_instance import MessageInstanceModel -from spiffworkflow_backend.models.message_triggerable_process_model import ( - MessageTriggerableProcessModel, -) from spiffworkflow_backend.models.process_instance import ProcessInstanceModel -from spiffworkflow_backend.models.process_instance import ProcessInstanceModelSchema from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus from spiffworkflow_backend.models.process_model import ProcessModelInfo from spiffworkflow_backend.services.message_service import MessageService @@ -72,18 +66,19 @@ class ErrorHandlingService: @staticmethod def handle_system_notification( - error: Union[ApiError, Exception], - process_model: ProcessModelInfo, - _processor: ProcessInstanceProcessor - ) -> Response: - """Send a BPMN Message - which may kick off a waiting process. """ + error: Union[ApiError, Exception], + process_model: ProcessModelInfo, + _processor: ProcessInstanceProcessor, + ) -> None: + """Send a BPMN Message - which may kick off a waiting process.""" message_text = ( f"There was an exception running process {process_model.id}.\nOriginal" f" Error:\n{error.__repr__()}" ) - message_payload = {"message_text": message_text, - "recipients": process_model.exception_notification_addresses - } + message_payload = { + "message_text": message_text, + "recipients": process_model.exception_notification_addresses, + } user_id = None if "user" in g: user_id = g.user.id diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_error_handling.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_error_handling.py index 424aa3a1..9d481788 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_error_handling.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_error_handling.py @@ -1,44 +1,52 @@ import pytest from flask import Flask from flask.testing import FlaskClient +from tests.spiffworkflow_backend.helpers.base_test import BaseTest +from tests.spiffworkflow_backend.helpers.test_data import load_test_spec -from conftest import with_super_admin_user from spiffworkflow_backend import db from spiffworkflow_backend.exceptions.api_error import ApiError from spiffworkflow_backend.models.message_instance import MessageInstanceModel +from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus from spiffworkflow_backend.models.process_model import ProcessModelInfo from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.error_handling_service import ErrorHandlingService -from spiffworkflow_backend.services.process_instance_processor import ProcessInstanceProcessor -from spiffworkflow_backend.services.process_instance_service import ProcessInstanceService +from spiffworkflow_backend.services.process_instance_processor import ( + ProcessInstanceProcessor, +) +from spiffworkflow_backend.services.process_instance_service import ( + ProcessInstanceService, +) from spiffworkflow_backend.services.process_model_service import ProcessModelService -from tests.spiffworkflow_backend.helpers.base_test import BaseTest -from tests.spiffworkflow_backend.helpers.test_data import load_test_spec class TestErrorHandlingService(BaseTest): - """Error Handling does some crazy stuff man, like even firing off - messages in case a BPMN Task is waiting for that message. """ + """Error Handling does some crazy stuff man. - def run_process_model_and_handle_error(self, process_model: ProcessModelInfo, user: UserModel) -> Exception: + Like it can fire off BPMN messages in case a BPMN Task is waiting for that message. + """ + + def run_process_model_and_handle_error( + self, process_model: ProcessModelInfo, user: UserModel + ) -> ProcessInstanceModel: process_instance = ProcessInstanceService.create_process_instance_from_process_model_identifier( - process_model.id, user) - pip = ProcessInstanceProcessor(process_instance) + process_model.id, user + ) + pip = ProcessInstanceProcessor(process_instance) with pytest.raises(ApiError) as e: pip.do_engine_steps(save=True) - ErrorHandlingService().handle_error(pip, e) + ErrorHandlingService().handle_error(pip, e.value) return process_instance - def test_handle_error_suspends_or_faults_process( - self, - app: Flask, - client: FlaskClient, - with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel, + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, ) -> None: - """ Process Model in DB marked as suspended when error occurs""" + """Process Model in DB marked as suspended when error occurs.""" process_model = load_test_spec( "test_group/error_suspend", process_model_source_directory="error", @@ -46,42 +54,51 @@ class TestErrorHandlingService(BaseTest): ) # Process instance should be marked as errored by default. - process_instance = self.run_process_model_and_handle_error(process_model, with_super_admin_user) - assert(ProcessInstanceStatus.error.value == process_instance.status) + process_instance = self.run_process_model_and_handle_error( + process_model, with_super_admin_user + ) + assert ProcessInstanceStatus.error.value == process_instance.status # If process model should be suspended on error, then that is what should happen. process_model.fault_or_suspend_on_exception = "suspend" ProcessModelService.save_process_model(process_model) - process_instance = self.run_process_model_and_handle_error(process_model, with_super_admin_user) - assert(ProcessInstanceStatus.suspended.value == process_instance.status) + process_instance = self.run_process_model_and_handle_error( + process_model, with_super_admin_user + ) + assert ProcessInstanceStatus.suspended.value == process_instance.status def test_error_sends_bpmn_message( - self, - app: Flask, - client: FlaskClient, - with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel): - """ Process Model in DB marked as suspended when error occurs""" + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + """Real BPMN Messages should get generated and processes should fire off and complete.""" process_model = load_test_spec( "test_group/error_send_message_bpmn", process_model_source_directory="error", bpmn_file_name="error.bpmn", # Slightly misnamed, it sends and receives ) """ Process Model that will listen for errors sent.""" - process_model_error_handler = load_test_spec( + load_test_spec( "test_group/admin_tools/error_handler", process_model_source_directory="error", bpmn_file_name="error_handler.bpmn", # Slightly misnamed, it sends and receives ) - process_model.exception_notification_addresses=["dan@ILoveToReadErrorsInMyEmails.com"] + process_model.exception_notification_addresses = [ + "dan@ILoveToReadErrorsInMyEmails.com" + ] ProcessModelService.save_process_model(process_model) # kick off the process and assure it got marked as an error. - process_instance = self.run_process_model_and_handle_error(process_model, with_super_admin_user) - assert(ProcessInstanceStatus.error.value == process_instance.status) + process_instance = self.run_process_model_and_handle_error( + process_model, with_super_admin_user + ) + assert ProcessInstanceStatus.error.value == process_instance.status # Both send and receive messages should be generated, matched # and considered complete. messages = db.session.query(MessageInstanceModel).all() - assert(2 == len(messages)) - assert("completed", messages[0].status) - assert("completed", messages[1].status) \ No newline at end of file + assert 2 == len(messages) + assert "completed" == messages[0].status + assert "completed" == messages[1].status From 7e44c90fbb525652f211ff5fd2e5c07207b91f91 Mon Sep 17 00:00:00 2001 From: jasquat Date: Thu, 9 Mar 2023 17:16:44 -0500 Subject: [PATCH 06/48] the primary test is passing now but subprocesses and call activities are probably broken w/ burnettk --- .flake8 | 4 +- spiffworkflow-backend/.flake8 | 4 +- .../services/process_instance_processor.py | 91 ++++++++++++------- .../services/task_service.py | 49 ++++++++++ .../services/workflow_execution_service.py | 89 ++++++++++++++---- .../tests/data/manual_task/manual_task.bpmn | 33 +++++-- .../unit/test_process_instance_processor.py | 63 +++++++++++++ 7 files changed, 266 insertions(+), 67 deletions(-) create mode 100644 spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py diff --git a/.flake8 b/.flake8 index fe5c9a94..9c54dc0e 100644 --- a/.flake8 +++ b/.flake8 @@ -17,10 +17,10 @@ per-file-ignores = # THEN, test_hey.py will NOT be excluding D103 # asserts are ok in tests - spiffworkflow-backend/tests/*:S101,D100,D101,D102,D103 + spiffworkflow-backend/tests/*:S101,D100,D101,D102,D103,D107 # prefer naming functions descriptively rather than forcing comments - spiffworkflow-backend/src/*:D100,D101,D102,D103 + spiffworkflow-backend/src/*:D100,D101,D102,D103,D107 spiffworkflow-backend/bin/keycloak_test_server.py:B950,D spiffworkflow-backend/conftest.py:S105 diff --git a/spiffworkflow-backend/.flake8 b/spiffworkflow-backend/.flake8 index 0fceed15..2e6554e5 100644 --- a/spiffworkflow-backend/.flake8 +++ b/spiffworkflow-backend/.flake8 @@ -17,10 +17,10 @@ per-file-ignores = # THEN, test_hey.py will NOT be excluding D103 # asserts are ok in tests - tests/*:S101,D100,D101,D102,D103 + tests/*:S101,D100,D101,D102,D103,D107 # prefer naming functions descriptively rather than forcing comments - src/*:D100,D101,D102,D103 + src/*:D100,D101,D102,D103,D107 bin/keycloak_test_server.py:B950,D conftest.py:S105 diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 30f8a481..eeee7dc6 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -93,6 +93,7 @@ from spiffworkflow_backend.services.file_system_service import FileSystemService from spiffworkflow_backend.services.process_model_service import ProcessModelService from spiffworkflow_backend.services.service_task_service import ServiceTaskDelegate from spiffworkflow_backend.services.spec_file_service import SpecFileService +from spiffworkflow_backend.services.task_service import TaskService from spiffworkflow_backend.services.user_service import UserService from spiffworkflow_backend.services.workflow_execution_service import ( execution_strategy_named, @@ -100,6 +101,9 @@ from spiffworkflow_backend.services.workflow_execution_service import ( from spiffworkflow_backend.services.workflow_execution_service import ( StepDetailLoggingDelegate, ) +from spiffworkflow_backend.services.workflow_execution_service import ( + TaskModelSavingDelegate, +) from spiffworkflow_backend.services.workflow_execution_service import ( WorkflowExecutionService, ) @@ -152,6 +156,10 @@ class SpiffStepDetailIsMissingError(Exception): pass +class TaskNotFoundError(Exception): + pass + + class BoxedTaskDataBasedScriptEngineEnvironment(BoxedTaskDataEnvironment): # type: ignore def __init__(self, environment_globals: Dict[str, Any]): """BoxedTaskDataBasedScriptEngineEnvironment.""" @@ -802,7 +810,7 @@ class ProcessInstanceProcessor: if start_in_seconds is None: start_in_seconds = time.time() - task_json = self.get_task_json_from_spiff_task(spiff_task) + task_json = self.get_task_dict_from_spiff_task(spiff_task) return { "process_instance_id": self.process_instance_model.id, @@ -1083,8 +1091,8 @@ class ProcessInstanceProcessor: task_data_dict = task_properties.pop("data") state_int = task_properties["state"] - task = TaskModel.query.filter_by(guid=task_id).first() - if task is None: + task_model = TaskModel.query.filter_by(guid=task_id).first() + if task_model is None: # bpmn_process_identifier = task_properties['workflow_name'] # bpmn_identifier = task_properties['task_spec'] # @@ -1092,23 +1100,12 @@ class ProcessInstanceProcessor: # .join(BpmnProcessDefinitionModel).filter(BpmnProcessDefinitionModel.bpmn_identifier==bpmn_process_identifier).first() # if task_definition is None: # subprocess_task = TaskModel.query.filter_by(guid=bpmn_process.guid) - task = TaskModel(guid=task_id, bpmn_process_id=bpmn_process.id) - task.state = TaskStateNames[state_int] - task.properties_json = task_properties + task_model = TaskModel(guid=task_id, bpmn_process_id=bpmn_process.id) + task_model.state = TaskStateNames[state_int] + task_model.properties_json = task_properties - task_data_json = json.dumps(task_data_dict, sort_keys=True).encode("utf8") - task_data_hash = sha256(task_data_json).hexdigest() - if task.json_data_hash != task_data_hash: - json_data = ( - db.session.query(JsonDataModel.id) - .filter_by(hash=task_data_hash) - .first() - ) - if json_data is None: - json_data = JsonDataModel(hash=task_data_hash, data=task_data_dict) - db.session.add(json_data) - task.json_data_hash = task_data_hash - db.session.add(task) + TaskService.update_task_data_on_task_model(task_model, task_data_dict) + db.session.add(task_model) return bpmn_process @@ -1135,14 +1132,15 @@ class ProcessInstanceProcessor: # FIXME: Update tasks in the did_complete_task instead to set the final info. # We will need to somehow cache all tasks initially though before each task is run. # Maybe always do this for first run - just need to know it's the first run. - subprocesses = process_instance_data_dict.pop("subprocesses") - bpmn_process_parent = self._add_bpmn_process(process_instance_data_dict) - for subprocess_task_id, subprocess_properties in subprocesses.items(): - self._add_bpmn_process( - subprocess_properties, - bpmn_process_parent, - bpmn_process_guid=subprocess_task_id, - ) + if self.process_instance_model.bpmn_process_id is None: + subprocesses = process_instance_data_dict.pop("subprocesses") + bpmn_process_parent = self._add_bpmn_process(process_instance_data_dict) + for subprocess_task_id, subprocess_properties in subprocesses.items(): + self._add_bpmn_process( + subprocess_properties, + bpmn_process_parent, + bpmn_process_guid=subprocess_task_id, + ) def save(self) -> None: """Saves the current state of this processor to the database.""" @@ -1691,8 +1689,13 @@ class ProcessInstanceProcessor: step_delegate = StepDetailLoggingDelegate( self.increment_spiff_step, spiff_step_details_mapping_builder ) + task_model_delegate = TaskModelSavingDelegate( + secondary_engine_step_delegate=step_delegate, + serializer=self._serializer, + process_instance=self.process_instance_model, + ) execution_strategy = execution_strategy_named( - execution_strategy_name, step_delegate + execution_strategy_name, task_model_delegate ) execution_service = WorkflowExecutionService( self.bpmn_process_instance, @@ -1871,7 +1874,7 @@ class ProcessInstanceProcessor: ) return user_tasks # type: ignore - def get_task_json_from_spiff_task(self, spiff_task: SpiffTask) -> dict[str, Any]: + def get_task_dict_from_spiff_task(self, spiff_task: SpiffTask) -> dict[str, Any]: default_registry = DefaultRegistry() task_data = default_registry.convert(spiff_task.data) python_env = default_registry.convert( @@ -1884,17 +1887,29 @@ class ProcessInstanceProcessor: return task_json def complete_task( - self, task: SpiffTask, human_task: HumanTaskModel, user: UserModel + self, spiff_task: SpiffTask, human_task: HumanTaskModel, user: UserModel ) -> None: """Complete_task.""" - self.bpmn_process_instance.complete_task_from_id(task.id) + task_model = TaskModel.query.filter_by(guid=human_task.task_id).first() + if task_model is None: + raise TaskNotFoundError( + "Cannot find a task with guid" + f" {self.process_instance_model.id} and task_id is {human_task.task_id}" + ) + + task_model.start_in_seconds = time.time() + self.bpmn_process_instance.complete_task_from_id(spiff_task.id) + task_model.end_in_seconds = time.time() + human_task.completed_by_user_id = user.id human_task.completed = True db.session.add(human_task) + + # FIXME: remove when we switch over to using tasks only details_model = ( SpiffStepDetailsModel.query.filter_by( process_instance_id=self.process_instance_model.id, - task_id=str(task.id), + task_id=str(spiff_task.id), task_state="READY", ) .order_by(SpiffStepDetailsModel.id.desc()) # type: ignore @@ -1903,13 +1918,19 @@ class ProcessInstanceProcessor: if details_model is None: raise SpiffStepDetailIsMissingError( "Cannot find a ready spiff_step_detail entry for process instance" - f" {self.process_instance_model.id} and task_id is {task.id}" + f" {self.process_instance_model.id} and task_id is {spiff_task.id}" ) - details_model.task_state = task.get_state_name() + details_model.task_state = spiff_task.get_state_name() details_model.end_in_seconds = time.time() - details_model.task_json = self.get_task_json_from_spiff_task(task) + details_model.task_json = self.get_task_dict_from_spiff_task(spiff_task) db.session.add(details_model) + # ####### + + TaskService.update_task_model_and_add_to_db_session( + task_model, spiff_task, self._serializer + ) + # this is the thing that actually commits the db transaction (on behalf of the other updates above as well) self.save() diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py new file mode 100644 index 00000000..9c6db0fd --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py @@ -0,0 +1,49 @@ +from spiffworkflow_backend.models.process_instance import ProcessInstanceModel +from spiffworkflow_backend.models.task import TaskModel # noqa: F401 +from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer # type: ignore +from SpiffWorkflow.task import TaskStateNames # type: ignore +from SpiffWorkflow.task import Task as SpiffTask +from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401 +from spiffworkflow_backend.models.db import db +from hashlib import sha256 +import json + + +class TaskService(): + + @classmethod + def update_task_data_on_task_model(cls, task_model: TaskModel, task_data_dict: dict) -> None: + task_data_json = json.dumps(task_data_dict, sort_keys=True).encode("utf8") + task_data_hash = sha256(task_data_json).hexdigest() + if task_model.json_data_hash != task_data_hash: + json_data = ( + db.session.query(JsonDataModel.id) + .filter_by(hash=task_data_hash) + .first() + ) + if json_data is None: + json_data = JsonDataModel(hash=task_data_hash, data=task_data_dict) + db.session.add(json_data) + task_model.json_data_hash = task_data_hash + + @classmethod + def update_task_model_and_add_to_db_session(cls, task_model: TaskModel, spiff_task: SpiffTask, serializer: BpmnWorkflowSerializer) -> None: + """Updates properties_json and data on given task_model. + + This will NOT update start_in_seconds or end_in_seconds. + """ + new_properties_json = serializer.task_to_dict(spiff_task) + task_model.properties_json = new_properties_json + task_model.state = TaskStateNames[new_properties_json['state']] + cls.update_task_data_on_task_model(task_model, spiff_task.data) + db.session.add(task_model) + + @classmethod + def find_or_create_task_model_from_spiff_task(cls, spiff_task: SpiffTask, process_instance: ProcessInstanceModel) -> TaskModel: + spiff_task_guid = str(spiff_task.id) + task_model: TaskModel = TaskModel.query.filter_by(guid=spiff_task_guid).first() + # if task_model is None: + # task_model = TaskModel(guid=spiff_task_guid, bpmn_process_id=process_instance.bpmn_process_id) + # db.session.add(task_model) + # db.session.commit() + return task_model diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index 576dee1b..89ca9caa 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -2,7 +2,9 @@ import logging import time from typing import Callable from typing import List +from typing import Optional +from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer # type: ignore from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore from SpiffWorkflow.exceptions import SpiffWorkflowException # type: ignore from SpiffWorkflow.task import Task as SpiffTask # type: ignore @@ -16,18 +18,20 @@ from spiffworkflow_backend.models.message_instance_correlation import ( ) from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel +from spiffworkflow_backend.models.task import TaskModel # noqa: F401 +from spiffworkflow_backend.services.task_service import TaskService class EngineStepDelegate: """Interface of sorts for a concrete engine step delegate.""" - def will_complete_task(self, task: SpiffTask) -> None: + def will_complete_task(self, spiff_task: SpiffTask) -> None: pass - def did_complete_task(self, task: SpiffTask) -> None: + def did_complete_task(self, spiff_task: SpiffTask) -> None: pass - def save(self) -> None: + def save(self, commit: bool = False) -> None: pass @@ -35,6 +39,54 @@ SpiffStepIncrementer = Callable[[], None] SpiffStepDetailsMappingBuilder = Callable[[SpiffTask, float, float], dict] +class TaskModelSavingDelegate(EngineStepDelegate): + """Engine step delegate that takes care of saving a task model to the database. + + It can also be given another EngineStepDelegate. + """ + + def __init__( + self, + serializer: BpmnWorkflowSerializer, + process_instance: ProcessInstanceModel, + secondary_engine_step_delegate: Optional[EngineStepDelegate] = None, + ) -> None: + self.secondary_engine_step_delegate = secondary_engine_step_delegate + self.process_instance = process_instance + + self.current_task_model: Optional[TaskModel] = None + self.serializer = serializer + + def should_update_task_model(self) -> bool: + return self.process_instance.bpmn_process_id is not None + + def will_complete_task(self, spiff_task: SpiffTask) -> None: + if self.should_update_task_model(): + self.current_task_model = ( + TaskService.find_or_create_task_model_from_spiff_task( + spiff_task, self.process_instance + ) + ) + self.current_task_model.start_in_seconds = time.time() + if self.secondary_engine_step_delegate: + self.secondary_engine_step_delegate.will_complete_task(spiff_task) + + def did_complete_task(self, spiff_task: SpiffTask) -> None: + if self.current_task_model and self.should_update_task_model(): + self.current_task_model.end_in_seconds = time.time() + TaskService.update_task_model_and_add_to_db_session( + self.current_task_model, spiff_task, self.serializer + ) + db.session.add(self.current_task_model) + if self.secondary_engine_step_delegate: + self.secondary_engine_step_delegate.did_complete_task(spiff_task) + + def save(self, _commit: bool = True) -> None: + if self.secondary_engine_step_delegate: + self.secondary_engine_step_delegate.save(commit=False) + db.session.commit() + + class StepDetailLoggingDelegate(EngineStepDelegate): """Engine step delegate that takes care of logging spiff step details. @@ -65,28 +117,29 @@ class StepDetailLoggingDelegate(EngineStepDelegate): "Transactional Subprocess", } - def should_log(self, task: SpiffTask) -> bool: + def should_log(self, spiff_task: SpiffTask) -> bool: return ( - task.task_spec.spec_type in self.tasks_to_log - and not task.task_spec.name.endswith(".EndJoin") + spiff_task.task_spec.spec_type in self.tasks_to_log + and not spiff_task.task_spec.name.endswith(".EndJoin") ) - def will_complete_task(self, task: SpiffTask) -> None: - if self.should_log(task): + def will_complete_task(self, spiff_task: SpiffTask) -> None: + if self.should_log(spiff_task): self.current_task_start_in_seconds = time.time() self.increment_spiff_step() - def did_complete_task(self, task: SpiffTask) -> None: - if self.should_log(task): + def did_complete_task(self, spiff_task: SpiffTask) -> None: + if self.should_log(spiff_task): self.step_details.append( self.spiff_step_details_mapping( - task, self.current_task_start_in_seconds, time.time() + spiff_task, self.current_task_start_in_seconds, time.time() ) ) - def save(self) -> None: + def save(self, commit: bool = True) -> None: db.session.bulk_insert_mappings(SpiffStepDetailsModel, self.step_details) - db.session.commit() + if commit: + db.session.commit() class ExecutionStrategy: @@ -136,12 +189,12 @@ class RunUntilServiceTaskExecutionStrategy(ExecutionStrategy): ] ) while engine_steps: - for task in engine_steps: - if task.task_spec.spec_type == "Service Task": + for spiff_task in engine_steps: + if spiff_task.task_spec.spec_type == "Service Task": return - self.delegate.will_complete_task(task) - task.complete() - self.delegate.did_complete_task(task) + self.delegate.will_complete_task(spiff_task) + spiff_task.complete() + self.delegate.did_complete_task(spiff_task) engine_steps = list( [ diff --git a/spiffworkflow-backend/tests/data/manual_task/manual_task.bpmn b/spiffworkflow-backend/tests/data/manual_task/manual_task.bpmn index 4f0fba72..f4d0190b 100644 --- a/spiffworkflow-backend/tests/data/manual_task/manual_task.bpmn +++ b/spiffworkflow-backend/tests/data/manual_task/manual_task.bpmn @@ -2,39 +2,52 @@ - Flow_1xlck7g + Flow_0stlaxe - - + Flow_0nnh2x9 - + ## Hello - Flow_1xlck7g + Flow_1pmem7s Flow_0nnh2x9 + + + + Flow_0stlaxe + Flow_1pmem7s + the_new_var = "HEY" + - - + + + + + - + + + + + - + - + diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py index 75eb3146..7fa0f13b 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py @@ -10,6 +10,7 @@ from tests.spiffworkflow_backend.helpers.test_data import load_test_spec from spiffworkflow_backend.models.group import GroupModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus +from spiffworkflow_backend.models.task import TaskModel # noqa: F401 from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.authorization_service import ( @@ -292,6 +293,68 @@ class TestProcessInstanceProcessor(BaseTest): assert spiff_task is not None assert spiff_task.state == TaskState.COMPLETED + def test_properly_saves_tasks_when_running( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + """Test_does_not_recreate_human_tasks_on_multiple_saves.""" + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) + initiator_user = self.find_or_create_user("initiator_user") + finance_user_three = self.find_or_create_user("testuser3") + assert initiator_user.principal is not None + assert finance_user_three.principal is not None + AuthorizationService.import_permissions_from_yaml_file() + + finance_group = GroupModel.query.filter_by(identifier="Finance Team").first() + assert finance_group is not None + + process_model = load_test_spec( + process_model_id="test_group/manual_task", + bpmn_file_name="manual_task.bpmn", + process_model_source_directory="manual_task", + ) + process_instance = self.create_process_instance_from_process_model( + process_model=process_model, user=initiator_user + ) + processor = ProcessInstanceProcessor(process_instance) + processor.do_engine_steps(save=True) + assert len(process_instance.active_human_tasks) == 1 + initial_human_task_id = process_instance.active_human_tasks[0].id + + # save again to ensure we go attempt to process the human tasks again + processor.save() + + assert len(process_instance.active_human_tasks) == 1 + assert initial_human_task_id == process_instance.active_human_tasks[0].id + + processor = ProcessInstanceProcessor(process_instance) + human_task_one = process_instance.active_human_tasks[0] + spiff_task = processor.__class__.get_task_by_bpmn_identifier( + human_task_one.task_name, processor.bpmn_process_instance + ) + ProcessInstanceService.complete_form_task( + processor, spiff_task, {}, initiator_user, human_task_one + ) + + process_instance_relookup = ProcessInstanceModel.query.filter_by( + id=process_instance.id + ).first() + processor = ProcessInstanceProcessor(process_instance_relookup) + assert process_instance_relookup.status == "complete" + task = TaskModel.query.filter_by(guid=human_task_one.task_id).first() + assert task.state == "COMPLETED" + end_event_spiff_task = processor.__class__.get_task_by_bpmn_identifier( + "end_event_of_manual_task_model", processor.bpmn_process_instance + ) + assert end_event_spiff_task + assert end_event_spiff_task.state == TaskState.COMPLETED + # # NOTE: also check the spiff task from the new processor + def test_does_not_recreate_human_tasks_on_multiple_saves( self, app: Flask, From ec52e85f048e21673e8d2e81fddb4820c569fd4b Mon Sep 17 00:00:00 2001 From: jasquat Date: Thu, 9 Mar 2023 17:23:08 -0500 Subject: [PATCH 07/48] added comment --- .../services/workflow_execution_service.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index 89ca9caa..bdd8ebee 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -58,6 +58,10 @@ class TaskModelSavingDelegate(EngineStepDelegate): self.serializer = serializer def should_update_task_model(self) -> bool: + """We need to figure out if we have previously save task info on this process intance. + + Use the bpmn_process_id to do this. + """ return self.process_instance.bpmn_process_id is not None def will_complete_task(self, spiff_task: SpiffTask) -> None: From 513871ad211b754954fb61dd906442a3621c438b Mon Sep 17 00:00:00 2001 From: burnettk Date: Thu, 9 Mar 2023 22:37:45 -0500 Subject: [PATCH 08/48] get bpmn process for spiff task by guid or from the process instance --- .../services/task_service.py | 85 +++++++++++++++---- 1 file changed, 67 insertions(+), 18 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py index 9c6db0fd..32dbade8 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py @@ -1,18 +1,23 @@ +import json +from hashlib import sha256 +from typing import Optional + +from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer # type: ignore +from SpiffWorkflow.task import Task as SpiffTask # type: ignore +from SpiffWorkflow.task import TaskStateNames + +from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel +from spiffworkflow_backend.models.db import db +from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401 from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.task import TaskModel # noqa: F401 -from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer # type: ignore -from SpiffWorkflow.task import TaskStateNames # type: ignore -from SpiffWorkflow.task import Task as SpiffTask -from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401 -from spiffworkflow_backend.models.db import db -from hashlib import sha256 -import json -class TaskService(): - +class TaskService: @classmethod - def update_task_data_on_task_model(cls, task_model: TaskModel, task_data_dict: dict) -> None: + def update_task_data_on_task_model( + cls, task_model: TaskModel, task_data_dict: dict + ) -> None: task_data_json = json.dumps(task_data_dict, sort_keys=True).encode("utf8") task_data_hash = sha256(task_data_json).hexdigest() if task_model.json_data_hash != task_data_hash: @@ -27,23 +32,67 @@ class TaskService(): task_model.json_data_hash = task_data_hash @classmethod - def update_task_model_and_add_to_db_session(cls, task_model: TaskModel, spiff_task: SpiffTask, serializer: BpmnWorkflowSerializer) -> None: + def update_task_model_and_add_to_db_session( + cls, + task_model: TaskModel, + spiff_task: SpiffTask, + serializer: BpmnWorkflowSerializer, + ) -> None: """Updates properties_json and data on given task_model. This will NOT update start_in_seconds or end_in_seconds. """ new_properties_json = serializer.task_to_dict(spiff_task) task_model.properties_json = new_properties_json - task_model.state = TaskStateNames[new_properties_json['state']] + task_model.state = TaskStateNames[new_properties_json["state"]] cls.update_task_data_on_task_model(task_model, spiff_task.data) db.session.add(task_model) @classmethod - def find_or_create_task_model_from_spiff_task(cls, spiff_task: SpiffTask, process_instance: ProcessInstanceModel) -> TaskModel: + def find_or_create_task_model_from_spiff_task( + cls, spiff_task: SpiffTask, process_instance: ProcessInstanceModel + ) -> TaskModel: spiff_task_guid = str(spiff_task.id) - task_model: TaskModel = TaskModel.query.filter_by(guid=spiff_task_guid).first() - # if task_model is None: - # task_model = TaskModel(guid=spiff_task_guid, bpmn_process_id=process_instance.bpmn_process_id) - # db.session.add(task_model) - # db.session.commit() + task_model: Optional[TaskModel] = TaskModel.query.filter_by( + guid=spiff_task_guid + ).first() + if task_model is None: + bpmn_process = cls.task_bpmn_process(spiff_task, process_instance) + task_model = TaskModel( + guid=spiff_task_guid, bpmn_process_id=bpmn_process.id + ) + db.session.add(task_model) + db.session.commit() return task_model + + @classmethod + def task_subprocess_guid(cls, spiff_task: SpiffTask) -> Optional[str]: + top_level_workflow = spiff_task.workflow._get_outermost_workflow() + my_wf = spiff_task.workflow # This is the workflow the spiff_task is part of + my_sp_id = None + if my_wf != top_level_workflow: + # All the subprocesses are at the top level, so you can just compare them + for sp_id, sp in top_level_workflow.subprocesses.items(): + if sp == my_wf: + my_sp_id = sp_id + break + return my_sp_id + + @classmethod + def task_bpmn_process( + cls, spiff_task: SpiffTask, process_instance: ProcessInstanceModel + ) -> BpmnProcessModel: + subprocess_guid = cls.task_subprocess_guid(spiff_task) + if subprocess_guid is None: + # This is the top level workflow, which has no guid + return process_instance.bpmn_process + else: + bpmn_process: Optional[BpmnProcessModel] = BpmnProcessModel.query.filter_by( + guid=subprocess_guid + ).first() + if bpmn_process is None: + spiff_task_guid = str(spiff_task.id) + raise Exception( + f"Could not find bpmn_process for task {spiff_task_guid}" + ) + return bpmn_process From f0ff6aa20efeadf311bd3af3b159975ddbc74266 Mon Sep 17 00:00:00 2001 From: Kevin Burnett <18027+burnettk@users.noreply.github.com> Date: Fri, 10 Mar 2023 07:09:02 -0800 Subject: [PATCH 09/48] Update README.md --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index b5d272a6..66a3b3c3 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,18 @@ Requires at root: - .pre-commit-config.yaml - pyproject.toml +Run cypress automated browser tests +----------------------------------- + +Get the app running so you can access the frontend at http://localhost:7001 in your browser. + +First install nodejs, ideally the version in .tool-versions (but likely other versions will work). + +Then: + + cd spiffworkflow-frontend + npm install + ./bin/run_cypress_tests_locally License ------- From 8f1975d7e2670bddff7cb09e853d27e78db13560 Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Fri, 10 Mar 2023 10:21:07 -0500 Subject: [PATCH 10/48] Bump SpiffWorkflow (#171) --- spiffworkflow-backend/poetry.lock | 4264 ++++++++++++++--------------- 1 file changed, 2132 insertions(+), 2132 deletions(-) diff --git a/spiffworkflow-backend/poetry.lock b/spiffworkflow-backend/poetry.lock index c95a3a95..7ac2c0c3 100644 --- a/spiffworkflow-backend/poetry.lock +++ b/spiffworkflow-backend/poetry.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Poetry and should not be changed by hand. + [[package]] name = "alabaster" version = "0.7.12" @@ -5,6 +7,10 @@ description = "A configurable sidebar-enabled Sphinx theme" category = "main" optional = false python-versions = "*" +files = [ + {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, + {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, +] [[package]] name = "alembic" @@ -13,6 +19,10 @@ description = "A database migration tool for SQLAlchemy." category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "alembic-1.8.1-py3-none-any.whl", hash = "sha256:0a024d7f2de88d738d7395ff866997314c837be6104e90c5724350313dee4da4"}, + {file = "alembic-1.8.1.tar.gz", hash = "sha256:cd0b5e45b14b706426b833f06369b9a6d5ee03f826ec3238723ce8caaf6e5ffa"}, +] [package.dependencies] Mako = "*" @@ -28,6 +38,10 @@ description = "Low-level AMQP client for Python (fork of amqplib)." category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "amqp-5.1.1-py3-none-any.whl", hash = "sha256:6f0956d2c23d8fa6e7691934d8c3930eadb44972cbbd1a7ae3a520f735d43359"}, + {file = "amqp-5.1.1.tar.gz", hash = "sha256:2c1b13fecc0893e946c65cbd5f36427861cffa4ea2201d8f6fca22e2a373b5e2"}, +] [package.dependencies] vine = ">=5.0.0" @@ -39,6 +53,10 @@ description = "A library for parsing ISO 8601 strings." category = "main" optional = false python-versions = "*" +files = [ + {file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"}, + {file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"}, +] [package.extras] dev = ["black", "coverage", "isort", "pre-commit", "pyenchant", "pylint"] @@ -50,6 +68,10 @@ description = "In-process task scheduler with Cron-like capabilities" category = "main" optional = false python-versions = "!=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +files = [ + {file = "APScheduler-3.9.1.post1-py2.py3-none-any.whl", hash = "sha256:c8c618241dbb2785ed5a687504b14cb1851d6f7b5a4edf3a51e39cc6a069967a"}, + {file = "APScheduler-3.9.1.post1.tar.gz", hash = "sha256:b2bea0309569da53a7261bfa0ce19c67ddbfe151bda776a6a907579fdbd3eb2a"}, +] [package.dependencies] pytz = "*" @@ -77,6 +99,10 @@ description = "An abstract syntax tree for Python with inference support." category = "main" optional = false python-versions = ">=3.7.2" +files = [ + {file = "astroid-2.13.3-py3-none-any.whl", hash = "sha256:14c1603c41cc61aae731cad1884a073c4645e26f126d13ac8346113c95577f3b"}, + {file = "astroid-2.13.3.tar.gz", hash = "sha256:6afc22718a48a689ca24a97981ad377ba7fb78c133f40335dfd16772f29bcfb1"}, +] [package.dependencies] lazy-object-proxy = ">=1.4.0" @@ -93,6 +119,10 @@ description = "Classes Without Boilerplate" category = "main" optional = false python-versions = ">=3.5" +files = [ + {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, + {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, +] [package.extras] dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] @@ -107,6 +137,10 @@ description = "Internationalization utilities" category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, + {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, +] [package.dependencies] pytz = ">=2015.7" @@ -118,6 +152,10 @@ description = "Security oriented static analyser for python code." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "bandit-1.7.2-py3-none-any.whl", hash = "sha256:e20402cadfd126d85b68ed4c8862959663c8c372dbbb1fca8f8e2c9f55a067ec"}, + {file = "bandit-1.7.2.tar.gz", hash = "sha256:6d11adea0214a43813887bfe71a377b5a9955e4c826c8ffd341b494e3ab25260"}, +] [package.dependencies] colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} @@ -137,6 +175,29 @@ description = "Modern password hashing for your software and your servers" category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "bcrypt-4.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2"}, + {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535"}, + {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e"}, + {file = "bcrypt-4.0.1-cp36-abi3-win32.whl", hash = "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab"}, + {file = "bcrypt-4.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71"}, + {file = "bcrypt-4.0.1.tar.gz", hash = "sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd"}, +] [package.extras] tests = ["pytest (>=3.2.1,!=3.3.0)"] @@ -149,6 +210,10 @@ description = "Screen-scraping library" category = "dev" optional = false python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"}, + {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"}, +] [package.dependencies] soupsieve = ">1.2" @@ -164,6 +229,10 @@ description = "Python multiprocessing fork with improvements and bugfixes" category = "main" optional = false python-versions = "*" +files = [ + {file = "billiard-3.6.4.0-py3-none-any.whl", hash = "sha256:87103ea78fa6ab4d5c751c4909bcff74617d985de7fa8b672cf8618afd5a875b"}, + {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"}, +] [[package]] name = "black" @@ -172,6 +241,29 @@ description = "The uncompromising code formatter." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"}, + {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"}, + {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"}, + {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"}, + {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"}, + {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"}, + {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"}, + {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"}, + {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"}, + {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"}, + {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"}, + {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"}, + {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"}, + {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"}, + {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"}, + {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"}, + {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"}, + {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"}, + {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"}, + {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"}, + {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"}, +] [package.dependencies] click = ">=8.0.0" @@ -194,6 +286,10 @@ description = "Fast, simple object-to-object and broadcast signaling" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "blinker-1.5-py2.py3-none-any.whl", hash = "sha256:1eb563df6fdbc39eeddc177d953203f99f097e9bf0e2b8f9f3cf18b6ca425e36"}, + {file = "blinker-1.5.tar.gz", hash = "sha256:923e5e2f69c155f2cc42dafbbd70e16e3fde24d2d4aa2ab72fbe386238892462"}, +] [[package]] name = "celery" @@ -202,6 +298,10 @@ description = "Distributed Task Queue." category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "celery-5.2.7-py3-none-any.whl", hash = "sha256:138420c020cd58d6707e6257b6beda91fd39af7afde5d36c6334d175302c0e14"}, + {file = "celery-5.2.7.tar.gz", hash = "sha256:fafbd82934d30f8a004f81e8f7a062e31413a23d444be8ee3326553915958c6d"}, +] [package.dependencies] billiard = ">=3.6.4.0,<4.0" @@ -253,6 +353,10 @@ description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, +] [[package]] name = "cfgv" @@ -261,6 +365,10 @@ description = "Validate configuration and produce human readable error messages. category = "dev" optional = false python-versions = ">=3.6.1" +files = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, +] [[package]] name = "charset-normalizer" @@ -269,6 +377,10 @@ description = "The Real First Universal Charset Detector. Open, modern and activ category = "main" optional = false python-versions = ">=3.6.0" +files = [ + {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, + {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, +] [package.extras] unicode-backport = ["unicodedata2"] @@ -280,6 +392,10 @@ description = "Utilities for refactoring imports in python-like syntax." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "classify_imports-4.2.0-py2.py3-none-any.whl", hash = "sha256:dbbc264b70a470ed8c6c95976a11dfb8b7f63df44ed1af87328bbed2663f5161"}, + {file = "classify_imports-4.2.0.tar.gz", hash = "sha256:7abfb7ea92149b29d046bd34573d247ba6e68cc28100c801eba4af17964fc40e"}, +] [[package]] name = "click" @@ -288,6 +404,10 @@ description = "Composable command line interface toolkit" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} @@ -299,6 +419,10 @@ description = "Enables git-like *did-you-mean* feature in click" category = "main" optional = false python-versions = ">=3.6.2,<4.0.0" +files = [ + {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"}, + {file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"}, +] [package.dependencies] click = ">=7" @@ -310,6 +434,10 @@ description = "An extension module for click to enable registering CLI commands category = "main" optional = false python-versions = "*" +files = [ + {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, + {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, +] [package.dependencies] click = ">=4.0" @@ -324,6 +452,10 @@ description = "REPL plugin for Click" category = "main" optional = false python-versions = "*" +files = [ + {file = "click-repl-0.2.0.tar.gz", hash = "sha256:cd12f68d745bf6151210790540b4cb064c7b13e571bc64b6957d98d120dacfd8"}, + {file = "click_repl-0.2.0-py3-none-any.whl", hash = "sha256:94b3fbbc9406a236f176e0506524b2937e4b23b6f4c0c0b2a0a83f8a64e9194b"}, +] [package.dependencies] click = "*" @@ -337,6 +469,10 @@ description = "Click utility functions" category = "main" optional = false python-versions = "*" +files = [ + {file = "clickclick-20.10.2-py2.py3-none-any.whl", hash = "sha256:c8f33e6d9ec83f68416dd2136a7950125bd256ec39ccc9a85c6e280a16be2bb5"}, + {file = "clickclick-20.10.2.tar.gz", hash = "sha256:4efb13e62353e34c5eef7ed6582c4920b418d7dedc86d819e22ee089ba01802c"}, +] [package.dependencies] click = ">=4.0" @@ -349,6 +485,10 @@ description = "Cross-platform colored terminal text." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, + {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, +] [[package]] name = "configparser" @@ -357,6 +497,10 @@ description = "Updated configparser from stdlib for earlier Pythons." category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "configparser-5.3.0-py3-none-any.whl", hash = "sha256:b065779fd93c6bf4cee42202fa4351b4bb842e96a3fb469440e484517a49b9fa"}, + {file = "configparser-5.3.0.tar.gz", hash = "sha256:8be267824b541c09b08db124917f48ab525a6c3e837011f3130781a224c57090"}, +] [package.extras] docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] @@ -369,6 +513,10 @@ description = "Connexion - API first applications with OpenAPI/Swagger and Flask category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "connexion-2.14.1-py2.py3-none-any.whl", hash = "sha256:f343717241b4c4802a694c38fee66fb1693c897fe4ea5a957fa9b3b07caf6394"}, + {file = "connexion-2.14.1.tar.gz", hash = "sha256:99aa5781e70a7b94f8ffae8cf89f309d49cdb811bbd65a8e2f2546f3b19a01e6"}, +] [package.dependencies] clickclick = ">=1.2,<21" @@ -396,1994 +544,7 @@ description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=3.7" - -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "darglint" -version = "1.8.1" -description = "A utility for ensuring Google-style docstrings stay up to date with the source code." -category = "dev" -optional = false -python-versions = ">=3.6,<4.0" - -[[package]] -name = "dateparser" -version = "1.1.2" -description = "Date parsing library designed to parse dates from HTML pages" -category = "main" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -python-dateutil = "*" -pytz = "*" -regex = "<2019.02.19 || >2019.02.19,<2021.8.27 || >2021.8.27,<2022.3.15" -tzlocal = "*" - -[package.extras] -calendars = ["convertdate", "convertdate", "hijri-converter"] -fasttext = ["fasttext"] -langdetect = ["langdetect"] - -[[package]] -name = "dill" -version = "0.3.6" -description = "serialize all of python" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -graph = ["objgraph (>=1.7.2)"] - -[[package]] -name = "distlib" -version = "0.3.6" -description = "Distribution utilities" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "docutils" -version = "0.19" -description = "Docutils -- Python Documentation Utilities" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "dparse" -version = "0.6.2" -description = "A parser for Python dependency files" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -packaging = "*" -toml = "*" - -[package.extras] -conda = ["pyyaml"] -pipenv = ["pipenv"] - -[[package]] -name = "exceptiongroup" -version = "1.0.4" -description = "Backport of PEP 654 (exception groups)" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "filelock" -version = "3.8.0" -description = "A platform independent file lock." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"] -testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"] - -[[package]] -name = "flake8" -version = "4.0.1" -description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.8.0,<2.9.0" -pyflakes = ">=2.4.0,<2.5.0" - -[[package]] -name = "flake8-bandit" -version = "2.1.2" -description = "Automated security testing with bandit and flake8." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -bandit = "*" -flake8 = "*" -flake8-polyfill = "*" -pycodestyle = "*" - -[[package]] -name = "flake8-bugbear" -version = "22.10.25" -description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -attrs = ">=19.2.0" -flake8 = ">=3.0.0" - -[package.extras] -dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "tox"] - -[[package]] -name = "flake8-docstrings" -version = "1.6.0" -description = "Extension for flake8 which uses pydocstyle to check docstrings" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -flake8 = ">=3" -pydocstyle = ">=2.1" - -[[package]] -name = "flake8-polyfill" -version = "1.0.2" -description = "Polyfill package for Flake8 plugins" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -flake8 = "*" - -[[package]] -name = "flake8-rst-docstrings" -version = "0.2.7" -description = "Python docstring reStructuredText (RST) validator" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -flake8 = ">=3.0.0" -pygments = "*" -restructuredtext-lint = "*" - -[[package]] -name = "Flask" -version = "2.2.2" -description = "A simple framework for building complex web applications." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -click = ">=8.0" -importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} -itsdangerous = ">=2.0" -Jinja2 = ">=3.0" -Werkzeug = ">=2.2.2" - -[package.extras] -async = ["asgiref (>=3.2)"] -dotenv = ["python-dotenv"] - -[[package]] -name = "Flask-Admin" -version = "1.6.0" -description = "Simple and extensible admin interface framework for Flask" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -Flask = ">=0.7" -wtforms = "*" - -[package.extras] -aws = ["boto"] -azure = ["azure-storage-blob"] - -[[package]] -name = "Flask-Bcrypt" -version = "1.0.1" -description = "Brcrypt hashing for Flask." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -bcrypt = ">=3.1.1" -Flask = "*" - -[[package]] -name = "flask-bpmn" -version = "0.0.0" -description = "Flask Bpmn" -category = "main" -optional = false -python-versions = "^3.7" -develop = false - -[package.dependencies] -click = "^8.0.1" -flask = "*" -flask-admin = "*" -flask-bcrypt = "*" -flask-cors = "*" -flask-mail = "*" -flask-marshmallow = "*" -flask-migrate = "*" -flask-restful = "*" -greenlet = "^2.0.1" -sentry-sdk = "*" -sphinx-autoapi = "^2.0.0" -spiffworkflow = "*" -werkzeug = "*" - -[package.source] -type = "git" -url = "https://github.com/sartography/flask-bpmn" -reference = "main" -resolved_reference = "c18306300f4312b8d36e0197fd6b62399180d0b1" - -[[package]] -name = "Flask-Cors" -version = "3.0.10" -description = "A Flask extension adding a decorator for CORS support" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -Flask = ">=0.9" -Six = "*" - -[[package]] -name = "flask-jwt-extended" -version = "4.4.4" -description = "Extended JWT integration with Flask" -category = "main" -optional = false -python-versions = ">=3.7,<4" - -[package.dependencies] -Flask = ">=2.0,<3.0" -PyJWT = ">=2.0,<3.0" -Werkzeug = ">=0.14" - -[package.extras] -asymmetric-crypto = ["cryptography (>=3.3.1)"] - -[[package]] -name = "Flask-Mail" -version = "0.9.1" -description = "Flask extension for sending email" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -blinker = "*" -Flask = "*" - -[[package]] -name = "flask-marshmallow" -version = "0.14.0" -description = "Flask + marshmallow for beautiful APIs" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -Flask = "*" -marshmallow = ">=2.0.0" -six = ">=1.9.0" - -[package.extras] -dev = ["flake8 (==3.8.3)", "flake8-bugbear (==20.1.4)", "flask-sqlalchemy", "marshmallow-sqlalchemy (>=0.13.0)", "marshmallow-sqlalchemy (>=0.13.0,<0.19.0)", "mock", "pre-commit (>=2.4,<3.0)", "pytest", "tox"] -docs = ["Sphinx (==3.2.1)", "marshmallow-sqlalchemy (>=0.13.0)", "sphinx-issues (==1.2.0)"] -lint = ["flake8 (==3.8.3)", "flake8-bugbear (==20.1.4)", "pre-commit (>=2.4,<3.0)"] -sqlalchemy = ["flask-sqlalchemy", "marshmallow-sqlalchemy (>=0.13.0)", "marshmallow-sqlalchemy (>=0.13.0,<0.19.0)"] -tests = ["flask-sqlalchemy", "marshmallow-sqlalchemy (>=0.13.0)", "marshmallow-sqlalchemy (>=0.13.0,<0.19.0)", "mock", "pytest"] - -[[package]] -name = "Flask-Migrate" -version = "3.1.0" -description = "SQLAlchemy database migrations for Flask applications using Alembic." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -alembic = ">=0.7" -Flask = ">=0.9" -Flask-SQLAlchemy = ">=1.0" - -[[package]] -name = "Flask-RESTful" -version = "0.3.9" -description = "Simple framework for creating REST APIs" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -aniso8601 = ">=0.82" -Flask = ">=0.8" -pytz = "*" -six = ">=1.3.0" - -[package.extras] -docs = ["sphinx"] - -[[package]] -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" - -[package.dependencies] -Flask = "*" -pycryptodome = "*" - -[[package]] -name = "flask-sqlalchemy" -version = "3.0.2" -description = "Add SQLAlchemy support to your Flask application." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -Flask = ">=2.2" -SQLAlchemy = ">=1.4.18" - -[[package]] -name = "furo" -version = "2022.9.29" -description = "A clean customisable Sphinx documentation theme." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -beautifulsoup4 = "*" -pygments = ">=2.7" -sphinx = ">=4.0,<6.0" -sphinx-basic-ng = "*" - -[[package]] -name = "gitdb" -version = "4.0.9" -description = "Git Object Database" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -smmap = ">=3.0.1,<6" - -[[package]] -name = "GitPython" -version = "3.1.29" -description = "GitPython is a python library used to interact with Git repositories" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -gitdb = ">=4.0.1,<5" - -[[package]] -name = "greenlet" -version = "2.0.1" -description = "Lightweight in-process concurrent programming" -category = "main" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" - -[package.extras] -docs = ["Sphinx", "docutils (<0.18)"] -test = ["faulthandler", "objgraph", "psutil"] - -[[package]] -name = "gunicorn" -version = "20.1.0" -description = "WSGI HTTP Server for UNIX" -category = "main" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -setuptools = ">=3.0" - -[package.extras] -eventlet = ["eventlet (>=0.24.1)"] -gevent = ["gevent (>=1.4.0)"] -setproctitle = ["setproctitle"] -tornado = ["tornado (>=0.2)"] - -[[package]] -name = "identify" -version = "2.5.6" -description = "File identification library for Python" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.extras] -license = ["ukkonen"] - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "imagesize" -version = "1.4.1" -description = "Getting image size from png/jpeg/jpeg2000/gif file" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "importlib-metadata" -version = "4.13.0" -description = "Read metadata from Python packages" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] -perf = ["ipython"] -testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] - -[[package]] -name = "inflection" -version = "0.5.1" -description = "A port of Ruby on Rails inflector to Python" -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "isort" -version = "5.11.4" -description = "A Python utility / library to sort Python imports." -category = "main" -optional = false -python-versions = ">=3.7.0" - -[package.extras] -colors = ["colorama (>=0.4.3,<0.5.0)"] -pipfile-deprecated-finder = ["pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] - -[[package]] -name = "itsdangerous" -version = "2.1.2" -description = "Safely pass data to untrusted environments and back." -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "Jinja2" -version = "3.1.2" -description = "A very fast and expressive template engine." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "jsonschema" -version = "4.16.0" -description = "An implementation of JSON Schema validation for Python" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -attrs = ">=17.4.0" -pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] - -[[package]] -name = "kombu" -version = "5.2.4" -description = "Messaging library for Python." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -amqp = ">=5.0.9,<6.0.0" -vine = "*" - -[package.extras] -azureservicebus = ["azure-servicebus (>=7.0.0)"] -azurestoragequeues = ["azure-storage-queue"] -consul = ["python-consul (>=0.6.0)"] -librabbitmq = ["librabbitmq (>=2.0.0)"] -mongodb = ["pymongo (>=3.3.0,<3.12.1)"] -msgpack = ["msgpack"] -pyro = ["pyro4"] -qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] -redis = ["redis (>=3.4.1,!=4.0.0,!=4.0.1)"] -slmq = ["softlayer-messaging (>=1.0.3)"] -sqlalchemy = ["sqlalchemy"] -sqs = ["boto3 (>=1.9.12)", "pycurl (>=7.44.1,<7.45.0)", "urllib3 (>=1.26.7)"] -yaml = ["PyYAML (>=3.10)"] -zookeeper = ["kazoo (>=1.3.1)"] - -[[package]] -name = "lazy-object-proxy" -version = "1.7.1" -description = "A fast and thorough lazy object proxy." -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "livereload" -version = "2.6.3" -description = "Python LiveReload is an awesome tool for web developers" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -six = "*" -tornado = {version = "*", markers = "python_version > \"2.7\""} - -[[package]] -name = "lxml" -version = "4.9.1" -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.*" - -[package.extras] -cssselect = ["cssselect (>=0.7)"] -html5 = ["html5lib"] -htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=0.29.7)"] - -[[package]] -name = "Mako" -version = "1.2.3" -description = "A super-fast templating language that borrows the best ideas from the existing templating languages." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -MarkupSafe = ">=0.9.2" - -[package.extras] -babel = ["Babel"] -lingua = ["lingua"] -testing = ["pytest"] - -[[package]] -name = "MarkupSafe" -version = "2.1.1" -description = "Safely add untrusted strings to HTML/XML markup." -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "marshmallow" -version = "3.18.0" -description = "A lightweight library for converting complex datatypes to and from native Python datatypes." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -packaging = ">=17.0" - -[package.extras] -dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"] -docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.1.1)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] -lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)"] -tests = ["pytest", "pytz", "simplejson"] - -[[package]] -name = "marshmallow-enum" -version = "1.5.1" -description = "Enum field for Marshmallow" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -marshmallow = ">=2.0.0" - -[[package]] -name = "marshmallow-sqlalchemy" -version = "0.28.1" -description = "SQLAlchemy integration with the marshmallow (de)serialization library" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -marshmallow = ">=3.0.0" -packaging = ">=21.3" -SQLAlchemy = ">=1.3.0" - -[package.extras] -dev = ["flake8 (==4.0.1)", "flake8-bugbear (==22.7.1)", "pre-commit (>=2.0,<3.0)", "pytest", "pytest-lazy-fixture (>=0.6.2)", "tox"] -docs = ["alabaster (==0.7.12)", "sphinx (==4.4.0)", "sphinx-issues (==3.0.1)"] -lint = ["flake8 (==4.0.1)", "flake8-bugbear (==22.7.1)", "pre-commit (>=2.0,<3.0)"] -tests = ["pytest", "pytest-lazy-fixture (>=0.6.2)"] - -[[package]] -name = "mccabe" -version = "0.6.1" -description = "McCabe checker, plugin for flake8" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "mypy" -version = "0.982" -description = "Optional static typing for Python" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -mypy-extensions = ">=0.4.3" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=3.10" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -python2 = ["typed-ast (>=1.4.0,<2)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "mysql-connector-python" -version = "8.0.32" -description = "MySQL driver written in Python" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -protobuf = ">=3.11.0,<=3.20.3" - -[package.extras] -compression = ["lz4 (>=2.1.6,<=3.1.3)", "zstandard (>=0.12.0,<=0.19.0)"] -dns-srv = ["dnspython (>=1.16.0,<=2.1.0)"] -gssapi = ["gssapi (>=1.6.9,<=1.8.2)"] - -[[package]] -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.*" - -[package.dependencies] -setuptools = "*" - -[[package]] -name = "packaging" -version = "21.3" -description = "Core utilities for Python packages" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" - -[[package]] -name = "pathspec" -version = "0.10.1" -description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "pbr" -version = "5.10.0" -description = "Python Build Reasonableness" -category = "dev" -optional = false -python-versions = ">=2.6" - -[[package]] -name = "pep8-naming" -version = "0.13.2" -description = "Check PEP-8 naming conventions, plugin for flake8" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -flake8 = ">=3.9.1" - -[[package]] -name = "platformdirs" -version = "2.5.2" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] -test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] - -[[package]] -name = "pluggy" -version = "1.0.0" -description = "plugin and hook calling mechanisms for python" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pre-commit" -version = "2.20.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -toml = "*" -virtualenv = ">=20.0.8" - -[[package]] -name = "pre-commit-hooks" -version = "4.3.0" -description = "Some out-of-the-box hooks for pre-commit." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -"ruamel.yaml" = ">=0.15" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "prompt-toolkit" -version = "3.0.31" -description = "Library for building powerful interactive command lines in Python" -category = "main" -optional = false -python-versions = ">=3.6.2" - -[package.dependencies] -wcwidth = "*" - -[[package]] -name = "protobuf" -version = "3.20.3" -description = "Protocol Buffers" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "psycopg2" -version = "2.9.4" -description = "psycopg2 - Python-PostgreSQL Database Adapter" -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "pycodestyle" -version = "2.8.0" -description = "Python style guide checker" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -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.*" - -[[package]] -name = "pydocstyle" -version = "6.1.1" -description = "Python docstring style checker" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -snowballstemmer = "*" - -[package.extras] -toml = ["toml"] - -[[package]] -name = "pyflakes" -version = "2.4.0" -description = "passive checker of Python programs" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "Pygments" -version = "2.13.0" -description = "Pygments is a syntax highlighting package written in Python." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -plugins = ["importlib-metadata"] - -[[package]] -name = "pyjwt" -version = "2.6.0" -description = "JSON Web Token implementation in Python" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -crypto = ["cryptography (>=3.4.0)"] -dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] -docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] - -[[package]] -name = "pylint" -version = "2.15.10" -description = "python code static checker" -category = "main" -optional = false -python-versions = ">=3.7.2" - -[package.dependencies] -astroid = ">=2.12.13,<=2.14.0-dev0" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = [ - {version = ">=0.2", markers = "python_version < \"3.11\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, -] -isort = ">=4.2.5,<6" -mccabe = ">=0.6,<0.8" -platformdirs = ">=2.2.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -tomlkit = ">=0.10.1" -typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} - -[package.extras] -spelling = ["pyenchant (>=3.2,<4.0)"] -testutils = ["gitpython (>3)"] - -[[package]] -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" - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - -[[package]] -name = "pyrsistent" -version = "0.18.1" -description = "Persistent/Functional/Immutable data structures" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "pytest" -version = "7.2.0" -description = "pytest: simple powerful testing with Python" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -attrs = ">=19.2.0" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} - -[package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] - -[[package]] -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" - -[package.dependencies] -Flask = "*" -pytest = ">=5.2" -Werkzeug = ">=0.7" - -[package.extras] -docs = ["Sphinx", "sphinx-rtd-theme"] - -[[package]] -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 = "*" - -[package.dependencies] -Flask-SQLAlchemy = ">=2.3" -packaging = ">=14.1" -pytest = ">=3.2.1" -pytest-mock = ">=1.6.2" -SQLAlchemy = ">=1.2.2" - -[package.extras] -tests = ["psycopg2-binary", "pytest (>=6.0.1)", "pytest-postgresql (>=2.4.0,<4.0.0)"] - -[[package]] -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" - -[package.dependencies] -pytest = ">=5.0" - -[package.extras] -dev = ["pre-commit", "pytest-asyncio", "tox"] - -[[package]] -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" - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pytz" -version = "2022.7.1" -description = "World timezone definitions, modern and historical" -category = "main" -optional = false -python-versions = "*" - -[[package]] -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" - -[package.dependencies] -tzdata = {version = "*", markers = "python_version >= \"3.6\""} - -[[package]] -name = "pyupgrade" -version = "3.1.0" -description = "A tool to automatically upgrade syntax for newer versions." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -tokenize-rt = ">=3.2.0" - -[[package]] -name = "PyYAML" -version = "6.0" -description = "YAML parser and emitter for Python" -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "regex" -version = "2022.3.2" -description = "Alternative regular expression module, to replace re." -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "reorder-python-imports" -version = "3.9.0" -description = "Tool for reordering python imports" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -classify-imports = ">=4.1" - -[[package]] -name = "requests" -version = "2.28.1" -description = "Python HTTP for Humans." -category = "main" -optional = false -python-versions = ">=3.7, <4" - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<3" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "restrictedpython" -version = "6.0" -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" - -[package.extras] -docs = ["Sphinx", "sphinx-rtd-theme"] -test = ["pytest", "pytest-mock"] - -[[package]] -name = "restructuredtext-lint" -version = "1.4.0" -description = "reStructuredText linter" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -docutils = ">=0.11,<1.0" - -[[package]] -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" - -[package.dependencies] -"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} - -[package.extras] -docs = ["ryd"] -jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] - -[[package]] -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" - -[[package]] -name = "safety" -version = "2.3.1" -description = "Checks installed dependencies for known vulnerabilities and licenses." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -Click = ">=8.0.2" -dparse = ">=0.6.2" -packaging = ">=21.0" -requests = "*" -"ruamel.yaml" = ">=0.17.21" -setuptools = ">=19.3" - -[package.extras] -github = ["jinja2 (>=3.1.0)", "pygithub (>=1.43.3)"] -gitlab = ["python-gitlab (>=1.3.0)"] - -[[package]] -name = "sentry-sdk" -version = "1.16.0" -description = "Python client for Sentry (https://sentry.io)" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -certifi = "*" -urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} - -[package.extras] -aiohttp = ["aiohttp (>=3.5)"] -arq = ["arq (>=0.23)"] -beam = ["apache-beam (>=2.12)"] -bottle = ["bottle (>=0.12.13)"] -celery = ["celery (>=3)"] -chalice = ["chalice (>=1.16.0)"] -django = ["django (>=1.8)"] -falcon = ["falcon (>=1.4)"] -fastapi = ["fastapi (>=0.79.0)"] -flask = ["blinker (>=1.1)", "flask (>=0.11)"] -httpx = ["httpx (>=0.16.0)"] -huey = ["huey (>=2)"] -opentelemetry = ["opentelemetry-distro (>=0.35b0)"] -pure-eval = ["asttokens", "executing", "pure-eval"] -pymongo = ["pymongo (>=3.1)"] -pyspark = ["pyspark (>=2.4.4)"] -quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] -rq = ["rq (>=0.6)"] -sanic = ["sanic (>=0.8)"] -sqlalchemy = ["sqlalchemy (>=1.2)"] -starlette = ["starlette (>=0.19.1)"] -starlite = ["starlite (>=1.48)"] -tornado = ["tornado (>=5)"] - -[[package]] -name = "setuptools" -version = "65.5.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "simplejson" -version = "3.17.6" -description = "Simple, fast, extensible JSON encoder/decoder for Python" -category = "main" -optional = false -python-versions = ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -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.*" - -[[package]] -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" - -[[package]] -name = "snowballstemmer" -version = "2.2.0" -description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "soupsieve" -version = "2.3.2.post1" -description = "A modern CSS selector implementation for Beautiful Soup." -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "Sphinx" -version = "5.3.0" -description = "Python documentation generator" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -alabaster = ">=0.7,<0.8" -babel = ">=2.9" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.14,<0.20" -imagesize = ">=1.3" -importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} -Jinja2 = ">=3.0" -packaging = ">=21.0" -Pygments = ">=2.12" -requests = ">=2.5.0" -snowballstemmer = ">=2.0" -sphinxcontrib-applehelp = "*" -sphinxcontrib-devhelp = "*" -sphinxcontrib-htmlhelp = ">=2.0.0" -sphinxcontrib-jsmath = "*" -sphinxcontrib-qthelp = "*" -sphinxcontrib-serializinghtml = ">=1.1.5" - -[package.extras] -docs = ["sphinxcontrib-websupport"] -lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "flake8-simplify", "isort", "mypy (>=0.981)", "sphinx-lint", "types-requests", "types-typed-ast"] -test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"] - -[[package]] -name = "sphinx-autoapi" -version = "2.0.0" -description = "Sphinx API documentation generator" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -astroid = ">=2.7" -Jinja2 = "*" -PyYAML = "*" -sphinx = ">=4.0" -unidecode = "*" - -[package.extras] -docs = ["sphinx", "sphinx-rtd-theme"] -dotnet = ["sphinxcontrib-dotnetdomain"] -go = ["sphinxcontrib-golangdomain"] - -[[package]] -name = "sphinx-autobuild" -version = "2021.3.14" -description = "Rebuild Sphinx documentation on changes, with live-reload in the browser." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -colorama = "*" -livereload = "*" -sphinx = "*" - -[package.extras] -test = ["pytest", "pytest-cov"] - -[[package]] -name = "sphinx-basic-ng" -version = "1.0.0b1" -description = "A modern skeleton for Sphinx themes." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -sphinx = ">=4.0" - -[package.extras] -docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] - -[[package]] -name = "sphinx-click" -version = "4.3.0" -description = "Sphinx extension that automatically documents click applications" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -click = ">=7.0" -docutils = "*" -sphinx = ">=2.0" - -[[package]] -name = "sphinxcontrib-applehelp" -version = "1.0.2" -description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" -category = "main" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "1.0.2" -description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." -category = "main" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.0.0" -description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["html5lib", "pytest"] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -description = "A sphinx extension which renders display math in HTML via JavaScript" -category = "main" -optional = false -python-versions = ">=3.5" - -[package.extras] -test = ["flake8", "mypy", "pytest"] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "1.0.3" -description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." -category = "main" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "1.1.5" -description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." -category = "main" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "SpiffWorkflow" -version = "1.2.1" -description = "A workflow framework and BPMN/DMN Processor" -category = "main" -optional = false -python-versions = "*" -develop = false - -[package.dependencies] -celery = "*" -configparser = "*" -lxml = "*" - -[package.source] -type = "git" -url = "https://github.com/sartography/SpiffWorkflow" -reference = "main" -resolved_reference = "bee868d38b2c3da680c7a96b6a634d16b90d5861" - -[[package]] -name = "SQLAlchemy" -version = "1.4.42" -description = "Database Abstraction Library" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" - -[package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (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\")"} - -[package.extras] -aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] -aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] -asyncio = ["greenlet (!=0.4.17)"] -asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"] -mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"] -mssql = ["pyodbc"] -mssql-pymssql = ["pymssql"] -mssql-pyodbc = ["pyodbc"] -mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"] -mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] -mysql-connector = ["mysql-connector-python"] -oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"] -postgresql = ["psycopg2 (>=2.7)"] -postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] -postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"] -postgresql-psycopg2binary = ["psycopg2-binary"] -postgresql-psycopg2cffi = ["psycopg2cffi"] -pymysql = ["pymysql", "pymysql (<1)"] -sqlcipher = ["sqlcipher3_binary"] - -[[package]] -name = "sqlalchemy-stubs" -version = "0.4" -description = "SQLAlchemy stubs and mypy plugin" -category = "main" -optional = false -python-versions = "*" -develop = false - -[package.dependencies] -mypy = ">=0.790" -typing-extensions = ">=3.7.4" - -[package.source] -type = "git" -url = "https://github.com/burnettk/sqlalchemy-stubs.git" -reference = "scoped-session-delete" -resolved_reference = "d1176931684ce5b327539cc9567d4a1cd8ef1efd" - -[[package]] -name = "stevedore" -version = "4.0.1" -description = "Manage dynamic plugins for Python applications" -category = "dev" -optional = false -python-versions = ">=3.8" - -[package.dependencies] -pbr = ">=2.0.0,<2.1.0 || >2.1.0" - -[[package]] -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 = "*" - -[package.dependencies] -Jinja2 = ">=2.0" - -[[package]] -name = "tokenize-rt" -version = "4.2.1" -description = "A wrapper around the stdlib `tokenize` which roundtrips." -category = "dev" -optional = false -python-versions = ">=3.6.1" - -[[package]] -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.*" - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "tomlkit" -version = "0.11.6" -description = "Style preserving TOML library" -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "tornado" -version = "6.2" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -category = "dev" -optional = false -python-versions = ">= 3.7" - -[[package]] -name = "typeguard" -version = "2.13.3" -description = "Run-time type checker for Python" -category = "dev" -optional = false -python-versions = ">=3.5.3" - -[package.extras] -doc = ["sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["mypy", "pytest", "typing-extensions"] - -[[package]] -name = "types-click" -version = "7.1.8" -description = "Typing stubs for click" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "types-dateparser" -version = "1.1.4.1" -description = "Typing stubs for dateparser" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "types-Flask" -version = "1.1.6" -description = "Typing stubs for Flask" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -types-click = "*" -types-Jinja2 = "*" -types-Werkzeug = "*" - -[[package]] -name = "types-Jinja2" -version = "2.11.9" -description = "Typing stubs for Jinja2" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -types-MarkupSafe = "*" - -[[package]] -name = "types-MarkupSafe" -version = "1.1.10" -description = "Typing stubs for MarkupSafe" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "types-pytz" -version = "2022.5.0.0" -description = "Typing stubs for pytz" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "types-PyYAML" -version = "6.0.12" -description = "Typing stubs for PyYAML" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "types-requests" -version = "2.28.11.2" -description = "Typing stubs for requests" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -types-urllib3 = "<1.27" - -[[package]] -name = "types-urllib3" -version = "1.26.25.1" -description = "Typing stubs for urllib3" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "types-Werkzeug" -version = "1.0.9" -description = "Typing stubs for Werkzeug" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "typing-extensions" -version = "4.4.0" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "tzdata" -version = "2022.5" -description = "Provider of IANA time zone data" -category = "main" -optional = false -python-versions = ">=2" - -[[package]] -name = "tzlocal" -version = "4.2" -description = "tzinfo object for the local timezone" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pytz-deprecation-shim = "*" -tzdata = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -devenv = ["black", "pyroma", "pytest-cov", "zest.releaser"] -test = ["pytest (>=4.3)", "pytest-mock (>=3.3)"] - -[[package]] -name = "Unidecode" -version = "1.3.6" -description = "ASCII transliterations of Unicode text" -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "urllib3" -version = "1.26.12" -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.*, <4" - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[[package]] -name = "vine" -version = "5.0.0" -description = "Promises, promises, promises." -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "virtualenv" -version = "20.16.5" -description = "Virtual Python Environment builder" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -distlib = ">=0.3.5,<1" -filelock = ">=3.4.1,<4" -platformdirs = ">=2.4,<3" - -[package.extras] -docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"] -testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] - -[[package]] -name = "wcwidth" -version = "0.2.5" -description = "Measures the displayed width of unicode strings in a terminal" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "Werkzeug" -version = "2.2.2" -description = "The comprehensive WSGI web application library." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -MarkupSafe = ">=2.1.1" - -[package.extras] -watchdog = ["watchdog"] - -[[package]] -name = "wrapt" -version = "1.14.1" -description = "Module for decorators, wrappers and monkey patching." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[[package]] -name = "WTForms" -version = "3.0.1" -description = "Form validation and rendering for Python web development." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -MarkupSafe = "*" - -[package.extras] -email = ["email-validator"] - -[[package]] -name = "xdoctest" -version = "1.1.0" -description = "A rewrite of the builtin doctest module" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -colorama = {version = "*", optional = true, markers = "platform_system == \"Windows\" and extra == \"colors\""} -Pygments = {version = "*", optional = true, markers = "python_version >= \"3.5.0\" and extra == \"colors\""} -six = "*" - -[package.extras] -all = ["IPython", "IPython", "Pygments", "Pygments", "attrs", "cmake", "codecov", "colorama", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert", "ninja", "pybind11", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest-cov", "pytest-cov", "pytest-cov", "pytest-cov", "scikit-build", "six", "typing"] -all-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "cmake (==3.21.2)", "codecov (==2.0.15)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==5.2.0)", "ipykernel (==6.0.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==6.1.5)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "ninja (==1.10.2)", "pybind11 (==2.7.1)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "scikit-build (==0.11.1)", "six (==1.11.0)", "typing (==3.7.4)"] -colors = ["Pygments", "Pygments", "colorama"] -jupyter = ["IPython", "IPython", "attrs", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert"] -optional = ["IPython", "IPython", "Pygments", "Pygments", "attrs", "colorama", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert", "pyflakes", "tomli"] -optional-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==5.2.0)", "ipykernel (==6.0.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==6.1.5)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "pyflakes (==2.2.0)", "tomli (==0.2.0)"] -runtime-strict = ["six (==1.11.0)"] -tests = ["cmake", "codecov", "ninja", "pybind11", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest-cov", "pytest-cov", "pytest-cov", "pytest-cov", "scikit-build", "typing"] -tests-strict = ["cmake (==3.21.2)", "codecov (==2.0.15)", "ninja (==1.10.2)", "pybind11 (==2.7.1)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "scikit-build (==0.11.1)", "typing (==3.7.4)"] - -[[package]] -name = "zipp" -version = "3.10.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] -testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - -[metadata] -lock-version = "1.1" -python-versions = ">=3.9,<3.12" -content-hash = "2fd5138221eabec441b601bb3769be478bed42099e72e20f7b8aaa1c1a888909" - -[metadata.files] -alabaster = [ - {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, - {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, -] -alembic = [ - {file = "alembic-1.8.1-py3-none-any.whl", hash = "sha256:0a024d7f2de88d738d7395ff866997314c837be6104e90c5724350313dee4da4"}, - {file = "alembic-1.8.1.tar.gz", hash = "sha256:cd0b5e45b14b706426b833f06369b9a6d5ee03f826ec3238723ce8caaf6e5ffa"}, -] -amqp = [ - {file = "amqp-5.1.1-py3-none-any.whl", hash = "sha256:6f0956d2c23d8fa6e7691934d8c3930eadb44972cbbd1a7ae3a520f735d43359"}, - {file = "amqp-5.1.1.tar.gz", hash = "sha256:2c1b13fecc0893e946c65cbd5f36427861cffa4ea2201d8f6fca22e2a373b5e2"}, -] -aniso8601 = [ - {file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"}, - {file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"}, -] -apscheduler = [ - {file = "APScheduler-3.9.1.post1-py2.py3-none-any.whl", hash = "sha256:c8c618241dbb2785ed5a687504b14cb1851d6f7b5a4edf3a51e39cc6a069967a"}, - {file = "APScheduler-3.9.1.post1.tar.gz", hash = "sha256:b2bea0309569da53a7261bfa0ce19c67ddbfe151bda776a6a907579fdbd3eb2a"}, -] -astroid = [ - {file = "astroid-2.13.3-py3-none-any.whl", hash = "sha256:14c1603c41cc61aae731cad1884a073c4645e26f126d13ac8346113c95577f3b"}, - {file = "astroid-2.13.3.tar.gz", hash = "sha256:6afc22718a48a689ca24a97981ad377ba7fb78c133f40335dfd16772f29bcfb1"}, -] -attrs = [ - {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, - {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, -] -Babel = [ - {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, - {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, -] -bandit = [ - {file = "bandit-1.7.2-py3-none-any.whl", hash = "sha256:e20402cadfd126d85b68ed4c8862959663c8c372dbbb1fca8f8e2c9f55a067ec"}, - {file = "bandit-1.7.2.tar.gz", hash = "sha256:6d11adea0214a43813887bfe71a377b5a9955e4c826c8ffd341b494e3ab25260"}, -] -bcrypt = [ - {file = "bcrypt-4.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2"}, - {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535"}, - {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e"}, - {file = "bcrypt-4.0.1-cp36-abi3-win32.whl", hash = "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab"}, - {file = "bcrypt-4.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9"}, - {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c"}, - {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b"}, - {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d"}, - {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d"}, - {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215"}, - {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c"}, - {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda"}, - {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665"}, - {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71"}, - {file = "bcrypt-4.0.1.tar.gz", hash = "sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd"}, -] -beautifulsoup4 = [ - {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"}, - {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"}, -] -billiard = [ - {file = "billiard-3.6.4.0-py3-none-any.whl", hash = "sha256:87103ea78fa6ab4d5c751c4909bcff74617d985de7fa8b672cf8618afd5a875b"}, - {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"}, -] -black = [ - {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"}, - {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"}, - {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"}, - {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"}, - {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"}, - {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"}, - {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"}, - {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"}, - {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"}, - {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"}, - {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"}, - {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"}, - {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"}, - {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"}, - {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"}, - {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"}, - {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"}, - {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"}, - {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"}, - {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"}, - {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"}, -] -blinker = [ - {file = "blinker-1.5-py2.py3-none-any.whl", hash = "sha256:1eb563df6fdbc39eeddc177d953203f99f097e9bf0e2b8f9f3cf18b6ca425e36"}, - {file = "blinker-1.5.tar.gz", hash = "sha256:923e5e2f69c155f2cc42dafbbd70e16e3fde24d2d4aa2ab72fbe386238892462"}, -] -celery = [ - {file = "celery-5.2.7-py3-none-any.whl", hash = "sha256:138420c020cd58d6707e6257b6beda91fd39af7afde5d36c6334d175302c0e14"}, - {file = "celery-5.2.7.tar.gz", hash = "sha256:fafbd82934d30f8a004f81e8f7a062e31413a23d444be8ee3326553915958c6d"}, -] -certifi = [ - {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, - {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, -] -cfgv = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, -] -charset-normalizer = [ - {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, - {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, -] -classify-imports = [ - {file = "classify_imports-4.2.0-py2.py3-none-any.whl", hash = "sha256:dbbc264b70a470ed8c6c95976a11dfb8b7f63df44ed1af87328bbed2663f5161"}, - {file = "classify_imports-4.2.0.tar.gz", hash = "sha256:7abfb7ea92149b29d046bd34573d247ba6e68cc28100c801eba4af17964fc40e"}, -] -click = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] -click-didyoumean = [ - {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"}, - {file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"}, -] -click-plugins = [ - {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, - {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, -] -click-repl = [ - {file = "click-repl-0.2.0.tar.gz", hash = "sha256:cd12f68d745bf6151210790540b4cb064c7b13e571bc64b6957d98d120dacfd8"}, - {file = "click_repl-0.2.0-py3-none-any.whl", hash = "sha256:94b3fbbc9406a236f176e0506524b2937e4b23b6f4c0c0b2a0a83f8a64e9194b"}, -] -clickclick = [ - {file = "clickclick-20.10.2-py2.py3-none-any.whl", hash = "sha256:c8f33e6d9ec83f68416dd2136a7950125bd256ec39ccc9a85c6e280a16be2bb5"}, - {file = "clickclick-20.10.2.tar.gz", hash = "sha256:4efb13e62353e34c5eef7ed6582c4920b418d7dedc86d819e22ee089ba01802c"}, -] -colorama = [ - {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, - {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, -] -configparser = [ - {file = "configparser-5.3.0-py3-none-any.whl", hash = "sha256:b065779fd93c6bf4cee42202fa4351b4bb842e96a3fb469440e484517a49b9fa"}, - {file = "configparser-5.3.0.tar.gz", hash = "sha256:8be267824b541c09b08db124917f48ab525a6c3e837011f3130781a224c57090"}, -] -connexion = [ - {file = "connexion-2.14.1-py2.py3-none-any.whl", hash = "sha256:f343717241b4c4802a694c38fee66fb1693c897fe4ea5a957fa9b3b07caf6394"}, - {file = "connexion-2.14.1.tar.gz", hash = "sha256:99aa5781e70a7b94f8ffae8cf89f309d49cdb811bbd65a8e2f2546f3b19a01e6"}, -] -coverage = [ +files = [ {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, @@ -2435,117 +596,530 @@ coverage = [ {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, ] -darglint = [ + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "darglint" +version = "1.8.1" +description = "A utility for ensuring Google-style docstrings stay up to date with the source code." +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" +files = [ {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, ] -dateparser = [ + +[[package]] +name = "dateparser" +version = "1.1.2" +description = "Date parsing library designed to parse dates from HTML pages" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ {file = "dateparser-1.1.2-py2.py3-none-any.whl", hash = "sha256:d31659dc806a7d88e2b510b2c74f68b525ae531f145c62a57a99bd616b7f90cf"}, {file = "dateparser-1.1.2.tar.gz", hash = "sha256:3821bf191f95b2658c4abd91571c09821ce7a2bc179bf6cefd8b4515c3ccf9ef"}, ] -dill = [ + +[package.dependencies] +python-dateutil = "*" +pytz = "*" +regex = "<2019.02.19 || >2019.02.19,<2021.8.27 || >2021.8.27,<2022.3.15" +tzlocal = "*" + +[package.extras] +calendars = ["convertdate", "convertdate", "hijri-converter"] +fasttext = ["fasttext"] +langdetect = ["langdetect"] + +[[package]] +name = "dill" +version = "0.3.6" +description = "serialize all of python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "dill-0.3.6-py3-none-any.whl", hash = "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0"}, {file = "dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"}, ] -distlib = [ + +[package.extras] +graph = ["objgraph (>=1.7.2)"] + +[[package]] +name = "distlib" +version = "0.3.6" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" +files = [ {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, ] -docutils = [ + +[[package]] +name = "docutils" +version = "0.19" +description = "Docutils -- Python Documentation Utilities" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"}, {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"}, ] -dparse = [ + +[[package]] +name = "dparse" +version = "0.6.2" +description = "A parser for Python dependency files" +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ {file = "dparse-0.6.2-py3-none-any.whl", hash = "sha256:8097076f1dd26c377f30d4745e6ec18fef42f3bf493933b842ac5bafad8c345f"}, {file = "dparse-0.6.2.tar.gz", hash = "sha256:d45255bda21f998bc7ddf2afd5e62505ba6134756ba2d42a84c56b0826614dfe"}, ] -exceptiongroup = [ + +[package.dependencies] +packaging = "*" +toml = "*" + +[package.extras] +conda = ["pyyaml"] +pipenv = ["pipenv"] + +[[package]] +name = "exceptiongroup" +version = "1.0.4" +description = "Backport of PEP 654 (exception groups)" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"}, {file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"}, ] -filelock = [ + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "filelock" +version = "3.8.0" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, ] -flake8 = [ + +[package.extras] +docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"] +testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "flake8" +version = "4.0.1" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, ] -flake8-bandit = [ + +[package.dependencies] +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.8.0,<2.9.0" +pyflakes = ">=2.4.0,<2.5.0" + +[[package]] +name = "flake8-bandit" +version = "2.1.2" +description = "Automated security testing with bandit and flake8." +category = "dev" +optional = false +python-versions = "*" +files = [ {file = "flake8_bandit-2.1.2.tar.gz", hash = "sha256:687fc8da2e4a239b206af2e54a90093572a60d0954f3054e23690739b0b0de3b"}, ] -flake8-bugbear = [ + +[package.dependencies] +bandit = "*" +flake8 = "*" +flake8-polyfill = "*" +pycodestyle = "*" + +[[package]] +name = "flake8-bugbear" +version = "22.10.25" +description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "flake8-bugbear-22.10.25.tar.gz", hash = "sha256:89e51284eb929fbb7f23fbd428491e7427f7cdc8b45a77248daffe86a039d696"}, {file = "flake8_bugbear-22.10.25-py3-none-any.whl", hash = "sha256:584631b608dc0d7d3f9201046d5840a45502da4732d5e8df6c7ac1694a91cb9e"}, ] -flake8-docstrings = [ + +[package.dependencies] +attrs = ">=19.2.0" +flake8 = ">=3.0.0" + +[package.extras] +dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "tox"] + +[[package]] +name = "flake8-docstrings" +version = "1.6.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +category = "dev" +optional = false +python-versions = "*" +files = [ {file = "flake8-docstrings-1.6.0.tar.gz", hash = "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"}, {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"}, ] -flake8-polyfill = [ + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-polyfill" +version = "1.0.2" +description = "Polyfill package for Flake8 plugins" +category = "dev" +optional = false +python-versions = "*" +files = [ {file = "flake8-polyfill-1.0.2.tar.gz", hash = "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"}, {file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"}, ] -flake8-rst-docstrings = [ + +[package.dependencies] +flake8 = "*" + +[[package]] +name = "flake8-rst-docstrings" +version = "0.2.7" +description = "Python docstring reStructuredText (RST) validator" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "flake8-rst-docstrings-0.2.7.tar.gz", hash = "sha256:2740067ab9237559dd45a3434d8c987792c7b259ca563621a3b95efe201f5382"}, {file = "flake8_rst_docstrings-0.2.7-py3-none-any.whl", hash = "sha256:5d56075dce360bcc9c6775bfe7cb431aa395de600ca7e8d40580a28d50b2a803"}, ] -Flask = [ + +[package.dependencies] +flake8 = ">=3.0.0" +pygments = "*" +restructuredtext-lint = "*" + +[[package]] +name = "Flask" +version = "2.2.2" +description = "A simple framework for building complex web applications." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "Flask-2.2.2-py3-none-any.whl", hash = "sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526"}, {file = "Flask-2.2.2.tar.gz", hash = "sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b"}, ] -Flask-Admin = [ + +[package.dependencies] +click = ">=8.0" +importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} +itsdangerous = ">=2.0" +Jinja2 = ">=3.0" +Werkzeug = ">=2.2.2" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "Flask-Admin" +version = "1.6.0" +description = "Simple and extensible admin interface framework for Flask" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ {file = "Flask-Admin-1.6.0.tar.gz", hash = "sha256:424ffc79b7b0dfff051555686ea12e86e48dffacac14beaa319fb4502ac40988"}, ] -Flask-Bcrypt = [ + +[package.dependencies] +Flask = ">=0.7" +wtforms = "*" + +[package.extras] +aws = ["boto"] +azure = ["azure-storage-blob"] + +[[package]] +name = "Flask-Bcrypt" +version = "1.0.1" +description = "Brcrypt hashing for Flask." +category = "main" +optional = false +python-versions = "*" +files = [ {file = "Flask-Bcrypt-1.0.1.tar.gz", hash = "sha256:f07b66b811417ea64eb188ae6455b0b708a793d966e1a80ceec4a23bc42a4369"}, {file = "Flask_Bcrypt-1.0.1-py3-none-any.whl", hash = "sha256:062fd991dc9118d05ac0583675507b9fe4670e44416c97e0e6819d03d01f808a"}, ] -flask-bpmn = [] -Flask-Cors = [ + +[package.dependencies] +bcrypt = ">=3.1.1" +Flask = "*" + +[[package]] +name = "flask-bpmn" +version = "0.0.0" +description = "Flask Bpmn" +category = "main" +optional = false +python-versions = "^3.7" +files = [] +develop = false + +[package.dependencies] +click = "^8.0.1" +flask = "*" +flask-admin = "*" +flask-bcrypt = "*" +flask-cors = "*" +flask-mail = "*" +flask-marshmallow = "*" +flask-migrate = "*" +flask-restful = "*" +greenlet = "^2.0.1" +sentry-sdk = "*" +sphinx-autoapi = "^2.0.0" +spiffworkflow = "*" +werkzeug = "*" + +[package.source] +type = "git" +url = "https://github.com/sartography/flask-bpmn" +reference = "main" +resolved_reference = "c18306300f4312b8d36e0197fd6b62399180d0b1" + +[[package]] +name = "Flask-Cors" +version = "3.0.10" +description = "A Flask extension adding a decorator for CORS support" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "Flask-Cors-3.0.10.tar.gz", hash = "sha256:b60839393f3b84a0f3746f6cdca56c1ad7426aa738b70d6c61375857823181de"}, {file = "Flask_Cors-3.0.10-py2.py3-none-any.whl", hash = "sha256:74efc975af1194fc7891ff5cd85b0f7478be4f7f59fe158102e91abb72bb4438"}, ] -flask-jwt-extended = [ + +[package.dependencies] +Flask = ">=0.9" +Six = "*" + +[[package]] +name = "flask-jwt-extended" +version = "4.4.4" +description = "Extended JWT integration with Flask" +category = "main" +optional = false +python-versions = ">=3.7,<4" +files = [ {file = "Flask-JWT-Extended-4.4.4.tar.gz", hash = "sha256:62b521d75494c290a646ae8acc77123721e4364790f1e64af0038d823961fbf0"}, {file = "Flask_JWT_Extended-4.4.4-py2.py3-none-any.whl", hash = "sha256:a85eebfa17c339a7260c4643475af444784ba6de5588adda67406f0a75599553"}, ] -Flask-Mail = [ + +[package.dependencies] +Flask = ">=2.0,<3.0" +PyJWT = ">=2.0,<3.0" +Werkzeug = ">=0.14" + +[package.extras] +asymmetric-crypto = ["cryptography (>=3.3.1)"] + +[[package]] +name = "Flask-Mail" +version = "0.9.1" +description = "Flask extension for sending email" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "Flask-Mail-0.9.1.tar.gz", hash = "sha256:22e5eb9a940bf407bcf30410ecc3708f3c56cc44b29c34e1726fe85006935f41"}, ] -flask-marshmallow = [ + +[package.dependencies] +blinker = "*" +Flask = "*" + +[[package]] +name = "flask-marshmallow" +version = "0.14.0" +description = "Flask + marshmallow for beautiful APIs" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "flask-marshmallow-0.14.0.tar.gz", hash = "sha256:bd01a6372cbe50e36f205cfff0fc5dab0b7b662c4c8b2c4fc06a3151b2950950"}, {file = "flask_marshmallow-0.14.0-py2.py3-none-any.whl", hash = "sha256:2adcd782b5a4a6c5ae3c96701f320d8ca6997995a52b2661093c56cc3ed24754"}, ] -Flask-Migrate = [ + +[package.dependencies] +Flask = "*" +marshmallow = ">=2.0.0" +six = ">=1.9.0" + +[package.extras] +dev = ["flake8 (==3.8.3)", "flake8-bugbear (==20.1.4)", "flask-sqlalchemy", "marshmallow-sqlalchemy (>=0.13.0)", "marshmallow-sqlalchemy (>=0.13.0,<0.19.0)", "mock", "pre-commit (>=2.4,<3.0)", "pytest", "tox"] +docs = ["Sphinx (==3.2.1)", "marshmallow-sqlalchemy (>=0.13.0)", "sphinx-issues (==1.2.0)"] +lint = ["flake8 (==3.8.3)", "flake8-bugbear (==20.1.4)", "pre-commit (>=2.4,<3.0)"] +sqlalchemy = ["flask-sqlalchemy", "marshmallow-sqlalchemy (>=0.13.0)", "marshmallow-sqlalchemy (>=0.13.0,<0.19.0)"] +tests = ["flask-sqlalchemy", "marshmallow-sqlalchemy (>=0.13.0)", "marshmallow-sqlalchemy (>=0.13.0,<0.19.0)", "mock", "pytest"] + +[[package]] +name = "Flask-Migrate" +version = "3.1.0" +description = "SQLAlchemy database migrations for Flask applications using Alembic." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ {file = "Flask-Migrate-3.1.0.tar.gz", hash = "sha256:57d6060839e3a7f150eaab6fe4e726d9e3e7cffe2150fb223d73f92421c6d1d9"}, {file = "Flask_Migrate-3.1.0-py3-none-any.whl", hash = "sha256:a6498706241aba6be7a251078de9cf166d74307bca41a4ca3e403c9d39e2f897"}, ] -Flask-RESTful = [ + +[package.dependencies] +alembic = ">=0.7" +Flask = ">=0.9" +Flask-SQLAlchemy = ">=1.0" + +[[package]] +name = "Flask-RESTful" +version = "0.3.9" +description = "Simple framework for creating REST APIs" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "Flask-RESTful-0.3.9.tar.gz", hash = "sha256:ccec650b835d48192138c85329ae03735e6ced58e9b2d9c2146d6c84c06fa53e"}, {file = "Flask_RESTful-0.3.9-py2.py3-none-any.whl", hash = "sha256:4970c49b6488e46c520b325f54833374dc2b98e211f1b272bd4b0c516232afe2"}, ] -flask-simple-crypt = [ + +[package.dependencies] +aniso8601 = ">=0.82" +Flask = ">=0.8" +pytz = "*" +six = ">=1.3.0" + +[package.extras] +docs = ["sphinx"] + +[[package]] +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 = [ {file = "Flask-Simple-Crypt-0.3.3.tar.gz", hash = "sha256:0d4033b6c9a03ac85d10f0fd213914390217dc53b2d41d153fa050fee9723594"}, {file = "Flask_Simple_Crypt-0.3.3-py3-none-any.whl", hash = "sha256:08c3fcad955ac148bb885b1de4798c1cfce8512452072beee414bacf1552e8ef"}, ] -flask-sqlalchemy = [ + +[package.dependencies] +Flask = "*" +pycryptodome = "*" + +[[package]] +name = "flask-sqlalchemy" +version = "3.0.2" +description = "Add SQLAlchemy support to your Flask application." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "Flask-SQLAlchemy-3.0.2.tar.gz", hash = "sha256:16199f5b3ddfb69e0df2f52ae4c76aedbfec823462349dabb21a1b2e0a2b65e9"}, {file = "Flask_SQLAlchemy-3.0.2-py3-none-any.whl", hash = "sha256:7d0cd9cf73e64a996bb881a1ebd01633fc5a6d11c36ea27f7b5e251dc45476e7"}, ] -furo = [ + +[package.dependencies] +Flask = ">=2.2" +SQLAlchemy = ">=1.4.18" + +[[package]] +name = "furo" +version = "2022.9.29" +description = "A clean customisable Sphinx documentation theme." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "furo-2022.9.29-py3-none-any.whl", hash = "sha256:559ee17999c0f52728481dcf6b1b0cf8c9743e68c5e3a18cb45a7992747869a9"}, {file = "furo-2022.9.29.tar.gz", hash = "sha256:d4238145629c623609c2deb5384f8d036e2a1ee2a101d64b67b4348112470dbd"}, ] -gitdb = [ + +[package.dependencies] +beautifulsoup4 = "*" +pygments = ">=2.7" +sphinx = ">=4.0,<6.0" +sphinx-basic-ng = "*" + +[[package]] +name = "gitdb" +version = "4.0.9" +description = "Git Object Database" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, ] -GitPython = [ + +[package.dependencies] +smmap = ">=3.0.1,<6" + +[[package]] +name = "GitPython" +version = "3.1.29" +description = "GitPython is a python library used to interact with Git repositories" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "GitPython-3.1.29-py3-none-any.whl", hash = "sha256:41eea0deec2deea139b459ac03656f0dd28fc4a3387240ec1d3c259a2c47850f"}, {file = "GitPython-3.1.29.tar.gz", hash = "sha256:cc36bfc4a3f913e66805a28e84703e419d9c264c1077e537b54f0e1af85dbefd"}, ] -greenlet = [ + +[package.dependencies] +gitdb = ">=4.0.1,<5" + +[[package]] +name = "greenlet" +version = "2.0.1" +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 = [ {file = "greenlet-2.0.1-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:9ed358312e63bf683b9ef22c8e442ef6c5c02973f0c2a939ec1d7b50c974015c"}, {file = "greenlet-2.0.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:4f09b0010e55bec3239278f642a8a506b91034f03a4fb28289a7d448a67f1515"}, {file = "greenlet-2.0.1-cp27-cp27m-win32.whl", hash = "sha256:1407fe45246632d0ffb7a3f4a520ba4e6051fc2cbd61ba1f806900c27f47706a"}, @@ -2607,55 +1181,223 @@ greenlet = [ {file = "greenlet-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b23d2a46d53210b498e5b701a1913697671988f4bf8e10f935433f6e7c332fb6"}, {file = "greenlet-2.0.1.tar.gz", hash = "sha256:42e602564460da0e8ee67cb6d7236363ee5e131aa15943b6670e44e5c2ed0f67"}, ] -gunicorn = [ + +[package.extras] +docs = ["Sphinx", "docutils (<0.18)"] +test = ["faulthandler", "objgraph", "psutil"] + +[[package]] +name = "gunicorn" +version = "20.1.0" +description = "WSGI HTTP Server for UNIX" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, ] -identify = [ + +[package.dependencies] +setuptools = ">=3.0" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "identify" +version = "2.5.6" +description = "File identification library for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "identify-2.5.6-py2.py3-none-any.whl", hash = "sha256:b276db7ec52d7e89f5bc4653380e33054ddc803d25875952ad90b0f012cbcdaa"}, {file = "identify-2.5.6.tar.gz", hash = "sha256:6c32dbd747aa4ceee1df33f25fed0b0f6e0d65721b15bd151307ff7056d50245"}, ] -idna = [ + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] -imagesize = [ + +[[package]] +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] -importlib-metadata = [ + +[[package]] +name = "importlib-metadata" +version = "4.13.0" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "importlib_metadata-4.13.0-py3-none-any.whl", hash = "sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116"}, {file = "importlib_metadata-4.13.0.tar.gz", hash = "sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d"}, ] -inflection = [ + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + +[[package]] +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 = [ {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, ] -iniconfig = [ + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] -isort = [ + +[[package]] +name = "isort" +version = "5.11.4" +description = "A Python utility / library to sort Python imports." +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ {file = "isort-5.11.4-py3-none-any.whl", hash = "sha256:c033fd0edb91000a7f09527fe5c75321878f98322a77ddcc81adbd83724afb7b"}, {file = "isort-5.11.4.tar.gz", hash = "sha256:6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6"}, ] -itsdangerous = [ + +[package.extras] +colors = ["colorama (>=0.4.3,<0.5.0)"] +pipfile-deprecated-finder = ["pipreqs", "requirementslib"] +plugins = ["setuptools"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] + +[[package]] +name = "itsdangerous" +version = "2.1.2" +description = "Safely pass data to untrusted environments and back." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, ] -Jinja2 = [ + +[[package]] +name = "Jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] -jsonschema = [ + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonschema" +version = "4.16.0" +description = "An implementation of JSON Schema validation for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "jsonschema-4.16.0-py3-none-any.whl", hash = "sha256:9e74b8f9738d6a946d70705dc692b74b5429cd0960d58e79ffecfc43b2221eb9"}, {file = "jsonschema-4.16.0.tar.gz", hash = "sha256:165059f076eff6971bae5b742fc029a7b4ef3f9bcf04c14e4776a7605de14b23"}, ] -kombu = [ + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "kombu" +version = "5.2.4" +description = "Messaging library for Python." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "kombu-5.2.4-py3-none-any.whl", hash = "sha256:8b213b24293d3417bcf0d2f5537b7f756079e3ea232a8386dcc89a59fd2361a4"}, {file = "kombu-5.2.4.tar.gz", hash = "sha256:37cee3ee725f94ea8bb173eaab7c1760203ea53bbebae226328600f9d2799610"}, ] -lazy-object-proxy = [ + +[package.dependencies] +amqp = ">=5.0.9,<6.0.0" +vine = "*" + +[package.extras] +azureservicebus = ["azure-servicebus (>=7.0.0)"] +azurestoragequeues = ["azure-storage-queue"] +consul = ["python-consul (>=0.6.0)"] +librabbitmq = ["librabbitmq (>=2.0.0)"] +mongodb = ["pymongo (>=3.3.0,<3.12.1)"] +msgpack = ["msgpack"] +pyro = ["pyro4"] +qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] +redis = ["redis (>=3.4.1,!=4.0.0,!=4.0.1)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +sqlalchemy = ["sqlalchemy"] +sqs = ["boto3 (>=1.9.12)", "pycurl (>=7.44.1,<7.45.0)", "urllib3 (>=1.26.7)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=1.3.1)"] + +[[package]] +name = "lazy-object-proxy" +version = "1.7.1" +description = "A fast and thorough lazy object proxy." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"}, {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"}, {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"}, @@ -2694,11 +1436,31 @@ lazy-object-proxy = [ {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"}, {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"}, ] -livereload = [ + +[[package]] +name = "livereload" +version = "2.6.3" +description = "Python LiveReload is an awesome tool for web developers" +category = "dev" +optional = false +python-versions = "*" +files = [ {file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"}, {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, ] -lxml = [ + +[package.dependencies] +six = "*" +tornado = {version = "*", markers = "python_version > \"2.7\""} + +[[package]] +name = "lxml" +version = "4.9.1" +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 = [ {file = "lxml-4.9.1-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:98cafc618614d72b02185ac583c6f7796202062c41d2eeecdf07820bad3295ed"}, {file = "lxml-4.9.1-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c62e8dd9754b7debda0c5ba59d34509c4688f853588d75b53c3791983faa96fc"}, {file = "lxml-4.9.1-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:21fb3d24ab430fc538a96e9fbb9b150029914805d551deeac7d7822f64631dfc"}, @@ -2770,11 +1532,41 @@ lxml = [ {file = "lxml-4.9.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:287605bede6bd36e930577c5925fcea17cb30453d96a7b4c63c14a257118dbb9"}, {file = "lxml-4.9.1.tar.gz", hash = "sha256:fe749b052bb7233fe5d072fcb549221a8cb1a16725c47c37e42b0b9cb3ff2c3f"}, ] -Mako = [ + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html5 = ["html5lib"] +htmlsoup = ["BeautifulSoup4"] +source = ["Cython (>=0.29.7)"] + +[[package]] +name = "Mako" +version = "1.2.3" +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 = [ {file = "Mako-1.2.3-py3-none-any.whl", hash = "sha256:c413a086e38cd885088d5e165305ee8eed04e8b3f8f62df343480da0a385735f"}, {file = "Mako-1.2.3.tar.gz", hash = "sha256:7fde96466fcfeedb0eed94f187f20b23d85e4cb41444be0e542e2c8c65c396cd"}, ] -MarkupSafe = [ + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "MarkupSafe" +version = "2.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, @@ -2816,23 +1608,86 @@ MarkupSafe = [ {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, ] -marshmallow = [ + +[[package]] +name = "marshmallow" +version = "3.18.0" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "marshmallow-3.18.0-py3-none-any.whl", hash = "sha256:35e02a3a06899c9119b785c12a22f4cda361745d66a71ab691fd7610202ae104"}, {file = "marshmallow-3.18.0.tar.gz", hash = "sha256:6804c16114f7fce1f5b4dadc31f4674af23317fcc7f075da21e35c1a35d781f7"}, ] -marshmallow-enum = [ + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"] +docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.1.1)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] +lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "marshmallow-enum" +version = "1.5.1" +description = "Enum field for Marshmallow" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "marshmallow-enum-1.5.1.tar.gz", hash = "sha256:38e697e11f45a8e64b4a1e664000897c659b60aa57bfa18d44e226a9920b6e58"}, {file = "marshmallow_enum-1.5.1-py2.py3-none-any.whl", hash = "sha256:57161ab3dbfde4f57adeb12090f39592e992b9c86d206d02f6bd03ebec60f072"}, ] -marshmallow-sqlalchemy = [ + +[package.dependencies] +marshmallow = ">=2.0.0" + +[[package]] +name = "marshmallow-sqlalchemy" +version = "0.28.1" +description = "SQLAlchemy integration with the marshmallow (de)serialization library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "marshmallow-sqlalchemy-0.28.1.tar.gz", hash = "sha256:aa376747296780a56355e3067b9c8bf43a2a1c44ff985de82b3a5d9e161ca2b8"}, {file = "marshmallow_sqlalchemy-0.28.1-py2.py3-none-any.whl", hash = "sha256:dbb061c19375eca3a7d18358d2ca8bbaee825fc3000a3f114e2698282362b536"}, ] -mccabe = [ + +[package.dependencies] +marshmallow = ">=3.0.0" +packaging = ">=21.3" +SQLAlchemy = ">=1.3.0" + +[package.extras] +dev = ["flake8 (==4.0.1)", "flake8-bugbear (==22.7.1)", "pre-commit (>=2.0,<3.0)", "pytest", "pytest-lazy-fixture (>=0.6.2)", "tox"] +docs = ["alabaster (==0.7.12)", "sphinx (==4.4.0)", "sphinx-issues (==3.0.1)"] +lint = ["flake8 (==4.0.1)", "flake8-bugbear (==22.7.1)", "pre-commit (>=2.0,<3.0)"] +tests = ["pytest", "pytest-lazy-fixture (>=0.6.2)"] + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] -mypy = [ + +[[package]] +name = "mypy" +version = "0.982" +description = "Optional static typing for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "mypy-0.982-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5085e6f442003fa915aeb0a46d4da58128da69325d8213b4b35cc7054090aed5"}, {file = "mypy-0.982-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:41fd1cf9bc0e1c19b9af13a6580ccb66c381a5ee2cf63ee5ebab747a4badeba3"}, {file = "mypy-0.982-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f793e3dd95e166b66d50e7b63e69e58e88643d80a3dcc3bcd81368e0478b089c"}, @@ -2858,11 +1713,37 @@ mypy = [ {file = "mypy-0.982-py3-none-any.whl", hash = "sha256:1021c241e8b6e1ca5a47e4d52601274ac078a89845cfde66c6d5f769819ffa1d"}, {file = "mypy-0.982.tar.gz", hash = "sha256:85f7a343542dc8b1ed0a888cdd34dca56462654ef23aa673907305b260b3d746"}, ] -mypy-extensions = [ + +[package.dependencies] +mypy-extensions = ">=0.4.3" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "main" +optional = false +python-versions = "*" +files = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] -mysql-connector-python = [ + +[[package]] +name = "mysql-connector-python" +version = "8.0.32" +description = "MySQL driver written in Python" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "mysql-connector-python-8.0.32.tar.gz", hash = "sha256:c2d20b29fd096a0633f9360c275bd2434d4bcf597281991c4b7f1c820cd07b84"}, {file = "mysql_connector_python-8.0.32-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:4df11c683924ef34c177a54887dc4844ae735b01c8a29ce6ab92d6d3db7a2757"}, {file = "mysql_connector_python-8.0.32-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:4b2d00c9e2cb9e3d11c57ec411226f43aa627607085fbed661cfea1c4dc57f61"}, @@ -2889,47 +1770,175 @@ mysql-connector-python = [ {file = "mysql_connector_python-8.0.32-cp39-cp39-win_amd64.whl", hash = "sha256:8c334c41cd1c5bcfa3550340253ef7d9d3b962211f33327c20f69706a0bcce06"}, {file = "mysql_connector_python-8.0.32-py2.py3-none-any.whl", hash = "sha256:e0299236297b63bf6cbb61d81a9d400bc01cad4743d1abe5296ef349de15ee53"}, ] -nodeenv = [ + +[package.dependencies] +protobuf = ">=3.11.0,<=3.20.3" + +[package.extras] +compression = ["lz4 (>=2.1.6,<=3.1.3)", "zstandard (>=0.12.0,<=0.19.0)"] +dns-srv = ["dnspython (>=1.16.0,<=2.1.0)"] +gssapi = ["gssapi (>=1.6.9,<=1.8.2)"] + +[[package]] +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 = [ {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, ] -packaging = [ + +[package.dependencies] +setuptools = "*" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, ] -pathspec = [ + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pathspec" +version = "0.10.1" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, ] -pbr = [ + +[[package]] +name = "pbr" +version = "5.10.0" +description = "Python Build Reasonableness" +category = "dev" +optional = false +python-versions = ">=2.6" +files = [ {file = "pbr-5.10.0-py2.py3-none-any.whl", hash = "sha256:da3e18aac0a3c003e9eea1a81bd23e5a3a75d745670dcf736317b7d966887fdf"}, {file = "pbr-5.10.0.tar.gz", hash = "sha256:cfcc4ff8e698256fc17ea3ff796478b050852585aa5bae79ecd05b2ab7b39b9a"}, ] -pep8-naming = [ + +[[package]] +name = "pep8-naming" +version = "0.13.2" +description = "Check PEP-8 naming conventions, plugin for flake8" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "pep8-naming-0.13.2.tar.gz", hash = "sha256:93eef62f525fd12a6f8c98f4dcc17fa70baae2f37fa1f73bec00e3e44392fa48"}, {file = "pep8_naming-0.13.2-py3-none-any.whl", hash = "sha256:59e29e55c478db69cffbe14ab24b5bd2cd615c0413edf790d47d3fb7ba9a4e23"}, ] -platformdirs = [ + +[package.dependencies] +flake8 = ">=3.9.1" + +[[package]] +name = "platformdirs" +version = "2.5.2" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, ] -pluggy = [ + +[package.extras] +docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] -pre-commit = [ + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "2.20.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, ] -pre-commit-hooks = [ + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +toml = "*" +virtualenv = ">=20.0.8" + +[[package]] +name = "pre-commit-hooks" +version = "4.3.0" +description = "Some out-of-the-box hooks for pre-commit." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "pre_commit_hooks-4.3.0-py2.py3-none-any.whl", hash = "sha256:9ccaf7c98794659d345080ee1ea0256a55ae059675045eebdbbc17c0be8c7e4b"}, {file = "pre_commit_hooks-4.3.0.tar.gz", hash = "sha256:fda598a4c834d030727e6a615722718b47510f4bed72df4c949f95ba9f5aaf88"}, ] -prompt-toolkit = [ + +[package.dependencies] +"ruamel.yaml" = ">=0.15" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "prompt-toolkit" +version = "3.0.31" +description = "Library for building powerful interactive command lines in Python" +category = "main" +optional = false +python-versions = ">=3.6.2" +files = [ {file = "prompt_toolkit-3.0.31-py3-none-any.whl", hash = "sha256:9696f386133df0fc8ca5af4895afe5d78f5fcfe5258111c2a79a1c3e41ffa96d"}, {file = "prompt_toolkit-3.0.31.tar.gz", hash = "sha256:9ada952c9d1787f52ff6d5f3484d0b4df8952787c087edf6a1f7c2cb1ea88148"}, ] -protobuf = [ + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "protobuf" +version = "3.20.3" +description = "Protocol Buffers" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"}, {file = "protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"}, {file = "protobuf-3.20.3-cp310-cp310-win32.whl", hash = "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c"}, @@ -2953,7 +1962,15 @@ protobuf = [ {file = "protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db"}, {file = "protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2"}, ] -psycopg2 = [ + +[[package]] +name = "psycopg2" +version = "2.9.4" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ {file = "psycopg2-2.9.4-cp310-cp310-win32.whl", hash = "sha256:8de6a9fc5f42fa52f559e65120dcd7502394692490c98fed1221acf0819d7797"}, {file = "psycopg2-2.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:1da77c061bdaab450581458932ae5e469cc6e36e0d62f988376e9f513f11cb5c"}, {file = "psycopg2-2.9.4-cp36-cp36m-win32.whl", hash = "sha256:a11946bad3557ca254f17357d5a4ed63bdca45163e7a7d2bfb8e695df069cc3a"}, @@ -2966,11 +1983,27 @@ psycopg2 = [ {file = "psycopg2-2.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:849bd868ae3369932127f0771c08d1109b254f08d48dc42493c3d1b87cb2d308"}, {file = "psycopg2-2.9.4.tar.gz", hash = "sha256:d529926254e093a1b669f692a3aa50069bc71faf5b0ecd91686a78f62767d52f"}, ] -pycodestyle = [ + +[[package]] +name = "pycodestyle" +version = "2.8.0" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, ] -pycryptodome = [ + +[[package]] +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 = [ {file = "pycryptodome-3.17-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:2c5631204ebcc7ae33d11c43037b2dafe25e2ab9c1de6448eb6502ac69c19a56"}, {file = "pycryptodome-3.17-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:04779cc588ad8f13c80a060b0b1c9d1c203d051d8a43879117fe6b8aaf1cd3fa"}, {file = "pycryptodome-3.17-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:f812d58c5af06d939b2baccdda614a3ffd80531a26e5faca2c9f8b1770b2b7af"}, @@ -3005,31 +2038,123 @@ pycryptodome = [ {file = "pycryptodome-3.17-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:74794a2e2896cd0cf56fdc9db61ef755fa812b4a4900fa46c49045663a92b8d0"}, {file = "pycryptodome-3.17.tar.gz", hash = "sha256:bce2e2d8e82fcf972005652371a3e8731956a0c1fbb719cc897943b3695ad91b"}, ] -pydocstyle = [ + +[[package]] +name = "pydocstyle" +version = "6.1.1" +description = "Python docstring style checker" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, ] -pyflakes = [ + +[package.dependencies] +snowballstemmer = "*" + +[package.extras] +toml = ["toml"] + +[[package]] +name = "pyflakes" +version = "2.4.0" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, ] -Pygments = [ + +[[package]] +name = "Pygments" +version = "2.13.0" +description = "Pygments is a syntax highlighting package written in Python." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, ] -pyjwt = [ + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pyjwt" +version = "2.6.0" +description = "JSON Web Token implementation in Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "PyJWT-2.6.0-py3-none-any.whl", hash = "sha256:d83c3d892a77bbb74d3e1a2cfa90afaadb60945205d1095d9221f04466f64c14"}, {file = "PyJWT-2.6.0.tar.gz", hash = "sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd"}, ] -pylint = [ + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] + +[[package]] +name = "pylint" +version = "2.15.10" +description = "python code static checker" +category = "main" +optional = false +python-versions = ">=3.7.2" +files = [ {file = "pylint-2.15.10-py3-none-any.whl", hash = "sha256:9df0d07e8948a1c3ffa3b6e2d7e6e63d9fb457c5da5b961ed63106594780cc7e"}, {file = "pylint-2.15.10.tar.gz", hash = "sha256:b3dc5ef7d33858f297ac0d06cc73862f01e4f2e74025ec3eff347ce0bc60baf5"}, ] -pyparsing = [ + +[package.dependencies] +astroid = ">=2.12.13,<=2.14.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, +] +isort = ">=4.2.5,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +tomlkit = ">=0.10.1" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +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 = [ {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, ] -pyrsistent = [ + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pyrsistent" +version = "0.18.1" +description = "Persistent/Functional/Immutable data structures" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "pyrsistent-0.18.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1"}, {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26"}, {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e"}, @@ -3052,39 +2177,156 @@ pyrsistent = [ {file = "pyrsistent-0.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07"}, {file = "pyrsistent-0.18.1.tar.gz", hash = "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96"}, ] -pytest = [ + +[[package]] +name = "pytest" +version = "7.2.0" +description = "pytest: simple powerful testing with Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, ] -pytest-flask = [ + +[package.dependencies] +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +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 = [ {file = "pytest-flask-1.2.0.tar.gz", hash = "sha256:46fde652f77777bf02dc91205aec4ce20cdf2acbbbd66a918ab91f5c14693d3d"}, {file = "pytest_flask-1.2.0-py3-none-any.whl", hash = "sha256:fe25b39ad0db09c3d1fe728edecf97ced85e774c775db259a6d25f0270a4e7c9"}, ] -pytest-flask-sqlalchemy = [ + +[package.dependencies] +Flask = "*" +pytest = ">=5.2" +Werkzeug = ">=0.7" + +[package.extras] +docs = ["Sphinx", "sphinx-rtd-theme"] + +[[package]] +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 = [ {file = "pytest-flask-sqlalchemy-1.1.0.tar.gz", hash = "sha256:db71a57b90435e5d854b21c37a2584056d6fc3ddb28c09d8d0a2546bd6e390ff"}, {file = "pytest_flask_sqlalchemy-1.1.0-py3-none-any.whl", hash = "sha256:b9f272d5c4092fcbe4a6284e402a37cad84f5b9be3c0bbe1a11927f24c99ff83"}, ] -pytest-mock = [ + +[package.dependencies] +Flask-SQLAlchemy = ">=2.3" +packaging = ">=14.1" +pytest = ">=3.2.1" +pytest-mock = ">=1.6.2" +SQLAlchemy = ">=1.2.2" + +[package.extras] +tests = ["psycopg2-binary", "pytest (>=6.0.1)", "pytest-postgresql (>=2.4.0,<4.0.0)"] + +[[package]] +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 = [ {file = "pytest-mock-3.10.0.tar.gz", hash = "sha256:fbbdb085ef7c252a326fd8cdcac0aa3b1333d8811f131bdcc701002e1be7ed4f"}, {file = "pytest_mock-3.10.0-py3-none-any.whl", hash = "sha256:f4c973eeae0282963eb293eb173ce91b091a79c1334455acfac9ddee8a1c784b"}, ] -python-dateutil = [ + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +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 = [ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, ] -pytz = [ + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2022.7.1" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"}, {file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"}, ] -pytz-deprecation-shim = [ + +[[package]] +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 = [ {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"}, {file = "pytz_deprecation_shim-0.1.0.post0.tar.gz", hash = "sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"}, ] -pyupgrade = [ + +[package.dependencies] +tzdata = {version = "*", markers = "python_version >= \"3.6\""} + +[[package]] +name = "pyupgrade" +version = "3.1.0" +description = "A tool to automatically upgrade syntax for newer versions." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "pyupgrade-3.1.0-py2.py3-none-any.whl", hash = "sha256:77c6101a710be3e24804891e43388cedbee617258e93b09c8c5e58de08617758"}, {file = "pyupgrade-3.1.0.tar.gz", hash = "sha256:7a8d393d85e15e0e2753e90b7b2e173b9d29dfd71e61f93d93e985b242627ed3"}, ] -PyYAML = [ + +[package.dependencies] +tokenize-rt = ">=3.2.0" + +[[package]] +name = "PyYAML" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, @@ -3126,7 +2368,15 @@ PyYAML = [ {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] -regex = [ + +[[package]] +name = "regex" +version = "2022.3.2" +description = "Alternative regular expression module, to replace re." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ {file = "regex-2022.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab69b4fe09e296261377d209068d52402fb85ef89dc78a9ac4a29a895f4e24a7"}, {file = "regex-2022.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5bc5f921be39ccb65fdda741e04b2555917a4bced24b4df14eddc7569be3b493"}, {file = "regex-2022.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43eba5c46208deedec833663201752e865feddc840433285fbadee07b84b464d"}, @@ -3202,26 +2452,101 @@ regex = [ {file = "regex-2022.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:9efa41d1527b366c88f265a227b20bcec65bda879962e3fc8a2aee11e81266d7"}, {file = "regex-2022.3.2.tar.gz", hash = "sha256:79e5af1ff258bc0fe0bdd6f69bc4ae33935a898e3cbefbbccf22e88a27fa053b"}, ] -reorder-python-imports = [ + +[[package]] +name = "reorder-python-imports" +version = "3.9.0" +description = "Tool for reordering python imports" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "reorder_python_imports-3.9.0-py2.py3-none-any.whl", hash = "sha256:3f9c16e8781f54c944756d0d1eb34a8c863554f7a4eb3693f574fe19b1a29b56"}, {file = "reorder_python_imports-3.9.0.tar.gz", hash = "sha256:49292ed537829a6bece9fb3746fc1bbe98f52643be5de01a4e13680268a5b0ec"}, ] -requests = [ + +[package.dependencies] +classify-imports = ">=4.1" + +[[package]] +name = "requests" +version = "2.28.1" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=3.7, <4" +files = [ {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, ] -restrictedpython = [ + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<3" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "restrictedpython" +version = "6.0" +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 = [ {file = "RestrictedPython-6.0-py3-none-any.whl", hash = "sha256:3479303f7bff48a7dedad76f96e7704993c5e86c5adbd67f607295d5352f0fb8"}, {file = "RestrictedPython-6.0.tar.gz", hash = "sha256:405cf0bd9eec2f19b1326b5f48228efe56d6590b4e91826b8cc3b2cd400a96ad"}, ] -restructuredtext-lint = [ + +[package.extras] +docs = ["Sphinx", "sphinx-rtd-theme"] +test = ["pytest", "pytest-mock"] + +[[package]] +name = "restructuredtext-lint" +version = "1.4.0" +description = "reStructuredText linter" +category = "dev" +optional = false +python-versions = "*" +files = [ {file = "restructuredtext_lint-1.4.0.tar.gz", hash = "sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45"}, ] -"ruamel.yaml" = [ + +[package.dependencies] +docutils = ">=0.11,<1.0" + +[[package]] +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 = [ {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, ] -ruamel-yaml-clib = [ + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} + +[package.extras] +docs = ["ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +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 = [ {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5859983f26d8cd7bb5c287ef452e8aacc86501487634573d260968f753e1d71"}, {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:debc87a9516b237d0466a711b18b6ebeb17ba9f391eb7f91c649c5c4ec5006c7"}, {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:df5828871e6648db72d1c19b4bd24819b80a755c4541d3409f0f7acd0f335c80"}, @@ -3257,19 +2582,97 @@ ruamel-yaml-clib = [ {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:184faeaec61dbaa3cace407cffc5819f7b977e75360e8d5ca19461cd851a5fc5"}, {file = "ruamel.yaml.clib-0.2.7.tar.gz", hash = "sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497"}, ] -safety = [ + +[[package]] +name = "safety" +version = "2.3.1" +description = "Checks installed dependencies for known vulnerabilities and licenses." +category = "dev" +optional = false +python-versions = "*" +files = [ {file = "safety-2.3.1-py3-none-any.whl", hash = "sha256:8f098d12b607db2756886280e85c28ece8db1bba4f45fc5f981f4663217bd619"}, {file = "safety-2.3.1.tar.gz", hash = "sha256:6e6fcb7d4e8321098cf289f59b65051cafd3467f089c6e57c9f894ae32c23b71"}, ] -sentry-sdk = [ + +[package.dependencies] +Click = ">=8.0.2" +dparse = ">=0.6.2" +packaging = ">=21.0" +requests = "*" +"ruamel.yaml" = ">=0.17.21" +setuptools = ">=19.3" + +[package.extras] +github = ["jinja2 (>=3.1.0)", "pygithub (>=1.43.3)"] +gitlab = ["python-gitlab (>=1.3.0)"] + +[[package]] +name = "sentry-sdk" +version = "1.16.0" +description = "Python client for Sentry (https://sentry.io)" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "sentry-sdk-1.16.0.tar.gz", hash = "sha256:a900845bd78c263d49695d48ce78a4bce1030bbd917e0b6cc021fc000c901113"}, {file = "sentry_sdk-1.16.0-py2.py3-none-any.whl", hash = "sha256:633edefead34d976ff22e7edc367cdf57768e24bc714615ccae746d9d91795ae"}, ] -setuptools = [ + +[package.dependencies] +certifi = "*" +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setuptools" +version = "65.5.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "setuptools-65.5.0-py3-none-any.whl", hash = "sha256:f62ea9da9ed6289bfe868cd6845968a2c854d1427f8548d52cae02a42b4f0356"}, {file = "setuptools-65.5.0.tar.gz", hash = "sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17"}, ] -simplejson = [ + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "simplejson" +version = "3.17.6" +description = "Simple, fast, extensible JSON encoder/decoder for Python" +category = "main" +optional = false +python-versions = ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ {file = "simplejson-3.17.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a89acae02b2975b1f8e4974cb8cdf9bf9f6c91162fb8dec50c259ce700f2770a"}, {file = "simplejson-3.17.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:82ff356ff91be0ab2293fc6d8d262451eb6ac4fd999244c4b5f863e049ba219c"}, {file = "simplejson-3.17.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:0de783e9c2b87bdd75b57efa2b6260c24b94605b5c9843517577d40ee0c3cc8a"}, @@ -3332,68 +2735,294 @@ simplejson = [ {file = "simplejson-3.17.6-cp39-cp39-win_amd64.whl", hash = "sha256:3fe87570168b2ae018391e2b43fbf66e8593a86feccb4b0500d134c998983ccc"}, {file = "simplejson-3.17.6.tar.gz", hash = "sha256:cf98038d2abf63a1ada5730e91e84c642ba6c225b0198c3684151b1f80c5f8a6"}, ] -six = [ + +[[package]] +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 = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -smmap = [ + +[[package]] +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 = [ {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, ] -snowballstemmer = [ + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +category = "main" +optional = false +python-versions = "*" +files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] -soupsieve = [ + +[[package]] +name = "soupsieve" +version = "2.3.2.post1" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"}, {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"}, ] -Sphinx = [ + +[[package]] +name = "Sphinx" +version = "5.3.0" +description = "Python documentation generator" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, ] -sphinx-autoapi = [ + +[package.dependencies] +alabaster = ">=0.7,<0.8" +babel = ">=2.9" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.14,<0.20" +imagesize = ">=1.3" +importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} +Jinja2 = ">=3.0" +packaging = ">=21.0" +Pygments = ">=2.12" +requests = ">=2.5.0" +snowballstemmer = ">=2.0" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.5" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "flake8-simplify", "isort", "mypy (>=0.981)", "sphinx-lint", "types-requests", "types-typed-ast"] +test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"] + +[[package]] +name = "sphinx-autoapi" +version = "2.0.0" +description = "Sphinx API documentation generator" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "sphinx-autoapi-2.0.0.tar.gz", hash = "sha256:97dcf1b5b54cd0d8efef867594e4a4f3e2d3a2c0ec1e5a891e0a61bc77046006"}, {file = "sphinx_autoapi-2.0.0-py2.py3-none-any.whl", hash = "sha256:dab2753a38cad907bf4e61473c0da365a26bfbe69fbf5aa6e4f7d48e1cf8a148"}, ] -sphinx-autobuild = [ + +[package.dependencies] +astroid = ">=2.7" +Jinja2 = "*" +PyYAML = "*" +sphinx = ">=4.0" +unidecode = "*" + +[package.extras] +docs = ["sphinx", "sphinx-rtd-theme"] +dotnet = ["sphinxcontrib-dotnetdomain"] +go = ["sphinxcontrib-golangdomain"] + +[[package]] +name = "sphinx-autobuild" +version = "2021.3.14" +description = "Rebuild Sphinx documentation on changes, with live-reload in the browser." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ {file = "sphinx-autobuild-2021.3.14.tar.gz", hash = "sha256:de1ca3b66e271d2b5b5140c35034c89e47f263f2cd5db302c9217065f7443f05"}, {file = "sphinx_autobuild-2021.3.14-py3-none-any.whl", hash = "sha256:8fe8cbfdb75db04475232f05187c776f46f6e9e04cacf1e49ce81bdac649ccac"}, ] -sphinx-basic-ng = [ + +[package.dependencies] +colorama = "*" +livereload = "*" +sphinx = "*" + +[package.extras] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "sphinx-basic-ng" +version = "1.0.0b1" +description = "A modern skeleton for Sphinx themes." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "sphinx_basic_ng-1.0.0b1-py3-none-any.whl", hash = "sha256:ade597a3029c7865b24ad0eda88318766bcc2f9f4cef60df7e28126fde94db2a"}, {file = "sphinx_basic_ng-1.0.0b1.tar.gz", hash = "sha256:89374bd3ccd9452a301786781e28c8718e99960f2d4f411845ea75fc7bb5a9b0"}, ] -sphinx-click = [ + +[package.dependencies] +sphinx = ">=4.0" + +[package.extras] +docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] + +[[package]] +name = "sphinx-click" +version = "4.3.0" +description = "Sphinx extension that automatically documents click applications" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ {file = "sphinx-click-4.3.0.tar.gz", hash = "sha256:bd4db5d3c1bec345f07af07b8e28a76cfc5006d997984e38ae246bbf8b9a3b38"}, {file = "sphinx_click-4.3.0-py3-none-any.whl", hash = "sha256:23e85a3cb0b728a421ea773699f6acadefae171d1a764a51dd8ec5981503ccbe"}, ] -sphinxcontrib-applehelp = [ + +[package.dependencies] +click = ">=7.0" +docutils = "*" +sphinx = ">=2.0" + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.2" +description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, ] -sphinxcontrib-devhelp = [ + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.2" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +category = "main" +optional = false +python-versions = ">=3.5" +files = [ {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, ] -sphinxcontrib-htmlhelp = [ + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.0" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, ] -sphinxcontrib-jsmath = [ + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, ] -sphinxcontrib-qthelp = [ + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.3" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +category = "main" +optional = false +python-versions = ">=3.5" +files = [ {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, ] -sphinxcontrib-serializinghtml = [ + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.5" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +category = "main" +optional = false +python-versions = ">=3.5" +files = [ {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, ] -SpiffWorkflow = [] -SQLAlchemy = [ + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "SpiffWorkflow" +version = "1.2.1" +description = "A workflow framework and BPMN/DMN Processor" +category = "main" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +celery = "*" +configparser = "*" +lxml = "*" + +[package.source] +type = "git" +url = "https://github.com/sartography/SpiffWorkflow" +reference = "main" +resolved_reference = "42b483054b3f1b86f7d3a9eb1a193345c578d86d" + +[[package]] +name = "SQLAlchemy" +version = "1.4.42" +description = "Database Abstraction Library" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ {file = "SQLAlchemy-1.4.42-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:28e881266a172a4d3c5929182fde6bb6fba22ac93f137d5380cc78a11a9dd124"}, {file = "SQLAlchemy-1.4.42-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ca9389a00f639383c93ed00333ed763812f80b5ae9e772ea32f627043f8c9c88"}, {file = "SQLAlchemy-1.4.42-cp27-cp27m-win32.whl", hash = "sha256:1d0c23ecf7b3bc81e29459c34a3f4c68ca538de01254e24718a7926810dc39a6"}, @@ -3436,32 +3065,137 @@ SQLAlchemy = [ {file = "SQLAlchemy-1.4.42-cp39-cp39-win_amd64.whl", hash = "sha256:5f966b64c852592469a7eb759615bbd351571340b8b344f1d3fa2478b5a4c934"}, {file = "SQLAlchemy-1.4.42.tar.gz", hash = "sha256:177e41914c476ed1e1b77fd05966ea88c094053e17a85303c4ce007f88eff363"}, ] -sqlalchemy-stubs = [] -stevedore = [ + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (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\")"} + +[package.extras] +aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing-extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"] +mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx-oracle (>=7)", "cx-oracle (>=7,<8)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +pymysql = ["pymysql", "pymysql (<1)"] +sqlcipher = ["sqlcipher3-binary"] + +[[package]] +name = "sqlalchemy-stubs" +version = "0.4" +description = "SQLAlchemy stubs and mypy plugin" +category = "main" +optional = false +python-versions = "*" +files = [] +develop = false + +[package.dependencies] +mypy = ">=0.790" +typing-extensions = ">=3.7.4" + +[package.source] +type = "git" +url = "https://github.com/burnettk/sqlalchemy-stubs.git" +reference = "scoped-session-delete" +resolved_reference = "d1176931684ce5b327539cc9567d4a1cd8ef1efd" + +[[package]] +name = "stevedore" +version = "4.0.1" +description = "Manage dynamic plugins for Python applications" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ {file = "stevedore-4.0.1-py3-none-any.whl", hash = "sha256:01645addb67beff04c7cfcbb0a6af8327d2efc3380b0f034aa316d4576c4d470"}, {file = "stevedore-4.0.1.tar.gz", hash = "sha256:9a23111a6e612270c591fd31ff3321c6b5f3d5f3dabb1427317a5ab608fc261a"}, ] -swagger-ui-bundle = [ + +[package.dependencies] +pbr = ">=2.0.0,<2.1.0 || >2.1.0" + +[[package]] +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 = [ {file = "swagger_ui_bundle-0.0.9-py3-none-any.whl", hash = "sha256:cea116ed81147c345001027325c1ddc9ca78c1ee7319935c3c75d3669279d575"}, {file = "swagger_ui_bundle-0.0.9.tar.gz", hash = "sha256:b462aa1460261796ab78fd4663961a7f6f347ce01760f1303bbbdf630f11f516"}, ] -tokenize-rt = [ + +[package.dependencies] +Jinja2 = ">=2.0" + +[[package]] +name = "tokenize-rt" +version = "4.2.1" +description = "A wrapper around the stdlib `tokenize` which roundtrips." +category = "dev" +optional = false +python-versions = ">=3.6.1" +files = [ {file = "tokenize_rt-4.2.1-py2.py3-none-any.whl", hash = "sha256:08a27fa032a81cf45e8858d0ac706004fcd523e8463415ddf1442be38e204ea8"}, {file = "tokenize_rt-4.2.1.tar.gz", hash = "sha256:0d4f69026fed520f8a1e0103aa36c406ef4661417f20ca643f913e33531b3b94"}, ] -toml = [ + +[[package]] +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 = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] -tomli = [ + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -tomlkit = [ + +[[package]] +name = "tomlkit" +version = "0.11.6" +description = "Style preserving TOML library" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ {file = "tomlkit-0.11.6-py3-none-any.whl", hash = "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b"}, {file = "tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"}, ] -tornado = [ + +[[package]] +name = "tornado" +version = "6.2" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +category = "dev" +optional = false +python-versions = ">= 3.7" +files = [ {file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"}, {file = "tornado-6.2-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9"}, {file = "tornado-6.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac"}, @@ -3474,87 +3208,298 @@ tornado = [ {file = "tornado-6.2-cp37-abi3-win_amd64.whl", hash = "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"}, {file = "tornado-6.2.tar.gz", hash = "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13"}, ] -typeguard = [ + +[[package]] +name = "typeguard" +version = "2.13.3" +description = "Run-time type checker for Python" +category = "dev" +optional = false +python-versions = ">=3.5.3" +files = [ {file = "typeguard-2.13.3-py3-none-any.whl", hash = "sha256:5e3e3be01e887e7eafae5af63d1f36c849aaa94e3a0112097312aabfa16284f1"}, {file = "typeguard-2.13.3.tar.gz", hash = "sha256:00edaa8da3a133674796cf5ea87d9f4b4c367d77476e185e80251cc13dfbb8c4"}, ] -types-click = [ + +[package.extras] +doc = ["sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["mypy", "pytest", "typing-extensions"] + +[[package]] +name = "types-click" +version = "7.1.8" +description = "Typing stubs for click" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "types-click-7.1.8.tar.gz", hash = "sha256:b6604968be6401dc516311ca50708a0a28baa7a0cb840efd7412f0dbbff4e092"}, {file = "types_click-7.1.8-py3-none-any.whl", hash = "sha256:8cb030a669e2e927461be9827375f83c16b8178c365852c060a34e24871e7e81"}, ] -types-dateparser = [ + +[[package]] +name = "types-dateparser" +version = "1.1.4.1" +description = "Typing stubs for dateparser" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "types-dateparser-1.1.4.1.tar.gz", hash = "sha256:0f76578bbae15c8b8701b5efd94db98a97ce0a27aedfe6f14a531170de6db97d"}, {file = "types_dateparser-1.1.4.1-py3-none-any.whl", hash = "sha256:dd7b2343bb06225c0e358533609b66a8edfb95e5426d8f658664e7d0f27dea68"}, ] -types-Flask = [ + +[[package]] +name = "types-Flask" +version = "1.1.6" +description = "Typing stubs for Flask" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "types-Flask-1.1.6.tar.gz", hash = "sha256:aac777b3abfff9436e6b01f6d08171cf23ea6e5be71cbf773aaabb1c5763e9cf"}, {file = "types_Flask-1.1.6-py3-none-any.whl", hash = "sha256:6ab8a9a5e258b76539d652f6341408867298550b19b81f0e41e916825fc39087"}, ] -types-Jinja2 = [ + +[package.dependencies] +types-click = "*" +types-Jinja2 = "*" +types-Werkzeug = "*" + +[[package]] +name = "types-Jinja2" +version = "2.11.9" +description = "Typing stubs for Jinja2" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "types-Jinja2-2.11.9.tar.gz", hash = "sha256:dbdc74a40aba7aed520b7e4d89e8f0fe4286518494208b35123bcf084d4b8c81"}, {file = "types_Jinja2-2.11.9-py3-none-any.whl", hash = "sha256:60a1e21e8296979db32f9374d8a239af4cb541ff66447bb915d8ad398f9c63b2"}, ] -types-MarkupSafe = [ + +[package.dependencies] +types-MarkupSafe = "*" + +[[package]] +name = "types-MarkupSafe" +version = "1.1.10" +description = "Typing stubs for MarkupSafe" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "types-MarkupSafe-1.1.10.tar.gz", hash = "sha256:85b3a872683d02aea3a5ac2a8ef590193c344092032f58457287fbf8e06711b1"}, {file = "types_MarkupSafe-1.1.10-py3-none-any.whl", hash = "sha256:ca2bee0f4faafc45250602567ef38d533e877d2ddca13003b319c551ff5b3cc5"}, ] -types-pytz = [ + +[[package]] +name = "types-pytz" +version = "2022.5.0.0" +description = "Typing stubs for pytz" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "types-pytz-2022.5.0.0.tar.gz", hash = "sha256:0c163b15d3e598e6cc7074a99ca9ec72b25dc1b446acc133b827667af0b7b09a"}, {file = "types_pytz-2022.5.0.0-py3-none-any.whl", hash = "sha256:a8e1fe6a1b270fbfaf2553b20ad0f1316707cc320e596da903bb17d7373fed2d"}, ] -types-PyYAML = [ + +[[package]] +name = "types-PyYAML" +version = "6.0.12" +description = "Typing stubs for PyYAML" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "types-PyYAML-6.0.12.tar.gz", hash = "sha256:f6f350418125872f3f0409d96a62a5a5ceb45231af5cc07ee0034ec48a3c82fa"}, {file = "types_PyYAML-6.0.12-py3-none-any.whl", hash = "sha256:29228db9f82df4f1b7febee06bbfb601677882e98a3da98132e31c6874163e15"}, ] -types-requests = [ + +[[package]] +name = "types-requests" +version = "2.28.11.2" +description = "Typing stubs for requests" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "types-requests-2.28.11.2.tar.gz", hash = "sha256:fdcd7bd148139fb8eef72cf4a41ac7273872cad9e6ada14b11ff5dfdeee60ed3"}, {file = "types_requests-2.28.11.2-py3-none-any.whl", hash = "sha256:14941f8023a80b16441b3b46caffcbfce5265fd14555844d6029697824b5a2ef"}, ] -types-urllib3 = [ + +[package.dependencies] +types-urllib3 = "<1.27" + +[[package]] +name = "types-urllib3" +version = "1.26.25.1" +description = "Typing stubs for urllib3" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "types-urllib3-1.26.25.1.tar.gz", hash = "sha256:a948584944b2412c9a74b9cf64f6c48caf8652cb88b38361316f6d15d8a184cd"}, {file = "types_urllib3-1.26.25.1-py3-none-any.whl", hash = "sha256:f6422596cc9ee5fdf68f9d547f541096a20c2dcfd587e37c804c9ea720bf5cb2"}, ] -types-Werkzeug = [ + +[[package]] +name = "types-Werkzeug" +version = "1.0.9" +description = "Typing stubs for Werkzeug" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "types-Werkzeug-1.0.9.tar.gz", hash = "sha256:5cc269604c400133d452a40cee6397655f878fc460e03fde291b9e3a5eaa518c"}, {file = "types_Werkzeug-1.0.9-py3-none-any.whl", hash = "sha256:194bd5715a13c598f05c63e8a739328657590943bce941e8a3619a6b5d4a54ec"}, ] -typing-extensions = [ + +[[package]] +name = "typing-extensions" +version = "4.4.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] -tzdata = [ + +[[package]] +name = "tzdata" +version = "2022.5" +description = "Provider of IANA time zone data" +category = "main" +optional = false +python-versions = ">=2" +files = [ {file = "tzdata-2022.5-py2.py3-none-any.whl", hash = "sha256:323161b22b7802fdc78f20ca5f6073639c64f1a7227c40cd3e19fd1d0ce6650a"}, {file = "tzdata-2022.5.tar.gz", hash = "sha256:e15b2b3005e2546108af42a0eb4ccab4d9e225e2dfbf4f77aad50c70a4b1f3ab"}, ] -tzlocal = [ + +[[package]] +name = "tzlocal" +version = "4.2" +description = "tzinfo object for the local timezone" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ {file = "tzlocal-4.2-py3-none-any.whl", hash = "sha256:89885494684c929d9191c57aa27502afc87a579be5cdd3225c77c463ea043745"}, {file = "tzlocal-4.2.tar.gz", hash = "sha256:ee5842fa3a795f023514ac2d801c4a81d1743bbe642e3940143326b3a00addd7"}, ] -Unidecode = [ + +[package.dependencies] +pytz-deprecation-shim = "*" +tzdata = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +devenv = ["black", "pyroma", "pytest-cov", "zest.releaser"] +test = ["pytest (>=4.3)", "pytest-mock (>=3.3)"] + +[[package]] +name = "Unidecode" +version = "1.3.6" +description = "ASCII transliterations of Unicode text" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ {file = "Unidecode-1.3.6-py3-none-any.whl", hash = "sha256:547d7c479e4f377b430dd91ac1275d593308dce0fc464fb2ab7d41f82ec653be"}, {file = "Unidecode-1.3.6.tar.gz", hash = "sha256:fed09cf0be8cf415b391642c2a5addfc72194407caee4f98719e40ec2a72b830"}, ] -urllib3 = [ + +[[package]] +name = "urllib3" +version = "1.26.12" +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.*, <4" +files = [ {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, ] -vine = [ + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "vine" +version = "5.0.0" +description = "Promises, promises, promises." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ {file = "vine-5.0.0-py2.py3-none-any.whl", hash = "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30"}, {file = "vine-5.0.0.tar.gz", hash = "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"}, ] -virtualenv = [ + +[[package]] +name = "virtualenv" +version = "20.16.5" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ {file = "virtualenv-20.16.5-py3-none-any.whl", hash = "sha256:d07dfc5df5e4e0dbc92862350ad87a36ed505b978f6c39609dc489eadd5b0d27"}, {file = "virtualenv-20.16.5.tar.gz", hash = "sha256:227ea1b9994fdc5ea31977ba3383ef296d7472ea85be9d6732e42a91c04e80da"}, ] -wcwidth = [ + +[package.dependencies] +distlib = ">=0.3.5,<1" +filelock = ">=3.4.1,<4" +platformdirs = ">=2.4,<3" + +[package.extras] +docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"] +testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "main" +optional = false +python-versions = "*" +files = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, ] -Werkzeug = [ + +[[package]] +name = "Werkzeug" +version = "2.2.2" +description = "The comprehensive WSGI web application library." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"}, {file = "Werkzeug-2.2.2.tar.gz", hash = "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f"}, ] -wrapt = [ + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog"] + +[[package]] +name = "wrapt" +version = "1.14.1" +description = "Module for decorators, wrappers and monkey patching." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, @@ -3620,15 +3565,70 @@ wrapt = [ {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, ] -WTForms = [ + +[[package]] +name = "WTForms" +version = "3.0.1" +description = "Form validation and rendering for Python web development." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "WTForms-3.0.1-py3-none-any.whl", hash = "sha256:837f2f0e0ca79481b92884962b914eba4e72b7a2daaf1f939c890ed0124b834b"}, {file = "WTForms-3.0.1.tar.gz", hash = "sha256:6b351bbb12dd58af57ffef05bc78425d08d1914e0fd68ee14143b7ade023c5bc"}, ] -xdoctest = [ + +[package.dependencies] +MarkupSafe = "*" + +[package.extras] +email = ["email-validator"] + +[[package]] +name = "xdoctest" +version = "1.1.0" +description = "A rewrite of the builtin doctest module" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ {file = "xdoctest-1.1.0-py3-none-any.whl", hash = "sha256:da330c4dacee51f3c785820bc743188fb6f7c64c5fa1c54bff8836b3cf23d69b"}, {file = "xdoctest-1.1.0.tar.gz", hash = "sha256:0fd4fad7932f0a2f082dfdfb857dd6ca41603757586c39b1e5b4d333fc389f8a"}, ] -zipp = [ + +[package.dependencies] +colorama = {version = "*", optional = true, markers = "platform_system == \"Windows\" and extra == \"colors\""} +Pygments = {version = "*", optional = true, markers = "python_version >= \"3.5.0\" and extra == \"colors\""} +six = "*" + +[package.extras] +all = ["IPython", "IPython", "Pygments", "Pygments", "attrs", "cmake", "codecov", "colorama", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert", "ninja", "pybind11", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest-cov", "pytest-cov", "pytest-cov", "pytest-cov", "scikit-build", "six", "typing"] +all-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "cmake (==3.21.2)", "codecov (==2.0.15)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==5.2.0)", "ipykernel (==6.0.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==6.1.5)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "ninja (==1.10.2)", "pybind11 (==2.7.1)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "scikit-build (==0.11.1)", "six (==1.11.0)", "typing (==3.7.4)"] +colors = ["Pygments", "Pygments", "colorama"] +jupyter = ["IPython", "IPython", "attrs", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert"] +optional = ["IPython", "IPython", "Pygments", "Pygments", "attrs", "colorama", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert", "pyflakes", "tomli"] +optional-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==5.2.0)", "ipykernel (==6.0.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==6.1.5)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "pyflakes (==2.2.0)", "tomli (==0.2.0)"] +runtime-strict = ["six (==1.11.0)"] +tests = ["cmake", "codecov", "ninja", "pybind11", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest-cov", "pytest-cov", "pytest-cov", "pytest-cov", "scikit-build", "typing"] +tests-strict = ["cmake (==3.21.2)", "codecov (==2.0.15)", "ninja (==1.10.2)", "pybind11 (==2.7.1)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "scikit-build (==0.11.1)", "typing (==3.7.4)"] + +[[package]] +name = "zipp" +version = "3.10.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ {file = "zipp-3.10.0-py3-none-any.whl", hash = "sha256:4fcb6f278987a6605757302a6e40e896257570d11c51628968ccb2a47e80c6c1"}, {file = "zipp-3.10.0.tar.gz", hash = "sha256:7a7262fd930bd3e36c50b9a64897aec3fafff3dfdeec9623ae22b40e93f99bb8"}, ] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.9,<3.12" +content-hash = "2fd5138221eabec441b601bb3769be478bed42099e72e20f7b8aaa1c1a888909" From 91ddb0cbdda6f394ec23c6ca40e63a9a23534e40 Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 10 Mar 2023 10:27:29 -0500 Subject: [PATCH 11/48] add keycloak users --- .../realm_exports/spiffworkflow-realm.json | 602 +++++++++++++++++- .../keycloak/test_user_lists/status | 24 + .../cypress/pilot/pp1.cy.js | 2 +- 3 files changed, 602 insertions(+), 26 deletions(-) diff --git a/spiffworkflow-backend/keycloak/realm_exports/spiffworkflow-realm.json b/spiffworkflow-backend/keycloak/realm_exports/spiffworkflow-realm.json index 008755eb..1c57944b 100644 --- a/spiffworkflow-backend/keycloak/realm_exports/spiffworkflow-realm.json +++ b/spiffworkflow-backend/keycloak/realm_exports/spiffworkflow-realm.json @@ -396,7 +396,7 @@ "otpPolicyLookAheadWindow" : 1, "otpPolicyPeriod" : 30, "otpPolicyCodeReusable" : false, - "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppGoogleName" ], + "otpSupportedApplications" : [ "totpAppGoogleName", "totpAppFreeOTPName" ], "webAuthnPolicyRpEntityName" : "keycloak", "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], "webAuthnPolicyRpId" : "", @@ -807,6 +807,190 @@ "realmRoles" : [ "default-roles-spiffworkflow" ], "notBefore" : 0, "groups" : [ ] + }, { + "id" : "3730e6ec-4b0c-4fbe-a34b-2cd43d8c9854", + "createdTimestamp" : 1678461819329, + "username" : "core10.contributor", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "core10.contributor@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "225" ] + }, + "credentials" : [ { + "id" : "223cbe3b-d432-4707-b826-6220caa14bd7", + "type" : "password", + "createdDate" : 1678461819366, + "secretData" : "{\"value\":\"Mp81SeHhDQa2U/i/S2CfPnKvjwRDJCKZMgCQX3BkZWE/a6791XjXmwB8DE5qS8tiST68BQoQRuc1VCiNKL3zaQ==\",\"salt\":\"Jb0BB2tIQ+HUJQIFr82g9w==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "88e7ca9e-1825-4d4a-9f60-29368023c67b", + "createdTimestamp" : 1678461819411, + "username" : "core11.contributor", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "core11.contributor@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "226" ] + }, + "credentials" : [ { + "id" : "46dc7656-b70b-4d86-80fc-aa08d807be2b", + "type" : "password", + "createdDate" : 1678461819447, + "secretData" : "{\"value\":\"hgBEI05fhPMVx47O9KmnrTvPomKJXK0IjEHZ30zM3fu6maT2fOHGh4+ti6MVhKqQeXKZR4wtC3i1RoqLNOsjpQ==\",\"salt\":\"BWxZnmTfzggGqzVKkFY+vQ==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "6504eeda-be24-488b-ace4-1d50a7a354bc", + "createdTimestamp" : 1678461819494, + "username" : "core12.contributor", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "core12.contributor@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "227" ] + }, + "credentials" : [ { + "id" : "bde05120-10b5-4796-b559-9238847d2604", + "type" : "password", + "createdDate" : 1678461819527, + "secretData" : "{\"value\":\"njdHu9w1jeSvaNbdwVf0X+3TZaHmZVwUc+/TOAtv05eNGBIW9Vt1+500AsLReHS8lb/I3fglr5I9ZskYHUc0fA==\",\"salt\":\"lH6xJHf1jQGX1j4bYH6GXA==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "ed249cd3-c66e-46e0-9184-1e6468b57afa", + "createdTimestamp" : 1678461819557, + "username" : "core13.contributor", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "core13.contributor@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "228" ] + }, + "credentials" : [ { + "id" : "81b65ee8-6fcd-4cd6-8886-aa44feefa55f", + "type" : "password", + "createdDate" : 1678461819592, + "secretData" : "{\"value\":\"ywBsPI0pdoCOjNWinYNZQBBzL3NRp2u2jv3aXBGxneTo9v8XaVweGL52HIyTikdfmX46TEMIH6LQopaYFcwhng==\",\"salt\":\"GTw17rcE4UvB/Dx4UUkAog==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "1b7b3aa4-b0fe-46c7-a9a1-3fb3c99c7576", + "createdTimestamp" : 1678461819624, + "username" : "core14.contributor", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "core14.contributor@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "229" ] + }, + "credentials" : [ { + "id" : "0c24ffe5-cb97-4b0d-a0d1-920de540742e", + "type" : "password", + "createdDate" : 1678461819658, + "secretData" : "{\"value\":\"3RXjoEUpqxH6RM0sZUf393H9nzyVADId8IWNru9fWgdQg6tHaZezRBZ/lRRERvvdmLiupQ3cMsL/HHvPRQA6tA==\",\"salt\":\"zkaBJY+Dvg5Az74MACBBUg==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "8e2b39a8-a744-4345-928f-da1a36f15f46", + "createdTimestamp" : 1678461819686, + "username" : "core15.contributor", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "core15.contributor@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "230" ] + }, + "credentials" : [ { + "id" : "14a91e80-cec9-44cf-aa85-28e0043f660d", + "type" : "password", + "createdDate" : 1678461819720, + "secretData" : "{\"value\":\"JnP9MpLDM92LuzJnEVUy0vzm9LoSttezepYu4ANfJlmcS6cUvnnh1yDKm43I2YzM4+mXRdxJyoLZTk/ZpmshSQ==\",\"salt\":\"5CKz6mrqr4IaUeEuu/hR9Q==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "ffe3e131-9479-49d2-8125-83dc86a16478", + "createdTimestamp" : 1678461819751, + "username" : "core16.contributor", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "core16.contributor@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "231" ] + }, + "credentials" : [ { + "id" : "cf010c6c-035e-4a2f-ab74-5617fd23c808", + "type" : "password", + "createdDate" : 1678461819786, + "secretData" : "{\"value\":\"WeZ+YxLVtjRhlLZnb6j3AfecmQEsvTm3iM8ZqQthgq9c4BuZ23qare3PEVlRCA1+Oj5sAOOS1hs9iab6ia49wQ==\",\"salt\":\"uai22Okju4dg7GfO7p3C1Q==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "94bcef08-2af1-4805-864d-cbabcd851d67", + "createdTimestamp" : 1678461819815, + "username" : "core17.contributor", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "core17.contributor@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "232" ] + }, + "credentials" : [ { + "id" : "c7a58ff0-7c56-464b-9009-b6e845075087", + "type" : "password", + "createdDate" : 1678461819850, + "secretData" : "{\"value\":\"R53+DKM2eyUXDYJDjW9BtwdY+x0/CUhgUDDYjip7BvGAepzRqPvZVbCLqJjFf6YctO4Va7F65n4evd40GbO7fQ==\",\"salt\":\"U/ia7H+I4yeD3bpP1vnH6Q==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] }, { "id" : "3b81b45e-759b-4d7a-aa90-adf7b447208c", "createdTimestamp" : 1676302140358, @@ -922,6 +1106,75 @@ "realmRoles" : [ "default-roles-spiffworkflow" ], "notBefore" : 0, "groups" : [ ] + }, { + "id" : "89d57569-1a90-412a-ba01-aa8ff19ed171", + "createdTimestamp" : 1678461819085, + "username" : "core7.contributor", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "core7.contributor@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "222" ] + }, + "credentials" : [ { + "id" : "cfeb64ec-a38a-4f95-b0cd-28b5501524d8", + "type" : "password", + "createdDate" : 1678461819121, + "secretData" : "{\"value\":\"w4WKqWXTlin6MPQi0mO+Bvktb2zuMdIylqNNxYgBCnd5vwzq2widp7G9f3wz8Iy0wY8K2rqBjdSmmbZ7fJ8//Q==\",\"salt\":\"SRuRkx3572cDGoWhqAQGLQ==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "81efd609-b6ae-42ec-800e-d6fcca2f8282", + "createdTimestamp" : 1678461819150, + "username" : "core8.contributor", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "core8.contributor@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "223" ] + }, + "credentials" : [ { + "id" : "0b476f6f-7aa4-4f75-bf5c-ac47521f3900", + "type" : "password", + "createdDate" : 1678461819185, + "secretData" : "{\"value\":\"ALWI40OEZUhMJ1CQTV9wSrwQUWfYNiYbN2JTmCUfbLUcUbY+rTrKOfAn9Mc/bCEFJomiTb9u/eqnkKX/lCGgew==\",\"salt\":\"wW2T8PkpCnnPfMNwpPVUVQ==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "a1233c9f-e59a-48dc-aaa7-1513f1aa5654", + "createdTimestamp" : 1678461819225, + "username" : "core9.contributor", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "core9.contributor@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "224" ] + }, + "credentials" : [ { + "id" : "907b9d46-b8a3-4a14-ab89-b07d2c4d431a", + "type" : "password", + "createdDate" : 1678461819266, + "secretData" : "{\"value\":\"v9aFLHzLyiwWuAxNeVtRjtXzRtug6KU2f19SbS8dBdPC0mlHORoLYXy6VoAMdcTv8bfrW6e9iCgqWnXdXU6yMg==\",\"salt\":\"giVxblJWbFNNPiZZKxWYxg==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] }, { "id" : "7b9767ac-24dc-43b0-838f-29e16b4fd14e", "createdTimestamp" : 1675718483773, @@ -1522,6 +1775,29 @@ "realmRoles" : [ "default-roles-spiffworkflow" ], "notBefore" : 0, "groups" : [ ] + }, { + "id" : "9a4d176c-e61e-4392-8c50-a04988606aa6", + "createdTimestamp" : 1678461818383, + "username" : "infra6.sme", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "infra6.sme@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "212" ] + }, + "credentials" : [ { + "id" : "c381e58c-3e06-4e10-bd23-46f258c1c91f", + "type" : "password", + "createdDate" : 1678461818420, + "secretData" : "{\"value\":\"m17+awcU3Ezhfi/gBK0xyxvnGKHads95lhn7uxvEXaPCJF0ioN8C27tH1RwU1w9ptdWjWKWAM9dcimIegy7M7g==\",\"salt\":\"0kCljoos7qzCnVdv+3IMjQ==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] }, { "id" : "b8d0d90e-9a7e-446c-9984-082cb315af8f", "createdTimestamp" : 1675718484095, @@ -1869,6 +2145,75 @@ "realmRoles" : [ "default-roles-spiffworkflow" ], "notBefore" : 0, "groups" : [ ] + }, { + "id" : "a368625b-b905-4e0d-83f6-dfe707b6320a", + "createdTimestamp" : 1678461818455, + "username" : "legal6.sme", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "legal6.sme@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "213" ] + }, + "credentials" : [ { + "id" : "53a21d32-1da5-45f1-a7d9-e45304b213d1", + "type" : "password", + "createdDate" : 1678461818490, + "secretData" : "{\"value\":\"9zEoc1uV0QXsMvAS8lA1xdh4bOqcPdSAItg7zBFr5i+In/xOBtpRM0277nMgDNLtar4s+HRhytWgJ7OidVmjsw==\",\"salt\":\"ahEvQYvH0bHbT/uHz1I9QA==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "e02e085f-eb50-4fe3-844c-24e41479ab47", + "createdTimestamp" : 1678461818523, + "username" : "legal7.sme", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "legal7.sme@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "214" ] + }, + "credentials" : [ { + "id" : "f5377236-8b0b-4be4-8dab-afb2c4a6470f", + "type" : "password", + "createdDate" : 1678461818557, + "secretData" : "{\"value\":\"dyQhBsrNeYHkbJudEjiay3duLFO9B66l0d+2L26S+/HMGuKfuI4NT+gju1MfQPVJhyC01FH7EmDGGS8I45i2jw==\",\"salt\":\"kU4NM5QOWvGSX+kVyvwSoA==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "4de624bd-485f-49d5-817c-ba66c31be7a9", + "createdTimestamp" : 1678461818589, + "username" : "legal8.sme", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "legal8.sme@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "215" ] + }, + "credentials" : [ { + "id" : "5d71a02b-2f4b-484d-9125-a4454a17a800", + "type" : "password", + "createdDate" : 1678461818632, + "secretData" : "{\"value\":\"UH+hrjz9F+X0vQlbgzaFiZBA5uol9Lnjs1/5VpBnbWuISF6MAlxj2fmbnZbw4ILVSllaQvVSFaD4YUxbnRhUmw==\",\"salt\":\"MuAF2Rl7IOxOgZ7Xbqs3RQ==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] }, { "id" : "8a03f00f-310d-4bae-b918-f6f128f98095", "createdTimestamp" : 1677187934419, @@ -1958,6 +2303,29 @@ "realmRoles" : [ "default-roles-spiffworkflow" ], "notBefore" : 0, "groups" : [ ] + }, { + "id" : "97843876-e1b6-469a-bab4-f9bce4aa5936", + "createdTimestamp" : 1678461819014, + "username" : "mobile.project-lead", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "mobile.project-lead@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "221" ] + }, + "credentials" : [ { + "id" : "96c00769-4348-4ad3-82c5-f34124602c17", + "type" : "password", + "createdDate" : 1678461819049, + "secretData" : "{\"value\":\"E7nVydRqQ+TZs54VmJcT4AjjtT1la7PmQbOnylqTPkkcOdLRmZbNTw/K429lOhqUHX7y1prC3OjGdY1VI8bjsg==\",\"salt\":\"D61yv2zS3Bi8epVKjRpWQw==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] }, { "id" : "9d23748e-23a7-4c48-956c-64da75871277", "createdTimestamp" : 1675718484779, @@ -2001,6 +2369,29 @@ "realmRoles" : [ "default-roles-spiffworkflow" ], "notBefore" : 0, "groups" : [ ] + }, { + "id" : "e8e67210-5088-46bc-97db-09dbcaf9de97", + "createdTimestamp" : 1678461818939, + "username" : "nomos.project-lead", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "nomos.project-lead@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "220" ] + }, + "credentials" : [ { + "id" : "8139f9b8-bad9-41d2-b3c6-589a2c11bf45", + "type" : "password", + "createdDate" : 1678461818975, + "secretData" : "{\"value\":\"6g5XIaFghMzx8CFYO6VJLGpUqBRiAEwFklZSI+uzJ5vrMsDvrcGjDuWtY+lmRO4lKqy30lBvqhMFvPT6pCxF3g==\",\"salt\":\"dT+XvwD+hxUwRAJCZFFYiA==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] }, { "id" : "df72b3d2-07fd-4cb0-a447-a1c433db49d5", "createdTimestamp" : 1676302143785, @@ -2185,6 +2576,98 @@ "realmRoles" : [ "default-roles-spiffworkflow" ], "notBefore" : 0, "groups" : [ ] + }, { + "id" : "07f7a010-7542-4c2f-adf8-04b39433181d", + "createdTimestamp" : 1678461818663, + "username" : "peopleops.partner6.sme", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "peopleops.partner6.sme@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "216" ] + }, + "credentials" : [ { + "id" : "867e9236-3a15-4198-b085-d36a7fa859e9", + "type" : "password", + "createdDate" : 1678461818713, + "secretData" : "{\"value\":\"kmQkAD459XkLCGaWWTr1rrwZYQ2gQ4k2xTroJZAyHmWvBBnKg+a74cRaW2Y3dnzcGTlcprtuMvwYVfq7HIOkmg==\",\"salt\":\"uKORqhpJJnceOf/q56BiSA==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "5d41b5b7-bc3c-42fe-b20b-56a7c6cd3801", + "createdTimestamp" : 1678461818743, + "username" : "peopleops.partner7.sme", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "peopleops.partner7.sme@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "217" ] + }, + "credentials" : [ { + "id" : "745d419f-c6de-4504-9c8e-c3f7b1ac747e", + "type" : "password", + "createdDate" : 1678461818778, + "secretData" : "{\"value\":\"myjshlqPW/3DpwC5X4vsAaqcsisdKwqr+CQXP18mt3AQMzqipHJaVAEAJzkZS4j42VB/XAvh0olMxb8Vapyw3g==\",\"salt\":\"jNpX6DyT5Tt/5dPXYiQfpQ==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "73523c93-6104-4494-b1c8-2af6087bcdd9", + "createdTimestamp" : 1678461818810, + "username" : "peopleops.partner8.sme", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "peopleops.partner8.sme@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "218" ] + }, + "credentials" : [ { + "id" : "e839763b-aba2-4b4f-b715-b2c061b7430f", + "type" : "password", + "createdDate" : 1678461818843, + "secretData" : "{\"value\":\"M0KfNRU/4qt1WL/cGiSm6sKfN9PTK+6JiV96Y55Zg5CYaXH0ihTyGo62wS4T4YuyMm6/yTKz7+w3gdU4Zg/3Uw==\",\"salt\":\"sd/JEXtWTW4PetXzEBCNQA==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "cdff7ae3-72eb-45b6-9424-6f56df9c3b1c", + "createdTimestamp" : 1678461818873, + "username" : "peopleops.partner9.sme", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "peopleops.partner9.sme@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "219" ] + }, + "credentials" : [ { + "id" : "5ff8e042-a72e-4b46-9efa-e1910cd09d13", + "type" : "password", + "createdDate" : 1678461818908, + "secretData" : "{\"value\":\"q/hdvLKerMbnpe6yjC3VxDqCFi0ne7rD5A1K39EM+XgD6bFI62qKW5JIBB5BaGz/GrWYw7ipwMBaOvLBOubSkg==\",\"salt\":\"vfnCbi47kaYpILxbL0b3Tg==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] }, { "id" : "dbf941e7-0b45-4bc6-ae9e-d7153d32ce47", "createdTimestamp" : 1676302143401, @@ -2691,6 +3174,29 @@ "realmRoles" : [ "default-roles-spiffworkflow" ], "notBefore" : 0, "groups" : [ ] + }, { + "id" : "c684e919-6ae0-4031-a160-8e90338567b3", + "createdTimestamp" : 1678461818310, + "username" : "security6.sme", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "security6.sme@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "211" ] + }, + "credentials" : [ { + "id" : "aff2f083-f6aa-4f93-899f-aaa3119a9739", + "type" : "password", + "createdDate" : 1678461818346, + "secretData" : "{\"value\":\"7XGMuiylxKmwDwJZtiPNLllERwN8KLoILLE/BjjXOkqN3c+C+KYgNxPhrDt8dG9PDYOq/59vh/4E2y82GLaoEw==\",\"salt\":\"ufzmAcoMLoi0jtRHwGDadg==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] }, { "id" : "b768e3ef-f905-4493-976c-bc3408c04bec", "createdTimestamp" : 1675447832524, @@ -2794,6 +3300,52 @@ "realmRoles" : [ "default-roles-spiffworkflow" ], "notBefore" : 0, "groups" : [ ] + }, { + "id" : "8205682c-71fd-43e6-9e90-bac3d28f850c", + "createdTimestamp" : 1678461818109, + "username" : "vac.program-lead", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "vac.program-lead@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "163" ] + }, + "credentials" : [ { + "id" : "ea75f124-84aa-4058-819a-1175ffe1451b", + "type" : "password", + "createdDate" : 1678461818170, + "secretData" : "{\"value\":\"7Nuw33yzMfwWmMrPC1ytP3L5Y2HMWXANyKtP1+kjJ1HJeDSGVzYLvoKq3rIIYLRd0MQ/NFwJUyz08GAyqFfDMw==\",\"salt\":\"NSJ7cCO2SsQf/oLWJokRDg==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "cb99a5c4-2c28-4b19-b8c7-635b757fc817", + "createdTimestamp" : 1678461818231, + "username" : "waku.research.project-lead", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "email" : "waku.research.project-lead@status.im", + "attributes" : { + "spiffworkflow-employeeid" : [ "164" ] + }, + "credentials" : [ { + "id" : "ed5fc4a1-d574-4940-b5e4-3a1ad9d122ba", + "type" : "password", + "createdDate" : 1678461818268, + "secretData" : "{\"value\":\"K7MRRw2gO4bXHJH8U4cZU2rcVQT/hxw7kMHqN1uDae9FVqFEKh014qiwePOHr5K1xjUw8uU5e/d3HCcwhuRUQw==\",\"salt\":\"R4FdsDK6NvelgQ8gH7Me0g==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-spiffworkflow" ], + "notBefore" : 0, + "groups" : [ ] } ], "scopeMappings" : [ { "clientScope" : "offline_access", @@ -4026,7 +4578,7 @@ "subType" : "authenticated", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "oidc-full-name-mapper", "oidc-usermodel-property-mapper", "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "saml-role-list-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-address-mapper", "saml-user-property-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-role-list-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "oidc-address-mapper" ] } }, { "id" : "d68e938d-dde6-47d9-bdc8-8e8523eb08cd", @@ -4044,7 +4596,7 @@ "subType" : "anonymous", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "oidc-address-mapper", "saml-user-property-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "saml-user-attribute-mapper" ] + "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "oidc-address-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-role-list-mapper", "saml-user-property-mapper" ] } }, { "id" : "3854361d-3fe5-47fb-9417-a99592e3dc5c", @@ -4134,7 +4686,7 @@ "internationalizationEnabled" : false, "supportedLocales" : [ ], "authenticationFlows" : [ { - "id" : "84c5d297-0fe5-423d-a563-506a227fd48e", + "id" : "04b09640-f53c-4c1b-b2b1-8cac25afc2bb", "alias" : "Account verification options", "description" : "Method with which to verity the existing account", "providerId" : "basic-flow", @@ -4156,7 +4708,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "f816c0fb-937d-4874-823c-982e8a5b895d", + "id" : "e7c246f4-71c3-4a48-9037-72438bdcfcbb", "alias" : "Authentication Options", "description" : "Authentication options.", "providerId" : "basic-flow", @@ -4185,7 +4737,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "f9ae89b4-a726-4672-bcde-5c7d5bcae312", + "id" : "6e9d415e-98f7-4459-b10b-45b08302c681", "alias" : "Browser - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -4207,7 +4759,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "f56fc357-0772-4bd3-9c15-7a0265c13ae2", + "id" : "c86b0fad-f7dd-4c58-974e-25eb83c1dacf", "alias" : "Direct Grant - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -4229,7 +4781,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "e34eb5aa-0f88-4f92-8f1e-d64919a7313e", + "id" : "cb7f4c87-a8fa-445a-a8d4-53869cdfed12", "alias" : "First broker login - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -4251,7 +4803,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "5c3ff777-1fd3-4e13-a500-87fd064d4f56", + "id" : "8fa87954-bc65-4f1e-bc55-f5bb49f59fbb", "alias" : "Handle Existing Account", "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", "providerId" : "basic-flow", @@ -4273,7 +4825,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "a1684709-c4f0-493f-8eef-08cc4331b068", + "id" : "e617d826-c654-4c35-96ad-8381bd1e2298", "alias" : "Reset - Conditional OTP", "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", "providerId" : "basic-flow", @@ -4295,7 +4847,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "ddb4247d-e70f-4b3e-8c58-2127539df878", + "id" : "2e4a46ae-2813-4b71-9386-c08b2f063fa6", "alias" : "User creation or linking", "description" : "Flow for the existing/non-existing user alternatives", "providerId" : "basic-flow", @@ -4318,7 +4870,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "c1feb17c-2704-4854-abde-f2d97e1441eb", + "id" : "8fa69de0-13cf-4252-899b-c59a30ebd132", "alias" : "Verify Existing Account by Re-authentication", "description" : "Reauthentication of existing account", "providerId" : "basic-flow", @@ -4340,7 +4892,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "5c7e4833-d2ce-4973-9da5-370c77efda7d", + "id" : "204d20f6-d9a7-49ff-a7a3-45386fb884f4", "alias" : "browser", "description" : "browser based authentication", "providerId" : "basic-flow", @@ -4376,7 +4928,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "5b0ab6fc-da4c-4b1f-8036-3c6848a7b648", + "id" : "3c0c2987-65db-4920-ae44-34aba220c3fb", "alias" : "clients", "description" : "Base authentication for clients", "providerId" : "client-flow", @@ -4412,7 +4964,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "1c2c738c-2f40-4dcd-b82d-17b8d32b34dc", + "id" : "68a92113-be75-4e63-a322-8076d6c67650", "alias" : "direct grant", "description" : "OpenID Connect Resource Owner Grant", "providerId" : "basic-flow", @@ -4441,7 +4993,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "24544f4c-2e68-4f0e-9d7d-791e63b2c705", + "id" : "a630d78f-4fe1-4350-a19d-d091d1af514d", "alias" : "docker auth", "description" : "Used by Docker clients to authenticate against the IDP", "providerId" : "basic-flow", @@ -4456,7 +5008,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "6104dbf5-a433-454b-abba-c46665c0cfe3", + "id" : "f73b4437-8e82-4788-be69-e437b09b500c", "alias" : "first broker login", "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", "providerId" : "basic-flow", @@ -4479,7 +5031,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "ef60185e-3c7b-425e-b813-91b1a59ccddb", + "id" : "b7c8cc6d-bc1f-446e-b263-72214b2f5c56", "alias" : "forms", "description" : "Username, password, otp and other auth forms.", "providerId" : "basic-flow", @@ -4501,7 +5053,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "f5292250-2f32-4021-a4ed-fb8316c22331", + "id" : "a3bdf79f-8c7d-4bff-807d-76fa61093446", "alias" : "http challenge", "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes", "providerId" : "basic-flow", @@ -4523,7 +5075,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "60919bb8-7857-4217-9a3e-7f2c44396dd7", + "id" : "ada41b4e-5a12-496d-aa1e-d31cf8c08226", "alias" : "registration", "description" : "registration flow", "providerId" : "basic-flow", @@ -4539,7 +5091,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "e16f693e-70e8-446f-bab0-c5ee7ce14506", + "id" : "1c858bcd-2031-4056-bbf0-1fbaecdd7068", "alias" : "registration form", "description" : "registration form", "providerId" : "form-flow", @@ -4575,7 +5127,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "61441726-52c9-4720-b801-0fc4e7a89912", + "id" : "ff91e251-d85e-450b-bff7-d45be26777d5", "alias" : "reset credentials", "description" : "Reset credentials for a user if they forgot their password or something", "providerId" : "basic-flow", @@ -4611,7 +5163,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "87a6ced7-419e-419c-ba66-3aaad4a4970a", + "id" : "7b0680a2-99b9-454c-b145-f286e9d60c58", "alias" : "saml ecp", "description" : "SAML ECP Profile Authentication Flow", "providerId" : "basic-flow", @@ -4627,13 +5179,13 @@ } ] } ], "authenticatorConfig" : [ { - "id" : "0e0bacbb-9901-4f91-9eec-ef3b8816919b", + "id" : "aa1e4f55-3e7f-445a-a432-7a972776d719", "alias" : "create unique user config", "config" : { "require.password.update.after.registration" : "false" } }, { - "id" : "c4fecfc8-120e-44e7-8576-c2aafd9d433f", + "id" : "fd69765e-309b-4c5d-bdd5-51343427cd27", "alias" : "review profile config", "config" : { "update.profile.on.first.login" : "missing" diff --git a/spiffworkflow-backend/keycloak/test_user_lists/status b/spiffworkflow-backend/keycloak/test_user_lists/status index 23b865a9..151cfdf7 100644 --- a/spiffworkflow-backend/keycloak/test_user_lists/status +++ b/spiffworkflow-backend/keycloak/test_user_lists/status @@ -13,11 +13,22 @@ codex5.sme@status.im,190 core-a1.contributor@status.im,202 core-a2.contributor@status.im,203 core1.contributor@status.im,155 +core10.contributor@status.im,225 +core11.contributor@status.im,226 +core12.contributor@status.im,227 +core13.contributor@status.im,228 +core14.contributor@status.im,229 +core15.contributor@status.im,230 +core16.contributor@status.im,231 +core17.contributor@status.im,232 core2.contributor@status.im,156 core3.contributor@status.im,157 core4.contributor@status.im,158 core5.contributor@status.im,159 core6.contributor@status.im,199 +core7.contributor@status.im,222 +core8.contributor@status.im,223 +core9.contributor@status.im,224 core@status.im,113 dao.project.lead@status.im desktop-a1.sme@status.im,210 @@ -43,6 +54,7 @@ infra2.sme@status.im,132 infra3.sme@status.im,167 infra4.sme@status.im,175 infra5.sme@status.im,176 +infra6.sme@status.im,212 jakub@status.im jarrad@status.im lead@status.im,114 @@ -54,9 +66,14 @@ legal2.sme@status.im,165 legal3.sme@status.im,166 legal4.sme@status.im,177 legal5.sme@status.im,178 +legal6.sme@status.im,213 +legal7.sme@status.im,214 +legal8.sme@status.im,215 logos.program-lead@status.im,160 manuchehr@status.im,110 +mobile.project-lead@status.im,221 nimbus.program-lead@status.im,161 +nomos.project-lead@status.im,220 peopleops.partner-a1.sme@status.im,208 peopleops.partner.sme@status.im,148 peopleops.partner1.sme@status.im,149 @@ -64,6 +81,10 @@ peopleops.partner2.sme@status.im,173 peopleops.partner3.sme@status.im,174 peopleops.partner4.sme@status.im,181 peopleops.partner5.sme@status.im,182 +peopleops.partner6.sme@status.im,216 +peopleops.partner7.sme@status.im,217 +peopleops.partner8.sme@status.im,218 +peopleops.partner9.sme@status.im,219 peopleops.partner@status.im,150 peopleops.project-lead@status.im,147 peopleops.talent.sme@status.im,143 @@ -87,4 +108,7 @@ security2.sme@status.im,168 security3.sme@status.im,169 security4.sme@status.im,179 security5.sme@status.im,180 +security6.sme@status.im,211 services.lead@status.im,122 +vac.program-lead@status.im,163 +waku.research.project-lead@status.im,164 diff --git a/spiffworkflow-frontend/cypress/pilot/pp1.cy.js b/spiffworkflow-frontend/cypress/pilot/pp1.cy.js index f0e670de..bcb2091f 100644 --- a/spiffworkflow-frontend/cypress/pilot/pp1.cy.js +++ b/spiffworkflow-frontend/cypress/pilot/pp1.cy.js @@ -103,7 +103,7 @@ describe('pp1', () => { processInstanceId, 'Task: Update Application Landscape' ); - // approveWithUser('legal-a1.sme', processInstanceId); + approveWithUser('legal-a1.sme', processInstanceId); }); }); }); From aa2c21efb4dc53aaac08dc4314e760f39145888d Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 10 Mar 2023 10:36:42 -0500 Subject: [PATCH 12/48] When a row is clicked in a Process Instance list take the end user to that process instance UNLESS they click on the column containing the process name, in which case take them to that process model. Belugh. --- .../components/ProcessInstanceListTable.tsx | 85 ++++++++++++------- 1 file changed, 54 insertions(+), 31 deletions(-) diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx index 76a04440..d4af460d 100644 --- a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx +++ b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx @@ -1,10 +1,5 @@ import { useEffect, useMemo, useRef, useState } from 'react'; -import { - Link, - useNavigate, - useParams, - useSearchParams, -} from 'react-router-dom'; +import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; // @ts-ignore import { Filter, Close, AddAlt } from '@carbon/icons-react'; @@ -66,7 +61,6 @@ import ProcessModelSearch from './ProcessModelSearch'; import ProcessInstanceReportSearch from './ProcessInstanceReportSearch'; import ProcessInstanceListDeleteReport from './ProcessInstanceListDeleteReport'; import ProcessInstanceListSaveAsReport from './ProcessInstanceListSaveAsReport'; -import { FormatProcessModelDisplayName } from './MiniComponents'; import { Notification } from './Notification'; import useAPIError from '../hooks/UseApiError'; @@ -1203,28 +1197,13 @@ export default function ProcessInstanceListTable({ }); const formatProcessInstanceId = (row: ProcessInstance, id: number) => { - const modifiedProcessModelId: String = - modifyProcessIdentifierForPathParam(row.process_model_identifier); - return ( - - {id} - - ); + return {id}; }; const formatProcessModelIdentifier = (_row: any, identifier: any) => { - return ( - - {identifier} - - ); + return {identifier}; + }; + const formatProcessModelDisplayName = (_row: any, identifier: any) => { + return {identifier}; }; const formatSecondsForDisplay = (_row: any, seconds: any) => { @@ -1237,7 +1216,7 @@ export default function ProcessInstanceListTable({ const reportColumnFormatters: Record = { id: formatProcessInstanceId, process_model_identifier: formatProcessModelIdentifier, - process_model_display_name: FormatProcessModelDisplayName, + process_model_display_name: formatProcessModelDisplayName, start_in_seconds: formatSecondsForDisplay, end_in_seconds: formatSecondsForDisplay, }; @@ -1245,21 +1224,65 @@ export default function ProcessInstanceListTable({ const formatter = reportColumnFormatters[column.accessor] ?? defaultFormatter; const value = row[column.accessor]; + const modifiedModelId = modifyProcessIdentifierForPathParam( + row.process_model_identifier + ); + const navigateToProcessInstance = () => { + navigate( + `${processInstanceShowPathPrefix}/${modifiedModelId}/${row.id}` + ); + }; + const navigateToProcessModel = () => { + navigate(`/admin/process-models/${modifiedModelId}`); + }; + if (column.accessor === 'status') { return ( - + // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions + {formatter(row, value)} ); } - return {formatter(row, value)}; + console.log(column.accessor); + if (column.accessor === 'process_model_display_name') { + const pmStyle = { background: 'rgba(0, 0, 0, .02)' }; + return ( + // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions + + {formatter(row, value)} + + ); + } + return ( + // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions + + {formatter(row, value)} + + ); }; const rows = processInstances.map((row: any) => { const currentRow = reportColumns().map((column: any) => { return formattedColumn(row, column); }); - return {currentRow}; + const rowStyle = { cursor: 'pointer' }; + return ( + + {currentRow} + + ); }); return ( From 66a6c0449a5e9386c2ada886504764faac7c911f Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 10 Mar 2023 10:46:40 -0500 Subject: [PATCH 13/48] added subprocess and call activity to task data bpmn file and the test is passing now --- .../services/process_instance_processor.py | 62 +++++----- .../services/task_service.py | 47 +++++--- .../services/workflow_execution_service.py | 6 +- .../manual_task_with_subprocesses.bpmn | 112 ++++++++++++++++++ .../test_process_to_call.bpmn | 39 ++++++ .../unit/test_process_instance_processor.py | 5 +- 6 files changed, 223 insertions(+), 48 deletions(-) create mode 100644 spiffworkflow-backend/tests/data/manual_task_with_subprocesses/manual_task_with_subprocesses.bpmn create mode 100644 spiffworkflow-backend/tests/data/manual_task_with_subprocesses/test_process_to_call.bpmn diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index eeee7dc6..cdfa44ce 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -1047,6 +1047,7 @@ class ProcessInstanceProcessor: bpmn_process_dict: dict, bpmn_process_parent: Optional[BpmnProcessModel] = None, bpmn_process_guid: Optional[str] = None, + add_tasks_if_new_bpmn_process: bool = True, ) -> BpmnProcessModel: tasks = bpmn_process_dict.pop("tasks") bpmn_process_data = bpmn_process_dict.pop("data") @@ -1059,7 +1060,9 @@ class ProcessInstanceProcessor: elif self.process_instance_model.bpmn_process_id is not None: bpmn_process = self.process_instance_model.bpmn_process + bpmn_process_is_new = False if bpmn_process is None: + bpmn_process_is_new = True bpmn_process = BpmnProcessModel(guid=bpmn_process_guid) bpmn_process.properties_json = bpmn_process_dict @@ -1087,25 +1090,27 @@ class ProcessInstanceProcessor: bpmn_process.parent_process_id = bpmn_process_parent.id db.session.add(bpmn_process) - for task_id, task_properties in tasks.items(): - task_data_dict = task_properties.pop("data") - state_int = task_properties["state"] + if bpmn_process_is_new and add_tasks_if_new_bpmn_process: + # if True: + for task_id, task_properties in tasks.items(): + task_data_dict = task_properties.pop("data") + state_int = task_properties["state"] - task_model = TaskModel.query.filter_by(guid=task_id).first() - if task_model is None: - # bpmn_process_identifier = task_properties['workflow_name'] - # bpmn_identifier = task_properties['task_spec'] - # - # task_definition = TaskDefinitionModel.query.filter_by(bpmn_identifier=bpmn_identifier) - # .join(BpmnProcessDefinitionModel).filter(BpmnProcessDefinitionModel.bpmn_identifier==bpmn_process_identifier).first() - # if task_definition is None: - # subprocess_task = TaskModel.query.filter_by(guid=bpmn_process.guid) - task_model = TaskModel(guid=task_id, bpmn_process_id=bpmn_process.id) - task_model.state = TaskStateNames[state_int] - task_model.properties_json = task_properties + task_model = TaskModel.query.filter_by(guid=task_id).first() + if task_model is None: + # bpmn_process_identifier = task_properties['workflow_name'] + # bpmn_identifier = task_properties['task_spec'] + # + # task_definition = TaskDefinitionModel.query.filter_by(bpmn_identifier=bpmn_identifier) + # .join(BpmnProcessDefinitionModel).filter(BpmnProcessDefinitionModel.bpmn_identifier==bpmn_process_identifier).first() + # if task_definition is None: + # subprocess_task = TaskModel.query.filter_by(guid=bpmn_process.guid) + task_model = TaskModel(guid=task_id, bpmn_process_id=bpmn_process.id) + task_model.state = TaskStateNames[state_int] + task_model.properties_json = task_properties - TaskService.update_task_data_on_task_model(task_model, task_data_dict) - db.session.add(task_model) + TaskService.update_task_data_on_task_model(task_model, task_data_dict) + db.session.add(task_model) return bpmn_process @@ -1115,7 +1120,6 @@ class ProcessInstanceProcessor: Expects the save method to commit it. """ bpmn_dict = json.loads(self.serialize()) - # with open('tmp2.json', 'w') as f: f.write(json.dumps(bpmn_dict) bpmn_dict_keys = ("spec", "subprocess_specs", "serializer_version") process_instance_data_dict = {} bpmn_spec_dict = {} @@ -1132,15 +1136,18 @@ class ProcessInstanceProcessor: # FIXME: Update tasks in the did_complete_task instead to set the final info. # We will need to somehow cache all tasks initially though before each task is run. # Maybe always do this for first run - just need to know it's the first run. - if self.process_instance_model.bpmn_process_id is None: - subprocesses = process_instance_data_dict.pop("subprocesses") - bpmn_process_parent = self._add_bpmn_process(process_instance_data_dict) - for subprocess_task_id, subprocess_properties in subprocesses.items(): - self._add_bpmn_process( - subprocess_properties, - bpmn_process_parent, - bpmn_process_guid=subprocess_task_id, - ) + # import pdb; pdb.set_trace() + # if self.process_instance_model.bpmn_process_id is None: + subprocesses = process_instance_data_dict.pop("subprocesses") + bpmn_process_parent = self._add_bpmn_process(process_instance_data_dict) + for subprocess_task_id, subprocess_properties in subprocesses.items(): + # import pdb; pdb.set_trace() + print(f"subprocess_task_id: {subprocess_task_id}") + self._add_bpmn_process( + subprocess_properties, + bpmn_process_parent, + bpmn_process_guid=subprocess_task_id, + ) def save(self) -> None: """Saves the current state of this processor to the database.""" @@ -1693,6 +1700,7 @@ class ProcessInstanceProcessor: secondary_engine_step_delegate=step_delegate, serializer=self._serializer, process_instance=self.process_instance_model, + add_bpmn_process=self._add_bpmn_process, ) execution_strategy = execution_strategy_named( execution_strategy_name, task_model_delegate diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py index 32dbade8..700f29ca 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py @@ -1,8 +1,10 @@ import json from hashlib import sha256 +from typing import Tuple +from typing import Any from typing import Optional -from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer # type: ignore +from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer, BpmnWorkflow # type: ignore from SpiffWorkflow.task import Task as SpiffTask # type: ignore from SpiffWorkflow.task import TaskStateNames @@ -50,49 +52,60 @@ class TaskService: @classmethod def find_or_create_task_model_from_spiff_task( - cls, spiff_task: SpiffTask, process_instance: ProcessInstanceModel + cls, spiff_task: SpiffTask, process_instance: ProcessInstanceModel, + serializer: BpmnWorkflowSerializer, add_bpmn_process: Any ) -> TaskModel: spiff_task_guid = str(spiff_task.id) task_model: Optional[TaskModel] = TaskModel.query.filter_by( guid=spiff_task_guid ).first() if task_model is None: - bpmn_process = cls.task_bpmn_process(spiff_task, process_instance) - task_model = TaskModel( - guid=spiff_task_guid, bpmn_process_id=bpmn_process.id - ) - db.session.add(task_model) - db.session.commit() + bpmn_process = cls.task_bpmn_process(spiff_task, process_instance, serializer, add_bpmn_process) + task_model = TaskModel.query.filter_by( + guid=spiff_task_guid + ).first() + if task_model is None: + task_model = TaskModel( + guid=spiff_task_guid, bpmn_process_id=bpmn_process.id + ) + db.session.commit() return task_model @classmethod - def task_subprocess_guid(cls, spiff_task: SpiffTask) -> Optional[str]: + def task_subprocess(cls, spiff_task: SpiffTask) -> Optional[Tuple[str, BpmnWorkflow]]: top_level_workflow = spiff_task.workflow._get_outermost_workflow() my_wf = spiff_task.workflow # This is the workflow the spiff_task is part of + my_sp = None my_sp_id = None if my_wf != top_level_workflow: # All the subprocesses are at the top level, so you can just compare them for sp_id, sp in top_level_workflow.subprocesses.items(): if sp == my_wf: + my_sp = sp my_sp_id = sp_id break - return my_sp_id + return (str(my_sp_id), my_sp) @classmethod def task_bpmn_process( - cls, spiff_task: SpiffTask, process_instance: ProcessInstanceModel + cls, spiff_task: SpiffTask, process_instance: ProcessInstanceModel, + serializer: BpmnWorkflowSerializer, add_bpmn_process: Any ) -> BpmnProcessModel: - subprocess_guid = cls.task_subprocess_guid(spiff_task) - if subprocess_guid is None: + subprocess_guid, subprocess = cls.task_subprocess(spiff_task) + if subprocess is None: # This is the top level workflow, which has no guid return process_instance.bpmn_process else: + # import pdb; pdb.set_trace() bpmn_process: Optional[BpmnProcessModel] = BpmnProcessModel.query.filter_by( guid=subprocess_guid ).first() + # import pdb; pdb.set_trace() if bpmn_process is None: - spiff_task_guid = str(spiff_task.id) - raise Exception( - f"Could not find bpmn_process for task {spiff_task_guid}" - ) + bpmn_process = add_bpmn_process(serializer.workflow_to_dict(subprocess), process_instance.bpmn_process, subprocess_guid, add_tasks_if_new_bpmn_process=True) + db.session.commit() + # spiff_task_guid = str(spiff_task.id) + # raise Exception( + # f"Could not find bpmn_process for task {spiff_task_guid}" + # ) return bpmn_process diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index bdd8ebee..04cac19f 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -1,5 +1,6 @@ import logging import time +from typing import Any from typing import Callable from typing import List from typing import Optional @@ -50,12 +51,14 @@ class TaskModelSavingDelegate(EngineStepDelegate): serializer: BpmnWorkflowSerializer, process_instance: ProcessInstanceModel, secondary_engine_step_delegate: Optional[EngineStepDelegate] = None, + add_bpmn_process: Any = None, ) -> None: self.secondary_engine_step_delegate = secondary_engine_step_delegate self.process_instance = process_instance self.current_task_model: Optional[TaskModel] = None self.serializer = serializer + self.add_bpmn_process = add_bpmn_process def should_update_task_model(self) -> bool: """We need to figure out if we have previously save task info on this process intance. @@ -63,12 +66,13 @@ class TaskModelSavingDelegate(EngineStepDelegate): Use the bpmn_process_id to do this. """ return self.process_instance.bpmn_process_id is not None + # return False def will_complete_task(self, spiff_task: SpiffTask) -> None: if self.should_update_task_model(): self.current_task_model = ( TaskService.find_or_create_task_model_from_spiff_task( - spiff_task, self.process_instance + spiff_task, self.process_instance, self.serializer, self.add_bpmn_process ) ) self.current_task_model.start_in_seconds = time.time() diff --git a/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/manual_task_with_subprocesses.bpmn b/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/manual_task_with_subprocesses.bpmn new file mode 100644 index 00000000..d1b462f1 --- /dev/null +++ b/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/manual_task_with_subprocesses.bpmn @@ -0,0 +1,112 @@ + + + + + Flow_0stlaxe + + + Flow_1and8ze + + + + ## Hello + + Flow_1fktmf7 + Flow_09gjylo + + + + Flow_0stlaxe + Flow_1fktmf7 + set_in_top_level_script = 1 + + + + + + + Flow_09gjylo + Flow_1i7syph + + Flow_00k1tii + + + + Flow_1b4o55k + + + + Flow_00k1tii + Flow_1b4o55k + set_in_top_level_subprocess = 1 + + + + Flow_1i7syph + Flow_1and8ze + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/test_process_to_call.bpmn b/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/test_process_to_call.bpmn new file mode 100644 index 00000000..25b37c61 --- /dev/null +++ b/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/test_process_to_call.bpmn @@ -0,0 +1,39 @@ + + + + + Flow_06g687y + + + + Flow_01e21r0 + + + + Flow_06g687y + Flow_01e21r0 + set_in_test_process_to_call_script = 1 + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py index 7fa0f13b..63b4fa4f 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py @@ -314,9 +314,8 @@ class TestProcessInstanceProcessor(BaseTest): assert finance_group is not None process_model = load_test_spec( - process_model_id="test_group/manual_task", - bpmn_file_name="manual_task.bpmn", - process_model_source_directory="manual_task", + process_model_id="test_group/manual_task_with_subprocesses", + process_model_source_directory="manual_task_with_subprocesses", ) process_instance = self.create_process_instance_from_process_model( process_model=process_model, user=initiator_user From 3428d9a3948f58773efa76df94c46824406b7354 Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 10 Mar 2023 10:52:37 -0500 Subject: [PATCH 14/48] add more details to cypress pilot script w/ burnettk --- spiffworkflow-frontend/bin/cypress_pilot | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/spiffworkflow-frontend/bin/cypress_pilot b/spiffworkflow-frontend/bin/cypress_pilot index 650368bf..f69dae06 100755 --- a/spiffworkflow-frontend/bin/cypress_pilot +++ b/spiffworkflow-frontend/bin/cypress_pilot @@ -30,9 +30,15 @@ if [[ -z "${CYPRESS_SPIFFWORKFLOW_FRONTEND_AUTH_WITH_KEYCLOAK:-}" ]]; then export CYPRESS_SPIFFWORKFLOW_FRONTEND_AUTH_WITH_KEYCLOAK=true fi -cypress_run_file="/var/tmp/cypress_run_$(date +%s)" +cypress_run_file="/var/tmp/cypress_run" echo "Recording stats to ${cypress_run_file}" +if [[ ! -f "$cypress_run_file" ]]; then + echo "success,duration,start_time,end_time,frontend_url" >"$cypress_run_file" +fi + +frontend_url="${SPIFFWORKFLOW_FRONTEND_URL:localhost}" + for attempt in $(seq 1 "$ATTEMPTS" ); do echo "Running attempt: ${attempt}" @@ -43,6 +49,6 @@ for attempt in $(seq 1 "$ATTEMPTS" ); do fi end_time=$(date +%s) - echo "${success},$(( end_time - start_time ))" >>"$cypress_run_file" + echo "${success},$(( end_time - start_time )),$(date "-d@${start_time}"),$(date "-d@${end_time}"),${frontend_url}" >>"$cypress_run_file" done echo "Recorded stats to ${cypress_run_file}" From 6d5c03a3d0880b08ba8c0f66ddbd6af964c1367c Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 10 Mar 2023 12:23:27 -0500 Subject: [PATCH 15/48] moved add_bpmn_process to task_service w/ burnettk --- .../services/process_instance_processor.py | 78 +----------------- .../services/task_service.py | 82 +++++++++++++++++-- .../services/workflow_execution_service.py | 4 +- 3 files changed, 81 insertions(+), 83 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index cdfa44ce..a68d7d09 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -1042,78 +1042,6 @@ class ProcessInstanceProcessor: bpmn_process_definition_parent ) - def _add_bpmn_process( - self, - bpmn_process_dict: dict, - bpmn_process_parent: Optional[BpmnProcessModel] = None, - bpmn_process_guid: Optional[str] = None, - add_tasks_if_new_bpmn_process: bool = True, - ) -> BpmnProcessModel: - tasks = bpmn_process_dict.pop("tasks") - bpmn_process_data = bpmn_process_dict.pop("data") - - bpmn_process = None - if bpmn_process_parent is not None: - bpmn_process = BpmnProcessModel.query.filter_by( - parent_process_id=bpmn_process_parent.id, guid=bpmn_process_guid - ).first() - elif self.process_instance_model.bpmn_process_id is not None: - bpmn_process = self.process_instance_model.bpmn_process - - bpmn_process_is_new = False - if bpmn_process is None: - bpmn_process_is_new = True - bpmn_process = BpmnProcessModel(guid=bpmn_process_guid) - - bpmn_process.properties_json = bpmn_process_dict - - bpmn_process_data_json = json.dumps(bpmn_process_data, sort_keys=True).encode( - "utf8" - ) - bpmn_process_data_hash = sha256(bpmn_process_data_json).hexdigest() - if bpmn_process.json_data_hash != bpmn_process_data_hash: - json_data = ( - db.session.query(JsonDataModel.id) - .filter_by(hash=bpmn_process_data_hash) - .first() - ) - if json_data is None: - json_data = JsonDataModel( - hash=bpmn_process_data_hash, data=bpmn_process_data - ) - db.session.add(json_data) - bpmn_process.json_data_hash = bpmn_process_data_hash - - if bpmn_process_parent is None: - self.process_instance_model.bpmn_process = bpmn_process - elif bpmn_process.parent_process_id is None: - bpmn_process.parent_process_id = bpmn_process_parent.id - db.session.add(bpmn_process) - - if bpmn_process_is_new and add_tasks_if_new_bpmn_process: - # if True: - for task_id, task_properties in tasks.items(): - task_data_dict = task_properties.pop("data") - state_int = task_properties["state"] - - task_model = TaskModel.query.filter_by(guid=task_id).first() - if task_model is None: - # bpmn_process_identifier = task_properties['workflow_name'] - # bpmn_identifier = task_properties['task_spec'] - # - # task_definition = TaskDefinitionModel.query.filter_by(bpmn_identifier=bpmn_identifier) - # .join(BpmnProcessDefinitionModel).filter(BpmnProcessDefinitionModel.bpmn_identifier==bpmn_process_identifier).first() - # if task_definition is None: - # subprocess_task = TaskModel.query.filter_by(guid=bpmn_process.guid) - task_model = TaskModel(guid=task_id, bpmn_process_id=bpmn_process.id) - task_model.state = TaskStateNames[state_int] - task_model.properties_json = task_properties - - TaskService.update_task_data_on_task_model(task_model, task_data_dict) - db.session.add(task_model) - - return bpmn_process - def _add_bpmn_json_records(self) -> None: """Adds serialized_bpmn_definition and process_instance_data records to the db session. @@ -1139,12 +1067,13 @@ class ProcessInstanceProcessor: # import pdb; pdb.set_trace() # if self.process_instance_model.bpmn_process_id is None: subprocesses = process_instance_data_dict.pop("subprocesses") - bpmn_process_parent = self._add_bpmn_process(process_instance_data_dict) + bpmn_process_parent = TaskService.add_bpmn_process(process_instance_data_dict, self.process_instance_model) for subprocess_task_id, subprocess_properties in subprocesses.items(): # import pdb; pdb.set_trace() print(f"subprocess_task_id: {subprocess_task_id}") - self._add_bpmn_process( + TaskService.add_bpmn_process( subprocess_properties, + self.process_instance_model, bpmn_process_parent, bpmn_process_guid=subprocess_task_id, ) @@ -1700,7 +1629,6 @@ class ProcessInstanceProcessor: secondary_engine_step_delegate=step_delegate, serializer=self._serializer, process_instance=self.process_instance_model, - add_bpmn_process=self._add_bpmn_process, ) execution_strategy = execution_strategy_named( execution_strategy_name, task_model_delegate diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py index 700f29ca..763dd1b6 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py @@ -53,14 +53,14 @@ class TaskService: @classmethod def find_or_create_task_model_from_spiff_task( cls, spiff_task: SpiffTask, process_instance: ProcessInstanceModel, - serializer: BpmnWorkflowSerializer, add_bpmn_process: Any + serializer: BpmnWorkflowSerializer ) -> TaskModel: spiff_task_guid = str(spiff_task.id) task_model: Optional[TaskModel] = TaskModel.query.filter_by( guid=spiff_task_guid ).first() if task_model is None: - bpmn_process = cls.task_bpmn_process(spiff_task, process_instance, serializer, add_bpmn_process) + bpmn_process = cls.task_bpmn_process(spiff_task, process_instance, serializer) task_model = TaskModel.query.filter_by( guid=spiff_task_guid ).first() @@ -72,7 +72,7 @@ class TaskService: return task_model @classmethod - def task_subprocess(cls, spiff_task: SpiffTask) -> Optional[Tuple[str, BpmnWorkflow]]: + def task_subprocess(cls, spiff_task: SpiffTask) -> Tuple[Optional[str], Optional[BpmnWorkflow]]: top_level_workflow = spiff_task.workflow._get_outermost_workflow() my_wf = spiff_task.workflow # This is the workflow the spiff_task is part of my_sp = None @@ -89,7 +89,7 @@ class TaskService: @classmethod def task_bpmn_process( cls, spiff_task: SpiffTask, process_instance: ProcessInstanceModel, - serializer: BpmnWorkflowSerializer, add_bpmn_process: Any + serializer: BpmnWorkflowSerializer ) -> BpmnProcessModel: subprocess_guid, subprocess = cls.task_subprocess(spiff_task) if subprocess is None: @@ -102,10 +102,82 @@ class TaskService: ).first() # import pdb; pdb.set_trace() if bpmn_process is None: - bpmn_process = add_bpmn_process(serializer.workflow_to_dict(subprocess), process_instance.bpmn_process, subprocess_guid, add_tasks_if_new_bpmn_process=True) + bpmn_process = cls.add_bpmn_process(serializer.workflow_to_dict(subprocess), process_instance, process_instance.bpmn_process, subprocess_guid) db.session.commit() # spiff_task_guid = str(spiff_task.id) # raise Exception( # f"Could not find bpmn_process for task {spiff_task_guid}" # ) return bpmn_process + + @classmethod + def add_bpmn_process( + cls, + bpmn_process_dict: dict, + process_instance: ProcessInstanceModel, + bpmn_process_parent: Optional[BpmnProcessModel] = None, + bpmn_process_guid: Optional[str] = None, + ) -> BpmnProcessModel: + tasks = bpmn_process_dict.pop("tasks") + bpmn_process_data = bpmn_process_dict.pop("data") + + bpmn_process = None + if bpmn_process_parent is not None: + bpmn_process = BpmnProcessModel.query.filter_by( + parent_process_id=bpmn_process_parent.id, guid=bpmn_process_guid + ).first() + elif process_instance.bpmn_process_id is not None: + bpmn_process = process_instance.bpmn_process + + bpmn_process_is_new = False + if bpmn_process is None: + bpmn_process_is_new = True + bpmn_process = BpmnProcessModel(guid=bpmn_process_guid) + + bpmn_process.properties_json = bpmn_process_dict + + bpmn_process_data_json = json.dumps(bpmn_process_data, sort_keys=True).encode( + "utf8" + ) + bpmn_process_data_hash = sha256(bpmn_process_data_json).hexdigest() + if bpmn_process.json_data_hash != bpmn_process_data_hash: + json_data = ( + db.session.query(JsonDataModel.id) + .filter_by(hash=bpmn_process_data_hash) + .first() + ) + if json_data is None: + json_data = JsonDataModel( + hash=bpmn_process_data_hash, data=bpmn_process_data + ) + db.session.add(json_data) + bpmn_process.json_data_hash = bpmn_process_data_hash + + if bpmn_process_parent is None: + process_instance.bpmn_process = bpmn_process + elif bpmn_process.parent_process_id is None: + bpmn_process.parent_process_id = bpmn_process_parent.id + db.session.add(bpmn_process) + + if bpmn_process_is_new: + for task_id, task_properties in tasks.items(): + task_data_dict = task_properties.pop("data") + state_int = task_properties["state"] + + task_model = TaskModel.query.filter_by(guid=task_id).first() + if task_model is None: + # bpmn_process_identifier = task_properties['workflow_name'] + # bpmn_identifier = task_properties['task_spec'] + # + # task_definition = TaskDefinitionModel.query.filter_by(bpmn_identifier=bpmn_identifier) + # .join(BpmnProcessDefinitionModel).filter(BpmnProcessDefinitionModel.bpmn_identifier==bpmn_process_identifier).first() + # if task_definition is None: + # subprocess_task = TaskModel.query.filter_by(guid=bpmn_process.guid) + task_model = TaskModel(guid=task_id, bpmn_process_id=bpmn_process.id) + task_model.state = TaskStateNames[state_int] + task_model.properties_json = task_properties + + TaskService.update_task_data_on_task_model(task_model, task_data_dict) + db.session.add(task_model) + + return bpmn_process diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index 04cac19f..37b289c0 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -51,14 +51,12 @@ class TaskModelSavingDelegate(EngineStepDelegate): serializer: BpmnWorkflowSerializer, process_instance: ProcessInstanceModel, secondary_engine_step_delegate: Optional[EngineStepDelegate] = None, - add_bpmn_process: Any = None, ) -> None: self.secondary_engine_step_delegate = secondary_engine_step_delegate self.process_instance = process_instance self.current_task_model: Optional[TaskModel] = None self.serializer = serializer - self.add_bpmn_process = add_bpmn_process def should_update_task_model(self) -> bool: """We need to figure out if we have previously save task info on this process intance. @@ -72,7 +70,7 @@ class TaskModelSavingDelegate(EngineStepDelegate): if self.should_update_task_model(): self.current_task_model = ( TaskService.find_or_create_task_model_from_spiff_task( - spiff_task, self.process_instance, self.serializer, self.add_bpmn_process + spiff_task, self.process_instance, self.serializer ) ) self.current_task_model.start_in_seconds = time.time() From 12c4c3556704a0b5bb3fa3aebbb3964219fe455f Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 10 Mar 2023 12:23:42 -0500 Subject: [PATCH 16/48] fix dates on mac --- spiffworkflow-frontend/bin/cypress_pilot | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/spiffworkflow-frontend/bin/cypress_pilot b/spiffworkflow-frontend/bin/cypress_pilot index f69dae06..ecf50455 100755 --- a/spiffworkflow-frontend/bin/cypress_pilot +++ b/spiffworkflow-frontend/bin/cypress_pilot @@ -37,7 +37,7 @@ if [[ ! -f "$cypress_run_file" ]]; then echo "success,duration,start_time,end_time,frontend_url" >"$cypress_run_file" fi -frontend_url="${SPIFFWORKFLOW_FRONTEND_URL:localhost}" +frontend_url="${SPIFFWORKFLOW_FRONTEND_URL:-localhost}" for attempt in $(seq 1 "$ATTEMPTS" ); do echo "Running attempt: ${attempt}" @@ -48,7 +48,14 @@ for attempt in $(seq 1 "$ATTEMPTS" ); do success="true" fi end_time=$(date +%s) + if is_mac; then + formatted_start_time=$(date -r "${start_time}" +"%Y-%m-%dT%H-%M-%S") + formatted_end_time=$(date -r "${end_time}" +"%Y-%m-%dT%H-%M-%S") + else + formatted_start_time=$(date "-d@${start_time}" +"%Y-%m-%dT%H-%M-%S") + formatted_end_time=$(date "-d@${end_time}" +"%Y-%m-%dT%H-%M-%S") + fi - echo "${success},$(( end_time - start_time )),$(date "-d@${start_time}"),$(date "-d@${end_time}"),${frontend_url}" >>"$cypress_run_file" + echo "${success},$(( end_time - start_time )),${formatted_start_time},${formatted_end_time},${frontend_url}" >>"$cypress_run_file" done echo "Recorded stats to ${cypress_run_file}" From 1de98877114e80d77a7fc4ffbc1884a88b30cfc8 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 10 Mar 2023 12:56:37 -0500 Subject: [PATCH 17/48] Fixing Cypress Tests --- spiffworkflow-frontend/cypress.config.js | 2 +- spiffworkflow-frontend/cypress/e2e/process_instances.cy.js | 2 +- spiffworkflow-frontend/cypress/e2e/process_models.cy.js | 2 +- spiffworkflow-frontend/cypress/e2e/tasks.cy.js | 6 +++--- .../src/components/ProcessInstanceListTable.tsx | 1 + 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/spiffworkflow-frontend/cypress.config.js b/spiffworkflow-frontend/cypress.config.js index f7f7b48c..5dc0a4f3 100644 --- a/spiffworkflow-frontend/cypress.config.js +++ b/spiffworkflow-frontend/cypress.config.js @@ -32,7 +32,7 @@ if (process.env.SPIFFWORKFLOW_FRONTEND_URL) { const cypressConfig = { projectId: 'crax1q', - + defaultCommandTimeout: 10000, videoUploadOnPasses: false, chromeWebSecurity: false, e2e: { diff --git a/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js b/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js index 846d4162..64e0418a 100644 --- a/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js @@ -158,7 +158,7 @@ describe('process-instances', () => { // make sure we have some process instances cy.runPrimaryBpmnFile(); cy.getBySel('process-instance-list-link').click(); - cy.getBySel('process-instance-show-link').first().click(); + cy.getBySel('process-instance-show-link-id').first().click(); cy.getBySel('process-instance-log-list-link').click(); cy.getBySel('process-instance-log-detailed').click(); cy.contains('process_model_one'); diff --git a/spiffworkflow-frontend/cypress/e2e/process_models.cy.js b/spiffworkflow-frontend/cypress/e2e/process_models.cy.js index 0e925046..9e7bd55d 100644 --- a/spiffworkflow-frontend/cypress/e2e/process_models.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/process_models.cy.js @@ -168,7 +168,7 @@ describe('process-models', () => { .click(); cy.runPrimaryBpmnFile(); - cy.getBySel('process-instance-show-link').click(); + cy.getBySel('process-instance-show-link-id').click(); cy.getBySel('process-instance-delete').click(); cy.contains('Are you sure'); cy.getBySel('process-instance-delete-modal-confirmation-dialog') diff --git a/spiffworkflow-frontend/cypress/e2e/tasks.cy.js b/spiffworkflow-frontend/cypress/e2e/tasks.cy.js index 922c4209..06e59d81 100644 --- a/spiffworkflow-frontend/cypress/e2e/tasks.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/tasks.cy.js @@ -74,7 +74,7 @@ describe('tasks', () => { cy.assertAtLeastOneItemInPaginatedResults(); // This should get the first one which should be the one we just completed - cy.getBySel('process-instance-show-link').first().click(); + cy.getBySel('process-instance-show-link-id').first().click(); cy.contains('Process Instance Id: '); cy.get(`g[data-element-id=form3]`).click(); @@ -106,7 +106,7 @@ describe('tasks', () => { cy.assertAtLeastOneItemInPaginatedResults(); // This should get the first one which should be the one we just completed - cy.getBySel('process-instance-show-link').first().click(); + cy.getBySel('process-instance-show-link-id').first().click(); cy.contains('Process Instance Id: '); cy.contains('Status: complete'); }); @@ -120,6 +120,6 @@ describe('tasks', () => { kickOffModelWithForm(); cy.navigateToHome(); - cy.basicPaginationTest('process-instance-show-link'); + cy.basicPaginationTest('process-instance-show-link-id'); }); }); diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx index d4af460d..8c8f818f 100644 --- a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx +++ b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx @@ -1265,6 +1265,7 @@ export default function ProcessInstanceListTable({ return ( // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions From e13e703825574de7167d3fee3a9ad45979feff84 Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 10 Mar 2023 13:29:23 -0500 Subject: [PATCH 18/48] check task data on spiff tasks --- .../services/process_instance_processor.py | 7 ---- .../unit/test_process_instance_processor.py | 41 ++++++++++++++----- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index a68d7d09..3b9c43ac 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -1061,16 +1061,9 @@ class ProcessInstanceProcessor: # if self.process_instance_model.bpmn_process_definition_id is None: self._add_bpmn_process_definitions(bpmn_spec_dict) - # FIXME: Update tasks in the did_complete_task instead to set the final info. - # We will need to somehow cache all tasks initially though before each task is run. - # Maybe always do this for first run - just need to know it's the first run. - # import pdb; pdb.set_trace() - # if self.process_instance_model.bpmn_process_id is None: subprocesses = process_instance_data_dict.pop("subprocesses") bpmn_process_parent = TaskService.add_bpmn_process(process_instance_data_dict, self.process_instance_model) for subprocess_task_id, subprocess_properties in subprocesses.items(): - # import pdb; pdb.set_trace() - print(f"subprocess_task_id: {subprocess_task_id}") TaskService.add_bpmn_process( subprocess_properties, self.process_instance_model, diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py index 63b4fa4f..5e586fa0 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py @@ -333,26 +333,45 @@ class TestProcessInstanceProcessor(BaseTest): processor = ProcessInstanceProcessor(process_instance) human_task_one = process_instance.active_human_tasks[0] - spiff_task = processor.__class__.get_task_by_bpmn_identifier( + spiff_manual_task = processor.__class__.get_task_by_bpmn_identifier( human_task_one.task_name, processor.bpmn_process_instance ) ProcessInstanceService.complete_form_task( - processor, spiff_task, {}, initiator_user, human_task_one + processor, spiff_manual_task, {}, initiator_user, human_task_one ) + # recreate variables to ensure all bpmn json was recreated from scratch from the db process_instance_relookup = ProcessInstanceModel.query.filter_by( id=process_instance.id ).first() - processor = ProcessInstanceProcessor(process_instance_relookup) + processor_final = ProcessInstanceProcessor(process_instance_relookup) assert process_instance_relookup.status == "complete" - task = TaskModel.query.filter_by(guid=human_task_one.task_id).first() - assert task.state == "COMPLETED" - end_event_spiff_task = processor.__class__.get_task_by_bpmn_identifier( - "end_event_of_manual_task_model", processor.bpmn_process_instance - ) - assert end_event_spiff_task - assert end_event_spiff_task.state == TaskState.COMPLETED - # # NOTE: also check the spiff task from the new processor + + first_data_set = {'set_in_top_level_script': 1} + second_data_set = {**first_data_set, **{'set_in_top_level_subprocess': 1}} + third_data_set = {**second_data_set, **{'set_in_test_process_to_call_script': 1}} + expected_task_data = { + "top_level_script": first_data_set, + "manual_task": first_data_set, + "top_level_subprocess_script": second_data_set, + "top_level_subprocess": second_data_set, + "test_process_to_call_script": third_data_set, + "top_level_call_activity": third_data_set, + "end_event_of_manual_task_model": third_data_set, + } + + all_spiff_tasks = processor_final.bpmn_process_instance.get_tasks() + assert len(all_spiff_tasks) > 1 + for spiff_task in all_spiff_tasks: + assert spiff_task.state == TaskState.COMPLETED + spiff_task_name = spiff_task.task_spec.name + if spiff_task_name in expected_task_data: + spiff_task_data = expected_task_data[spiff_task_name] + failure_message = ( + f"Found unexpected task data on {spiff_task_name}. " + f"Expected: {spiff_task_data}, Found: {spiff_task.data}" + ) + assert spiff_task.data == spiff_task_data, failure_message def test_does_not_recreate_human_tasks_on_multiple_saves( self, From 7806e6c460de20710fb5b71894306d561fda3602 Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 10 Mar 2023 16:52:57 -0500 Subject: [PATCH 19/48] tests are now passing w/ burnettk --- .../services/process_instance_processor.py | 6 +- .../services/task_service.py | 83 +++++++++++-------- .../services/workflow_execution_service.py | 38 ++++++++- .../unit/test_process_instance_processor.py | 9 +- 4 files changed, 97 insertions(+), 39 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 3b9c43ac..0d52adb0 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -50,7 +50,6 @@ from SpiffWorkflow.serializer.exceptions import MissingSpecError # type: ignore from SpiffWorkflow.spiff.serializer.config import SPIFF_SPEC_CONFIG # type: ignore from SpiffWorkflow.task import Task as SpiffTask # type: ignore from SpiffWorkflow.task import TaskState -from SpiffWorkflow.task import TaskStateNames from SpiffWorkflow.util.deep_merge import DeepMerge # type: ignore from sqlalchemy import text @@ -612,6 +611,7 @@ class ProcessInstanceProcessor: ).first() bpmn_process_dict = {"data": json_data.data, "tasks": {}} bpmn_process_dict.update(bpmn_process.properties_json) + print(f"bpmn_process_dict: {bpmn_process_dict}") tasks = TaskModel.query.filter_by(bpmn_process_id=bpmn_process.id).all() for task in tasks: json_data = JsonDataModel.query.filter_by(hash=task.json_data_hash).first() @@ -1062,7 +1062,9 @@ class ProcessInstanceProcessor: self._add_bpmn_process_definitions(bpmn_spec_dict) subprocesses = process_instance_data_dict.pop("subprocesses") - bpmn_process_parent = TaskService.add_bpmn_process(process_instance_data_dict, self.process_instance_model) + bpmn_process_parent = TaskService.add_bpmn_process( + process_instance_data_dict, self.process_instance_model + ) for subprocess_task_id, subprocess_properties in subprocesses.items(): TaskService.add_bpmn_process( subprocess_properties, diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py index 763dd1b6..775e2909 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py @@ -1,10 +1,10 @@ import json from hashlib import sha256 -from typing import Tuple -from typing import Any from typing import Optional +from typing import Tuple -from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer, BpmnWorkflow # type: ignore +from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflow # type: ignore +from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer from SpiffWorkflow.task import Task as SpiffTask # type: ignore from SpiffWorkflow.task import TaskStateNames @@ -20,8 +20,8 @@ class TaskService: def update_task_data_on_task_model( cls, task_model: TaskModel, task_data_dict: dict ) -> None: - task_data_json = json.dumps(task_data_dict, sort_keys=True).encode("utf8") - task_data_hash = sha256(task_data_json).hexdigest() + task_data_json = json.dumps(task_data_dict, sort_keys=True) + task_data_hash = sha256(task_data_json.encode("utf8")).hexdigest() if task_model.json_data_hash != task_data_hash: json_data = ( db.session.query(JsonDataModel.id) @@ -45,34 +45,38 @@ class TaskService: This will NOT update start_in_seconds or end_in_seconds. """ new_properties_json = serializer.task_to_dict(spiff_task) + spiff_task_data = new_properties_json.pop("data") task_model.properties_json = new_properties_json task_model.state = TaskStateNames[new_properties_json["state"]] - cls.update_task_data_on_task_model(task_model, spiff_task.data) + cls.update_task_data_on_task_model(task_model, spiff_task_data) db.session.add(task_model) @classmethod def find_or_create_task_model_from_spiff_task( - cls, spiff_task: SpiffTask, process_instance: ProcessInstanceModel, - serializer: BpmnWorkflowSerializer + cls, + spiff_task: SpiffTask, + process_instance: ProcessInstanceModel, + serializer: BpmnWorkflowSerializer, ) -> TaskModel: spiff_task_guid = str(spiff_task.id) task_model: Optional[TaskModel] = TaskModel.query.filter_by( guid=spiff_task_guid ).first() if task_model is None: - bpmn_process = cls.task_bpmn_process(spiff_task, process_instance, serializer) - task_model = TaskModel.query.filter_by( - guid=spiff_task_guid - ).first() + bpmn_process = cls.task_bpmn_process( + spiff_task, process_instance, serializer + ) + task_model = TaskModel.query.filter_by(guid=spiff_task_guid).first() if task_model is None: task_model = TaskModel( guid=spiff_task_guid, bpmn_process_id=bpmn_process.id ) - db.session.commit() return task_model @classmethod - def task_subprocess(cls, spiff_task: SpiffTask) -> Tuple[Optional[str], Optional[BpmnWorkflow]]: + def task_subprocess( + cls, spiff_task: SpiffTask + ) -> Tuple[Optional[str], Optional[BpmnWorkflow]]: top_level_workflow = spiff_task.workflow._get_outermost_workflow() my_wf = spiff_task.workflow # This is the workflow the spiff_task is part of my_sp = None @@ -88,27 +92,38 @@ class TaskService: @classmethod def task_bpmn_process( - cls, spiff_task: SpiffTask, process_instance: ProcessInstanceModel, - serializer: BpmnWorkflowSerializer + cls, + spiff_task: SpiffTask, + process_instance: ProcessInstanceModel, + serializer: BpmnWorkflowSerializer, ) -> BpmnProcessModel: subprocess_guid, subprocess = cls.task_subprocess(spiff_task) + bpmn_process: Optional[BpmnProcessModel] = None if subprocess is None: + bpmn_process = process_instance.bpmn_process # This is the top level workflow, which has no guid - return process_instance.bpmn_process + # check for bpmn_process_id because mypy doesn't realize bpmn_process can be None + if process_instance.bpmn_process_id is None: + bpmn_process = cls.add_bpmn_process( + serializer.workflow_to_dict( + spiff_task.workflow._get_outermost_workflow() + ), + process_instance, + ) + db.session.commit() else: - # import pdb; pdb.set_trace() - bpmn_process: Optional[BpmnProcessModel] = BpmnProcessModel.query.filter_by( + bpmn_process = BpmnProcessModel.query.filter_by( guid=subprocess_guid ).first() - # import pdb; pdb.set_trace() if bpmn_process is None: - bpmn_process = cls.add_bpmn_process(serializer.workflow_to_dict(subprocess), process_instance, process_instance.bpmn_process, subprocess_guid) + bpmn_process = cls.add_bpmn_process( + serializer.workflow_to_dict(subprocess), + process_instance, + process_instance.bpmn_process, + subprocess_guid, + ) db.session.commit() - # spiff_task_guid = str(spiff_task.id) - # raise Exception( - # f"Could not find bpmn_process for task {spiff_task_guid}" - # ) - return bpmn_process + return bpmn_process @classmethod def add_bpmn_process( @@ -119,7 +134,7 @@ class TaskService: bpmn_process_guid: Optional[str] = None, ) -> BpmnProcessModel: tasks = bpmn_process_dict.pop("tasks") - bpmn_process_data = bpmn_process_dict.pop("data") + bpmn_process_data_dict = bpmn_process_dict.pop("data") bpmn_process = None if bpmn_process_parent is not None: @@ -136,10 +151,10 @@ class TaskService: bpmn_process.properties_json = bpmn_process_dict - bpmn_process_data_json = json.dumps(bpmn_process_data, sort_keys=True).encode( - "utf8" - ) - bpmn_process_data_hash = sha256(bpmn_process_data_json).hexdigest() + bpmn_process_data_json = json.dumps(bpmn_process_data_dict, sort_keys=True) + bpmn_process_data_hash = sha256( + bpmn_process_data_json.encode("utf8") + ).hexdigest() if bpmn_process.json_data_hash != bpmn_process_data_hash: json_data = ( db.session.query(JsonDataModel.id) @@ -148,7 +163,7 @@ class TaskService: ) if json_data is None: json_data = JsonDataModel( - hash=bpmn_process_data_hash, data=bpmn_process_data + hash=bpmn_process_data_hash, data=bpmn_process_data_dict ) db.session.add(json_data) bpmn_process.json_data_hash = bpmn_process_data_hash @@ -173,7 +188,9 @@ class TaskService: # .join(BpmnProcessDefinitionModel).filter(BpmnProcessDefinitionModel.bpmn_identifier==bpmn_process_identifier).first() # if task_definition is None: # subprocess_task = TaskModel.query.filter_by(guid=bpmn_process.guid) - task_model = TaskModel(guid=task_id, bpmn_process_id=bpmn_process.id) + task_model = TaskModel( + guid=task_id, bpmn_process_id=bpmn_process.id + ) task_model.state = TaskStateNames[state_int] task_model.properties_json = task_properties diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index 37b289c0..ef484fe6 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -1,6 +1,5 @@ import logging import time -from typing import Any from typing import Callable from typing import List from typing import Optional @@ -35,6 +34,9 @@ class EngineStepDelegate: def save(self, commit: bool = False) -> None: pass + def after_engine_steps(self, bpmn_process_instance: BpmnWorkflow) -> None: + pass + SpiffStepIncrementer = Callable[[], None] SpiffStepDetailsMappingBuilder = Callable[[SpiffTask, float, float], dict] @@ -92,6 +94,22 @@ class TaskModelSavingDelegate(EngineStepDelegate): self.secondary_engine_step_delegate.save(commit=False) db.session.commit() + def after_engine_steps(self, bpmn_process_instance: BpmnWorkflow) -> None: + for waiting_spiff_task in bpmn_process_instance.get_tasks( + TaskState.WAITING | TaskState.CANCELLED + ): + task_model = TaskModel.query.filter_by( + guid=str(waiting_spiff_task.id) + ).first() + if task_model is None: + task_model = TaskService.find_or_create_task_model_from_spiff_task( + waiting_spiff_task, self.process_instance, self.serializer + ) + TaskService.update_task_model_and_add_to_db_session( + task_model, waiting_spiff_task, self.serializer + ) + db.session.commit() + class StepDetailLoggingDelegate(EngineStepDelegate): """Engine step delegate that takes care of logging spiff step details. @@ -175,6 +193,7 @@ class GreedyExecutionStrategy(ExecutionStrategy): will_complete_task=self.delegate.will_complete_task, did_complete_task=self.delegate.did_complete_task, ) + self.delegate.after_engine_steps(bpmn_process_instance) class RunUntilServiceTaskExecutionStrategy(ExecutionStrategy): @@ -210,6 +229,8 @@ class RunUntilServiceTaskExecutionStrategy(ExecutionStrategy): ] ) + self.delegate.after_engine_steps(bpmn_process_instance) + def execution_strategy_named( name: str, delegate: EngineStepDelegate @@ -283,6 +304,13 @@ class WorkflowExecutionService: correlation_keys=self.bpmn_process_instance.correlations, ) db.session.add(message_instance) + + bpmn_process = self.process_instance_model.bpmn_process + if bpmn_process is not None: + bpmn_process_correlations = self.bpmn_process_instance.correlations + bpmn_process.properties_json["correlations"] = bpmn_process_correlations + db.session.add(bpmn_process) + db.session.commit() def queue_waiting_receive_messages(self) -> None: @@ -320,6 +348,14 @@ class WorkflowExecutionService: ) message_instance.correlation_rules.append(message_correlation) db.session.add(message_instance) + + bpmn_process = self.process_instance_model.bpmn_process + + if bpmn_process is not None: + bpmn_process_correlations = self.bpmn_process_instance.correlations + bpmn_process.properties_json["correlations"] = bpmn_process_correlations + db.session.add(bpmn_process) + db.session.commit() diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py index 5e586fa0..f124bcd9 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py @@ -347,9 +347,12 @@ class TestProcessInstanceProcessor(BaseTest): processor_final = ProcessInstanceProcessor(process_instance_relookup) assert process_instance_relookup.status == "complete" - first_data_set = {'set_in_top_level_script': 1} - second_data_set = {**first_data_set, **{'set_in_top_level_subprocess': 1}} - third_data_set = {**second_data_set, **{'set_in_test_process_to_call_script': 1}} + first_data_set = {"set_in_top_level_script": 1} + second_data_set = {**first_data_set, **{"set_in_top_level_subprocess": 1}} + third_data_set = { + **second_data_set, + **{"set_in_test_process_to_call_script": 1}, + } expected_task_data = { "top_level_script": first_data_set, "manual_task": first_data_set, From 55c7100c503475d89f46fbbdb2f8da8bb681b05c Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 10 Mar 2023 16:56:21 -0500 Subject: [PATCH 20/48] removed print statement w/ burnettk --- .../spiffworkflow_backend/services/process_instance_processor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 0d52adb0..5242c066 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -611,7 +611,6 @@ class ProcessInstanceProcessor: ).first() bpmn_process_dict = {"data": json_data.data, "tasks": {}} bpmn_process_dict.update(bpmn_process.properties_json) - print(f"bpmn_process_dict: {bpmn_process_dict}") tasks = TaskModel.query.filter_by(bpmn_process_id=bpmn_process.id).all() for task in tasks: json_data = JsonDataModel.query.filter_by(hash=task.json_data_hash).first() From 2ef0bf00657219faed3f6c29dbb5a9d91adc475d Mon Sep 17 00:00:00 2001 From: jasquat Date: Fri, 10 Mar 2023 17:15:27 -0500 Subject: [PATCH 21/48] added loop to task test bpmn file w/ burnettk --- spiffworkflow-backend/poetry.lock | 4264 ++++++++--------- spiffworkflow-backend/pyproject.toml | 2 +- .../manual_task_with_subprocesses.bpmn | 70 +- 3 files changed, 2189 insertions(+), 2147 deletions(-) diff --git a/spiffworkflow-backend/poetry.lock b/spiffworkflow-backend/poetry.lock index 7ac2c0c3..338f96dd 100644 --- a/spiffworkflow-backend/poetry.lock +++ b/spiffworkflow-backend/poetry.lock @@ -1,5 +1,3 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. - [[package]] name = "alabaster" version = "0.7.12" @@ -7,10 +5,6 @@ description = "A configurable sidebar-enabled Sphinx theme" category = "main" optional = false python-versions = "*" -files = [ - {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, - {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, -] [[package]] name = "alembic" @@ -19,10 +13,6 @@ description = "A database migration tool for SQLAlchemy." category = "main" optional = false python-versions = ">=3.7" -files = [ - {file = "alembic-1.8.1-py3-none-any.whl", hash = "sha256:0a024d7f2de88d738d7395ff866997314c837be6104e90c5724350313dee4da4"}, - {file = "alembic-1.8.1.tar.gz", hash = "sha256:cd0b5e45b14b706426b833f06369b9a6d5ee03f826ec3238723ce8caaf6e5ffa"}, -] [package.dependencies] Mako = "*" @@ -38,10 +28,6 @@ description = "Low-level AMQP client for Python (fork of amqplib)." category = "main" optional = false python-versions = ">=3.6" -files = [ - {file = "amqp-5.1.1-py3-none-any.whl", hash = "sha256:6f0956d2c23d8fa6e7691934d8c3930eadb44972cbbd1a7ae3a520f735d43359"}, - {file = "amqp-5.1.1.tar.gz", hash = "sha256:2c1b13fecc0893e946c65cbd5f36427861cffa4ea2201d8f6fca22e2a373b5e2"}, -] [package.dependencies] vine = ">=5.0.0" @@ -53,10 +39,6 @@ description = "A library for parsing ISO 8601 strings." category = "main" optional = false python-versions = "*" -files = [ - {file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"}, - {file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"}, -] [package.extras] dev = ["black", "coverage", "isort", "pre-commit", "pyenchant", "pylint"] @@ -68,10 +50,6 @@ description = "In-process task scheduler with Cron-like capabilities" category = "main" optional = false python-versions = "!=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" -files = [ - {file = "APScheduler-3.9.1.post1-py2.py3-none-any.whl", hash = "sha256:c8c618241dbb2785ed5a687504b14cb1851d6f7b5a4edf3a51e39cc6a069967a"}, - {file = "APScheduler-3.9.1.post1.tar.gz", hash = "sha256:b2bea0309569da53a7261bfa0ce19c67ddbfe151bda776a6a907579fdbd3eb2a"}, -] [package.dependencies] pytz = "*" @@ -99,10 +77,6 @@ description = "An abstract syntax tree for Python with inference support." category = "main" optional = false python-versions = ">=3.7.2" -files = [ - {file = "astroid-2.13.3-py3-none-any.whl", hash = "sha256:14c1603c41cc61aae731cad1884a073c4645e26f126d13ac8346113c95577f3b"}, - {file = "astroid-2.13.3.tar.gz", hash = "sha256:6afc22718a48a689ca24a97981ad377ba7fb78c133f40335dfd16772f29bcfb1"}, -] [package.dependencies] lazy-object-proxy = ">=1.4.0" @@ -119,10 +93,6 @@ description = "Classes Without Boilerplate" category = "main" optional = false python-versions = ">=3.5" -files = [ - {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, - {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, -] [package.extras] dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] @@ -137,10 +107,6 @@ description = "Internationalization utilities" category = "main" optional = false python-versions = ">=3.6" -files = [ - {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, - {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, -] [package.dependencies] pytz = ">=2015.7" @@ -152,10 +118,6 @@ description = "Security oriented static analyser for python code." category = "dev" optional = false python-versions = ">=3.7" -files = [ - {file = "bandit-1.7.2-py3-none-any.whl", hash = "sha256:e20402cadfd126d85b68ed4c8862959663c8c372dbbb1fca8f8e2c9f55a067ec"}, - {file = "bandit-1.7.2.tar.gz", hash = "sha256:6d11adea0214a43813887bfe71a377b5a9955e4c826c8ffd341b494e3ab25260"}, -] [package.dependencies] colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} @@ -175,29 +137,6 @@ description = "Modern password hashing for your software and your servers" category = "main" optional = false python-versions = ">=3.6" -files = [ - {file = "bcrypt-4.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3"}, - {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2"}, - {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535"}, - {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e"}, - {file = "bcrypt-4.0.1-cp36-abi3-win32.whl", hash = "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab"}, - {file = "bcrypt-4.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9"}, - {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c"}, - {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b"}, - {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d"}, - {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d"}, - {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215"}, - {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c"}, - {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda"}, - {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665"}, - {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71"}, - {file = "bcrypt-4.0.1.tar.gz", hash = "sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd"}, -] [package.extras] tests = ["pytest (>=3.2.1,!=3.3.0)"] @@ -210,10 +149,6 @@ description = "Screen-scraping library" category = "dev" optional = false python-versions = ">=3.6.0" -files = [ - {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"}, - {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"}, -] [package.dependencies] soupsieve = ">1.2" @@ -229,10 +164,6 @@ description = "Python multiprocessing fork with improvements and bugfixes" category = "main" optional = false python-versions = "*" -files = [ - {file = "billiard-3.6.4.0-py3-none-any.whl", hash = "sha256:87103ea78fa6ab4d5c751c4909bcff74617d985de7fa8b672cf8618afd5a875b"}, - {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"}, -] [[package]] name = "black" @@ -241,29 +172,6 @@ description = "The uncompromising code formatter." category = "dev" optional = false python-versions = ">=3.7" -files = [ - {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"}, - {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"}, - {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"}, - {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"}, - {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"}, - {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"}, - {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"}, - {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"}, - {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"}, - {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"}, - {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"}, - {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"}, - {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"}, - {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"}, - {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"}, - {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"}, - {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"}, - {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"}, - {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"}, - {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"}, - {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"}, -] [package.dependencies] click = ">=8.0.0" @@ -286,10 +194,6 @@ description = "Fast, simple object-to-object and broadcast signaling" category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "blinker-1.5-py2.py3-none-any.whl", hash = "sha256:1eb563df6fdbc39eeddc177d953203f99f097e9bf0e2b8f9f3cf18b6ca425e36"}, - {file = "blinker-1.5.tar.gz", hash = "sha256:923e5e2f69c155f2cc42dafbbd70e16e3fde24d2d4aa2ab72fbe386238892462"}, -] [[package]] name = "celery" @@ -298,10 +202,6 @@ description = "Distributed Task Queue." category = "main" optional = false python-versions = ">=3.7" -files = [ - {file = "celery-5.2.7-py3-none-any.whl", hash = "sha256:138420c020cd58d6707e6257b6beda91fd39af7afde5d36c6334d175302c0e14"}, - {file = "celery-5.2.7.tar.gz", hash = "sha256:fafbd82934d30f8a004f81e8f7a062e31413a23d444be8ee3326553915958c6d"}, -] [package.dependencies] billiard = ">=3.6.4.0,<4.0" @@ -353,10 +253,6 @@ description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false python-versions = ">=3.6" -files = [ - {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, - {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, -] [[package]] name = "cfgv" @@ -365,10 +261,6 @@ description = "Validate configuration and produce human readable error messages. category = "dev" optional = false python-versions = ">=3.6.1" -files = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, -] [[package]] name = "charset-normalizer" @@ -377,10 +269,6 @@ description = "The Real First Universal Charset Detector. Open, modern and activ category = "main" optional = false python-versions = ">=3.6.0" -files = [ - {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, - {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, -] [package.extras] unicode-backport = ["unicodedata2"] @@ -392,10 +280,6 @@ description = "Utilities for refactoring imports in python-like syntax." category = "dev" optional = false python-versions = ">=3.7" -files = [ - {file = "classify_imports-4.2.0-py2.py3-none-any.whl", hash = "sha256:dbbc264b70a470ed8c6c95976a11dfb8b7f63df44ed1af87328bbed2663f5161"}, - {file = "classify_imports-4.2.0.tar.gz", hash = "sha256:7abfb7ea92149b29d046bd34573d247ba6e68cc28100c801eba4af17964fc40e"}, -] [[package]] name = "click" @@ -404,10 +288,6 @@ description = "Composable command line interface toolkit" category = "main" optional = false python-versions = ">=3.7" -files = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} @@ -419,10 +299,6 @@ description = "Enables git-like *did-you-mean* feature in click" category = "main" optional = false python-versions = ">=3.6.2,<4.0.0" -files = [ - {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"}, - {file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"}, -] [package.dependencies] click = ">=7" @@ -434,10 +310,6 @@ description = "An extension module for click to enable registering CLI commands category = "main" optional = false python-versions = "*" -files = [ - {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, - {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, -] [package.dependencies] click = ">=4.0" @@ -452,10 +324,6 @@ description = "REPL plugin for Click" category = "main" optional = false python-versions = "*" -files = [ - {file = "click-repl-0.2.0.tar.gz", hash = "sha256:cd12f68d745bf6151210790540b4cb064c7b13e571bc64b6957d98d120dacfd8"}, - {file = "click_repl-0.2.0-py3-none-any.whl", hash = "sha256:94b3fbbc9406a236f176e0506524b2937e4b23b6f4c0c0b2a0a83f8a64e9194b"}, -] [package.dependencies] click = "*" @@ -469,10 +337,6 @@ description = "Click utility functions" category = "main" optional = false python-versions = "*" -files = [ - {file = "clickclick-20.10.2-py2.py3-none-any.whl", hash = "sha256:c8f33e6d9ec83f68416dd2136a7950125bd256ec39ccc9a85c6e280a16be2bb5"}, - {file = "clickclick-20.10.2.tar.gz", hash = "sha256:4efb13e62353e34c5eef7ed6582c4920b418d7dedc86d819e22ee089ba01802c"}, -] [package.dependencies] click = ">=4.0" @@ -485,10 +349,6 @@ description = "Cross-platform colored terminal text." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, - {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, -] [[package]] name = "configparser" @@ -497,10 +357,6 @@ description = "Updated configparser from stdlib for earlier Pythons." category = "main" optional = false python-versions = ">=3.7" -files = [ - {file = "configparser-5.3.0-py3-none-any.whl", hash = "sha256:b065779fd93c6bf4cee42202fa4351b4bb842e96a3fb469440e484517a49b9fa"}, - {file = "configparser-5.3.0.tar.gz", hash = "sha256:8be267824b541c09b08db124917f48ab525a6c3e837011f3130781a224c57090"}, -] [package.extras] docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"] @@ -513,10 +369,6 @@ description = "Connexion - API first applications with OpenAPI/Swagger and Flask category = "main" optional = false python-versions = ">=3.6" -files = [ - {file = "connexion-2.14.1-py2.py3-none-any.whl", hash = "sha256:f343717241b4c4802a694c38fee66fb1693c897fe4ea5a957fa9b3b07caf6394"}, - {file = "connexion-2.14.1.tar.gz", hash = "sha256:99aa5781e70a7b94f8ffae8cf89f309d49cdb811bbd65a8e2f2546f3b19a01e6"}, -] [package.dependencies] clickclick = ">=1.2,<21" @@ -544,7 +396,1994 @@ description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=3.7" -files = [ + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "darglint" +version = "1.8.1" +description = "A utility for ensuring Google-style docstrings stay up to date with the source code." +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" + +[[package]] +name = "dateparser" +version = "1.1.2" +description = "Date parsing library designed to parse dates from HTML pages" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +python-dateutil = "*" +pytz = "*" +regex = "<2019.02.19 || >2019.02.19,<2021.8.27 || >2021.8.27,<2022.3.15" +tzlocal = "*" + +[package.extras] +calendars = ["convertdate", "convertdate", "hijri-converter"] +fasttext = ["fasttext"] +langdetect = ["langdetect"] + +[[package]] +name = "dill" +version = "0.3.6" +description = "serialize all of python" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +graph = ["objgraph (>=1.7.2)"] + +[[package]] +name = "distlib" +version = "0.3.6" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "docutils" +version = "0.19" +description = "Docutils -- Python Documentation Utilities" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "dparse" +version = "0.6.2" +description = "A parser for Python dependency files" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +packaging = "*" +toml = "*" + +[package.extras] +conda = ["pyyaml"] +pipenv = ["pipenv"] + +[[package]] +name = "exceptiongroup" +version = "1.0.4" +description = "Backport of PEP 654 (exception groups)" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "filelock" +version = "3.8.0" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"] +testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "flake8" +version = "4.0.1" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.8.0,<2.9.0" +pyflakes = ">=2.4.0,<2.5.0" + +[[package]] +name = "flake8-bandit" +version = "2.1.2" +description = "Automated security testing with bandit and flake8." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +bandit = "*" +flake8 = "*" +flake8-polyfill = "*" +pycodestyle = "*" + +[[package]] +name = "flake8-bugbear" +version = "22.10.25" +description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +attrs = ">=19.2.0" +flake8 = ">=3.0.0" + +[package.extras] +dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "tox"] + +[[package]] +name = "flake8-docstrings" +version = "1.6.0" +description = "Extension for flake8 which uses pydocstyle to check docstrings" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +flake8 = ">=3" +pydocstyle = ">=2.1" + +[[package]] +name = "flake8-polyfill" +version = "1.0.2" +description = "Polyfill package for Flake8 plugins" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +flake8 = "*" + +[[package]] +name = "flake8-rst-docstrings" +version = "0.2.7" +description = "Python docstring reStructuredText (RST) validator" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +flake8 = ">=3.0.0" +pygments = "*" +restructuredtext-lint = "*" + +[[package]] +name = "Flask" +version = "2.2.2" +description = "A simple framework for building complex web applications." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +click = ">=8.0" +importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} +itsdangerous = ">=2.0" +Jinja2 = ">=3.0" +Werkzeug = ">=2.2.2" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "Flask-Admin" +version = "1.6.0" +description = "Simple and extensible admin interface framework for Flask" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +Flask = ">=0.7" +wtforms = "*" + +[package.extras] +aws = ["boto"] +azure = ["azure-storage-blob"] + +[[package]] +name = "Flask-Bcrypt" +version = "1.0.1" +description = "Brcrypt hashing for Flask." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +bcrypt = ">=3.1.1" +Flask = "*" + +[[package]] +name = "flask-bpmn" +version = "0.0.0" +description = "Flask Bpmn" +category = "main" +optional = false +python-versions = "^3.7" +develop = false + +[package.dependencies] +click = "^8.0.1" +flask = "*" +flask-admin = "*" +flask-bcrypt = "*" +flask-cors = "*" +flask-mail = "*" +flask-marshmallow = "*" +flask-migrate = "*" +flask-restful = "*" +greenlet = "^2.0.1" +sentry-sdk = "*" +sphinx-autoapi = "^2.0.0" +spiffworkflow = "*" +werkzeug = "*" + +[package.source] +type = "git" +url = "https://github.com/sartography/flask-bpmn" +reference = "main" +resolved_reference = "c18306300f4312b8d36e0197fd6b62399180d0b1" + +[[package]] +name = "Flask-Cors" +version = "3.0.10" +description = "A Flask extension adding a decorator for CORS support" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +Flask = ">=0.9" +Six = "*" + +[[package]] +name = "flask-jwt-extended" +version = "4.4.4" +description = "Extended JWT integration with Flask" +category = "main" +optional = false +python-versions = ">=3.7,<4" + +[package.dependencies] +Flask = ">=2.0,<3.0" +PyJWT = ">=2.0,<3.0" +Werkzeug = ">=0.14" + +[package.extras] +asymmetric-crypto = ["cryptography (>=3.3.1)"] + +[[package]] +name = "Flask-Mail" +version = "0.9.1" +description = "Flask extension for sending email" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +blinker = "*" +Flask = "*" + +[[package]] +name = "flask-marshmallow" +version = "0.14.0" +description = "Flask + marshmallow for beautiful APIs" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +Flask = "*" +marshmallow = ">=2.0.0" +six = ">=1.9.0" + +[package.extras] +dev = ["flake8 (==3.8.3)", "flake8-bugbear (==20.1.4)", "flask-sqlalchemy", "marshmallow-sqlalchemy (>=0.13.0)", "marshmallow-sqlalchemy (>=0.13.0,<0.19.0)", "mock", "pre-commit (>=2.4,<3.0)", "pytest", "tox"] +docs = ["Sphinx (==3.2.1)", "marshmallow-sqlalchemy (>=0.13.0)", "sphinx-issues (==1.2.0)"] +lint = ["flake8 (==3.8.3)", "flake8-bugbear (==20.1.4)", "pre-commit (>=2.4,<3.0)"] +sqlalchemy = ["flask-sqlalchemy", "marshmallow-sqlalchemy (>=0.13.0)", "marshmallow-sqlalchemy (>=0.13.0,<0.19.0)"] +tests = ["flask-sqlalchemy", "marshmallow-sqlalchemy (>=0.13.0)", "marshmallow-sqlalchemy (>=0.13.0,<0.19.0)", "mock", "pytest"] + +[[package]] +name = "Flask-Migrate" +version = "3.1.0" +description = "SQLAlchemy database migrations for Flask applications using Alembic." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +alembic = ">=0.7" +Flask = ">=0.9" +Flask-SQLAlchemy = ">=1.0" + +[[package]] +name = "Flask-RESTful" +version = "0.3.9" +description = "Simple framework for creating REST APIs" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +aniso8601 = ">=0.82" +Flask = ">=0.8" +pytz = "*" +six = ">=1.3.0" + +[package.extras] +docs = ["sphinx"] + +[[package]] +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" + +[package.dependencies] +Flask = "*" +pycryptodome = "*" + +[[package]] +name = "flask-sqlalchemy" +version = "3.0.2" +description = "Add SQLAlchemy support to your Flask application." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +Flask = ">=2.2" +SQLAlchemy = ">=1.4.18" + +[[package]] +name = "furo" +version = "2022.9.29" +description = "A clean customisable Sphinx documentation theme." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +beautifulsoup4 = "*" +pygments = ">=2.7" +sphinx = ">=4.0,<6.0" +sphinx-basic-ng = "*" + +[[package]] +name = "gitdb" +version = "4.0.9" +description = "Git Object Database" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +smmap = ">=3.0.1,<6" + +[[package]] +name = "GitPython" +version = "3.1.29" +description = "GitPython is a python library used to interact with Git repositories" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +gitdb = ">=4.0.1,<5" + +[[package]] +name = "greenlet" +version = "2.0.1" +description = "Lightweight in-process concurrent programming" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" + +[package.extras] +docs = ["Sphinx", "docutils (<0.18)"] +test = ["faulthandler", "objgraph", "psutil"] + +[[package]] +name = "gunicorn" +version = "20.1.0" +description = "WSGI HTTP Server for UNIX" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +setuptools = ">=3.0" + +[package.extras] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "identify" +version = "2.5.6" +description = "File identification library for Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "importlib-metadata" +version = "4.13.0" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + +[[package]] +name = "inflection" +version = "0.5.1" +description = "A port of Ruby on Rails inflector to Python" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "isort" +version = "5.11.4" +description = "A Python utility / library to sort Python imports." +category = "main" +optional = false +python-versions = ">=3.7.0" + +[package.extras] +colors = ["colorama (>=0.4.3,<0.5.0)"] +pipfile-deprecated-finder = ["pipreqs", "requirementslib"] +plugins = ["setuptools"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] + +[[package]] +name = "itsdangerous" +version = "2.1.2" +description = "Safely pass data to untrusted environments and back." +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "Jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonschema" +version = "4.16.0" +description = "An implementation of JSON Schema validation for Python" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "kombu" +version = "5.2.4" +description = "Messaging library for Python." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +amqp = ">=5.0.9,<6.0.0" +vine = "*" + +[package.extras] +azureservicebus = ["azure-servicebus (>=7.0.0)"] +azurestoragequeues = ["azure-storage-queue"] +consul = ["python-consul (>=0.6.0)"] +librabbitmq = ["librabbitmq (>=2.0.0)"] +mongodb = ["pymongo (>=3.3.0,<3.12.1)"] +msgpack = ["msgpack"] +pyro = ["pyro4"] +qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] +redis = ["redis (>=3.4.1,!=4.0.0,!=4.0.1)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +sqlalchemy = ["sqlalchemy"] +sqs = ["boto3 (>=1.9.12)", "pycurl (>=7.44.1,<7.45.0)", "urllib3 (>=1.26.7)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=1.3.1)"] + +[[package]] +name = "lazy-object-proxy" +version = "1.7.1" +description = "A fast and thorough lazy object proxy." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "livereload" +version = "2.6.3" +description = "Python LiveReload is an awesome tool for web developers" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +six = "*" +tornado = {version = "*", markers = "python_version > \"2.7\""} + +[[package]] +name = "lxml" +version = "4.9.1" +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.*" + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html5 = ["html5lib"] +htmlsoup = ["BeautifulSoup4"] +source = ["Cython (>=0.29.7)"] + +[[package]] +name = "Mako" +version = "1.2.3" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "MarkupSafe" +version = "2.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "marshmallow" +version = "3.18.0" +description = "A lightweight library for converting complex datatypes to and from native Python datatypes." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +packaging = ">=17.0" + +[package.extras] +dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"] +docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.1.1)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] +lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)"] +tests = ["pytest", "pytz", "simplejson"] + +[[package]] +name = "marshmallow-enum" +version = "1.5.1" +description = "Enum field for Marshmallow" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +marshmallow = ">=2.0.0" + +[[package]] +name = "marshmallow-sqlalchemy" +version = "0.28.1" +description = "SQLAlchemy integration with the marshmallow (de)serialization library" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +marshmallow = ">=3.0.0" +packaging = ">=21.3" +SQLAlchemy = ">=1.3.0" + +[package.extras] +dev = ["flake8 (==4.0.1)", "flake8-bugbear (==22.7.1)", "pre-commit (>=2.0,<3.0)", "pytest", "pytest-lazy-fixture (>=0.6.2)", "tox"] +docs = ["alabaster (==0.7.12)", "sphinx (==4.4.0)", "sphinx-issues (==3.0.1)"] +lint = ["flake8 (==4.0.1)", "flake8-bugbear (==22.7.1)", "pre-commit (>=2.0,<3.0)"] +tests = ["pytest", "pytest-lazy-fixture (>=0.6.2)"] + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "mypy" +version = "0.982" +description = "Optional static typing for Python" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +mypy-extensions = ">=0.4.3" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "mysql-connector-python" +version = "8.0.32" +description = "MySQL driver written in Python" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +protobuf = ">=3.11.0,<=3.20.3" + +[package.extras] +compression = ["lz4 (>=2.1.6,<=3.1.3)", "zstandard (>=0.12.0,<=0.19.0)"] +dns-srv = ["dnspython (>=1.16.0,<=2.1.0)"] +gssapi = ["gssapi (>=1.6.9,<=1.8.2)"] + +[[package]] +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.*" + +[package.dependencies] +setuptools = "*" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pathspec" +version = "0.10.1" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "pbr" +version = "5.10.0" +description = "Python Build Reasonableness" +category = "dev" +optional = false +python-versions = ">=2.6" + +[[package]] +name = "pep8-naming" +version = "0.13.2" +description = "Check PEP-8 naming conventions, plugin for flake8" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +flake8 = ">=3.9.1" + +[[package]] +name = "platformdirs" +version = "2.5.2" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "2.20.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +toml = "*" +virtualenv = ">=20.0.8" + +[[package]] +name = "pre-commit-hooks" +version = "4.3.0" +description = "Some out-of-the-box hooks for pre-commit." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +"ruamel.yaml" = ">=0.15" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "prompt-toolkit" +version = "3.0.31" +description = "Library for building powerful interactive command lines in Python" +category = "main" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "protobuf" +version = "3.20.3" +description = "Protocol Buffers" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "psycopg2" +version = "2.9.4" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "pycodestyle" +version = "2.8.0" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +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.*" + +[[package]] +name = "pydocstyle" +version = "6.1.1" +description = "Python docstring style checker" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +snowballstemmer = "*" + +[package.extras] +toml = ["toml"] + +[[package]] +name = "pyflakes" +version = "2.4.0" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "Pygments" +version = "2.13.0" +description = "Pygments is a syntax highlighting package written in Python." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pyjwt" +version = "2.6.0" +description = "JSON Web Token implementation in Python" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] + +[[package]] +name = "pylint" +version = "2.15.10" +description = "python code static checker" +category = "main" +optional = false +python-versions = ">=3.7.2" + +[package.dependencies] +astroid = ">=2.12.13,<=2.14.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, +] +isort = ">=4.2.5,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +tomlkit = ">=0.10.1" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +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" + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pyrsistent" +version = "0.18.1" +description = "Persistent/Functional/Immutable data structures" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "pytest" +version = "7.2.0" +description = "pytest: simple powerful testing with Python" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +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" + +[package.dependencies] +Flask = "*" +pytest = ">=5.2" +Werkzeug = ">=0.7" + +[package.extras] +docs = ["Sphinx", "sphinx-rtd-theme"] + +[[package]] +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 = "*" + +[package.dependencies] +Flask-SQLAlchemy = ">=2.3" +packaging = ">=14.1" +pytest = ">=3.2.1" +pytest-mock = ">=1.6.2" +SQLAlchemy = ">=1.2.2" + +[package.extras] +tests = ["psycopg2-binary", "pytest (>=6.0.1)", "pytest-postgresql (>=2.4.0,<4.0.0)"] + +[[package]] +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" + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +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" + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2022.7.1" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" + +[[package]] +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" + +[package.dependencies] +tzdata = {version = "*", markers = "python_version >= \"3.6\""} + +[[package]] +name = "pyupgrade" +version = "3.1.0" +description = "A tool to automatically upgrade syntax for newer versions." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +tokenize-rt = ">=3.2.0" + +[[package]] +name = "PyYAML" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "regex" +version = "2022.3.2" +description = "Alternative regular expression module, to replace re." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "reorder-python-imports" +version = "3.9.0" +description = "Tool for reordering python imports" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +classify-imports = ">=4.1" + +[[package]] +name = "requests" +version = "2.28.1" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=3.7, <4" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<3" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "restrictedpython" +version = "6.0" +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" + +[package.extras] +docs = ["Sphinx", "sphinx-rtd-theme"] +test = ["pytest", "pytest-mock"] + +[[package]] +name = "restructuredtext-lint" +version = "1.4.0" +description = "reStructuredText linter" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +docutils = ">=0.11,<1.0" + +[[package]] +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" + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} + +[package.extras] +docs = ["ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +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" + +[[package]] +name = "safety" +version = "2.3.1" +description = "Checks installed dependencies for known vulnerabilities and licenses." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +Click = ">=8.0.2" +dparse = ">=0.6.2" +packaging = ">=21.0" +requests = "*" +"ruamel.yaml" = ">=0.17.21" +setuptools = ">=19.3" + +[package.extras] +github = ["jinja2 (>=3.1.0)", "pygithub (>=1.43.3)"] +gitlab = ["python-gitlab (>=1.3.0)"] + +[[package]] +name = "sentry-sdk" +version = "1.16.0" +description = "Python client for Sentry (https://sentry.io)" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +certifi = "*" +urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} + +[package.extras] +aiohttp = ["aiohttp (>=3.5)"] +arq = ["arq (>=0.23)"] +beam = ["apache-beam (>=2.12)"] +bottle = ["bottle (>=0.12.13)"] +celery = ["celery (>=3)"] +chalice = ["chalice (>=1.16.0)"] +django = ["django (>=1.8)"] +falcon = ["falcon (>=1.4)"] +fastapi = ["fastapi (>=0.79.0)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)"] +httpx = ["httpx (>=0.16.0)"] +huey = ["huey (>=2)"] +opentelemetry = ["opentelemetry-distro (>=0.35b0)"] +pure-eval = ["asttokens", "executing", "pure-eval"] +pymongo = ["pymongo (>=3.1)"] +pyspark = ["pyspark (>=2.4.4)"] +quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] +rq = ["rq (>=0.6)"] +sanic = ["sanic (>=0.8)"] +sqlalchemy = ["sqlalchemy (>=1.2)"] +starlette = ["starlette (>=0.19.1)"] +starlite = ["starlite (>=1.48)"] +tornado = ["tornado (>=5)"] + +[[package]] +name = "setuptools" +version = "65.5.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "simplejson" +version = "3.17.6" +description = "Simple, fast, extensible JSON encoder/decoder for Python" +category = "main" +optional = false +python-versions = ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +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.*" + +[[package]] +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" + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "soupsieve" +version = "2.3.2.post1" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "Sphinx" +version = "5.3.0" +description = "Python documentation generator" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +alabaster = ">=0.7,<0.8" +babel = ">=2.9" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.14,<0.20" +imagesize = ">=1.3" +importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} +Jinja2 = ">=3.0" +packaging = ">=21.0" +Pygments = ">=2.12" +requests = ">=2.5.0" +snowballstemmer = ">=2.0" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.5" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "flake8-simplify", "isort", "mypy (>=0.981)", "sphinx-lint", "types-requests", "types-typed-ast"] +test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"] + +[[package]] +name = "sphinx-autoapi" +version = "2.0.0" +description = "Sphinx API documentation generator" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +astroid = ">=2.7" +Jinja2 = "*" +PyYAML = "*" +sphinx = ">=4.0" +unidecode = "*" + +[package.extras] +docs = ["sphinx", "sphinx-rtd-theme"] +dotnet = ["sphinxcontrib-dotnetdomain"] +go = ["sphinxcontrib-golangdomain"] + +[[package]] +name = "sphinx-autobuild" +version = "2021.3.14" +description = "Rebuild Sphinx documentation on changes, with live-reload in the browser." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +colorama = "*" +livereload = "*" +sphinx = "*" + +[package.extras] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "sphinx-basic-ng" +version = "1.0.0b1" +description = "A modern skeleton for Sphinx themes." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +sphinx = ">=4.0" + +[package.extras] +docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] + +[[package]] +name = "sphinx-click" +version = "4.3.0" +description = "Sphinx extension that automatically documents click applications" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +click = ">=7.0" +docutils = "*" +sphinx = ">=2.0" + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.2" +description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.2" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.0" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.3" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.5" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "SpiffWorkflow" +version = "1.2.1" +description = "A workflow framework and BPMN/DMN Processor" +category = "main" +optional = false +python-versions = "*" +develop = false + +[package.dependencies] +celery = "*" +configparser = "*" +lxml = "*" + +[package.source] +type = "git" +url = "https://github.com/sartography/SpiffWorkflow" +reference = "feature/remove-loop-reset" +resolved_reference = "13034aaf12f62aa3914744ca05bc9a3e3b3c3452" + +[[package]] +name = "SQLAlchemy" +version = "1.4.42" +description = "Database Abstraction Library" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (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\")"} + +[package.extras] +aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"] +mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +pymysql = ["pymysql", "pymysql (<1)"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "sqlalchemy-stubs" +version = "0.4" +description = "SQLAlchemy stubs and mypy plugin" +category = "main" +optional = false +python-versions = "*" +develop = false + +[package.dependencies] +mypy = ">=0.790" +typing-extensions = ">=3.7.4" + +[package.source] +type = "git" +url = "https://github.com/burnettk/sqlalchemy-stubs.git" +reference = "scoped-session-delete" +resolved_reference = "d1176931684ce5b327539cc9567d4a1cd8ef1efd" + +[[package]] +name = "stevedore" +version = "4.0.1" +description = "Manage dynamic plugins for Python applications" +category = "dev" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +pbr = ">=2.0.0,<2.1.0 || >2.1.0" + +[[package]] +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 = "*" + +[package.dependencies] +Jinja2 = ">=2.0" + +[[package]] +name = "tokenize-rt" +version = "4.2.1" +description = "A wrapper around the stdlib `tokenize` which roundtrips." +category = "dev" +optional = false +python-versions = ">=3.6.1" + +[[package]] +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.*" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "tomlkit" +version = "0.11.6" +description = "Style preserving TOML library" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "tornado" +version = "6.2" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +category = "dev" +optional = false +python-versions = ">= 3.7" + +[[package]] +name = "typeguard" +version = "2.13.3" +description = "Run-time type checker for Python" +category = "dev" +optional = false +python-versions = ">=3.5.3" + +[package.extras] +doc = ["sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["mypy", "pytest", "typing-extensions"] + +[[package]] +name = "types-click" +version = "7.1.8" +description = "Typing stubs for click" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "types-dateparser" +version = "1.1.4.1" +description = "Typing stubs for dateparser" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "types-Flask" +version = "1.1.6" +description = "Typing stubs for Flask" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +types-click = "*" +types-Jinja2 = "*" +types-Werkzeug = "*" + +[[package]] +name = "types-Jinja2" +version = "2.11.9" +description = "Typing stubs for Jinja2" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +types-MarkupSafe = "*" + +[[package]] +name = "types-MarkupSafe" +version = "1.1.10" +description = "Typing stubs for MarkupSafe" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "types-pytz" +version = "2022.5.0.0" +description = "Typing stubs for pytz" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "types-PyYAML" +version = "6.0.12" +description = "Typing stubs for PyYAML" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "types-requests" +version = "2.28.11.2" +description = "Typing stubs for requests" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +types-urllib3 = "<1.27" + +[[package]] +name = "types-urllib3" +version = "1.26.25.1" +description = "Typing stubs for urllib3" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "types-Werkzeug" +version = "1.0.9" +description = "Typing stubs for Werkzeug" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "typing-extensions" +version = "4.4.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "tzdata" +version = "2022.5" +description = "Provider of IANA time zone data" +category = "main" +optional = false +python-versions = ">=2" + +[[package]] +name = "tzlocal" +version = "4.2" +description = "tzinfo object for the local timezone" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pytz-deprecation-shim = "*" +tzdata = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +devenv = ["black", "pyroma", "pytest-cov", "zest.releaser"] +test = ["pytest (>=4.3)", "pytest-mock (>=3.3)"] + +[[package]] +name = "Unidecode" +version = "1.3.6" +description = "ASCII transliterations of Unicode text" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "urllib3" +version = "1.26.12" +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.*, <4" + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "vine" +version = "5.0.0" +description = "Promises, promises, promises." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "virtualenv" +version = "20.16.5" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +distlib = ">=0.3.5,<1" +filelock = ">=3.4.1,<4" +platformdirs = ">=2.4,<3" + +[package.extras] +docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"] +testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "Werkzeug" +version = "2.2.2" +description = "The comprehensive WSGI web application library." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog"] + +[[package]] +name = "wrapt" +version = "1.14.1" +description = "Module for decorators, wrappers and monkey patching." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[[package]] +name = "WTForms" +version = "3.0.1" +description = "Form validation and rendering for Python web development." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +MarkupSafe = "*" + +[package.extras] +email = ["email-validator"] + +[[package]] +name = "xdoctest" +version = "1.1.0" +description = "A rewrite of the builtin doctest module" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +colorama = {version = "*", optional = true, markers = "platform_system == \"Windows\" and extra == \"colors\""} +Pygments = {version = "*", optional = true, markers = "python_version >= \"3.5.0\" and extra == \"colors\""} +six = "*" + +[package.extras] +all = ["IPython", "IPython", "Pygments", "Pygments", "attrs", "cmake", "codecov", "colorama", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert", "ninja", "pybind11", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest-cov", "pytest-cov", "pytest-cov", "pytest-cov", "scikit-build", "six", "typing"] +all-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "cmake (==3.21.2)", "codecov (==2.0.15)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==5.2.0)", "ipykernel (==6.0.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==6.1.5)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "ninja (==1.10.2)", "pybind11 (==2.7.1)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "scikit-build (==0.11.1)", "six (==1.11.0)", "typing (==3.7.4)"] +colors = ["Pygments", "Pygments", "colorama"] +jupyter = ["IPython", "IPython", "attrs", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert"] +optional = ["IPython", "IPython", "Pygments", "Pygments", "attrs", "colorama", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert", "pyflakes", "tomli"] +optional-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==5.2.0)", "ipykernel (==6.0.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==6.1.5)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "pyflakes (==2.2.0)", "tomli (==0.2.0)"] +runtime-strict = ["six (==1.11.0)"] +tests = ["cmake", "codecov", "ninja", "pybind11", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest-cov", "pytest-cov", "pytest-cov", "pytest-cov", "scikit-build", "typing"] +tests-strict = ["cmake (==3.21.2)", "codecov (==2.0.15)", "ninja (==1.10.2)", "pybind11 (==2.7.1)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "scikit-build (==0.11.1)", "typing (==3.7.4)"] + +[[package]] +name = "zipp" +version = "3.10.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "1.1" +python-versions = ">=3.9,<3.12" +content-hash = "eac3b5aa78efea376a9e23e32f9e6853cc22c17a2a21b41e30800cb7c807d017" + +[metadata.files] +alabaster = [ + {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, + {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, +] +alembic = [ + {file = "alembic-1.8.1-py3-none-any.whl", hash = "sha256:0a024d7f2de88d738d7395ff866997314c837be6104e90c5724350313dee4da4"}, + {file = "alembic-1.8.1.tar.gz", hash = "sha256:cd0b5e45b14b706426b833f06369b9a6d5ee03f826ec3238723ce8caaf6e5ffa"}, +] +amqp = [ + {file = "amqp-5.1.1-py3-none-any.whl", hash = "sha256:6f0956d2c23d8fa6e7691934d8c3930eadb44972cbbd1a7ae3a520f735d43359"}, + {file = "amqp-5.1.1.tar.gz", hash = "sha256:2c1b13fecc0893e946c65cbd5f36427861cffa4ea2201d8f6fca22e2a373b5e2"}, +] +aniso8601 = [ + {file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"}, + {file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"}, +] +apscheduler = [ + {file = "APScheduler-3.9.1.post1-py2.py3-none-any.whl", hash = "sha256:c8c618241dbb2785ed5a687504b14cb1851d6f7b5a4edf3a51e39cc6a069967a"}, + {file = "APScheduler-3.9.1.post1.tar.gz", hash = "sha256:b2bea0309569da53a7261bfa0ce19c67ddbfe151bda776a6a907579fdbd3eb2a"}, +] +astroid = [ + {file = "astroid-2.13.3-py3-none-any.whl", hash = "sha256:14c1603c41cc61aae731cad1884a073c4645e26f126d13ac8346113c95577f3b"}, + {file = "astroid-2.13.3.tar.gz", hash = "sha256:6afc22718a48a689ca24a97981ad377ba7fb78c133f40335dfd16772f29bcfb1"}, +] +attrs = [ + {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, + {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, +] +Babel = [ + {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, + {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, +] +bandit = [ + {file = "bandit-1.7.2-py3-none-any.whl", hash = "sha256:e20402cadfd126d85b68ed4c8862959663c8c372dbbb1fca8f8e2c9f55a067ec"}, + {file = "bandit-1.7.2.tar.gz", hash = "sha256:6d11adea0214a43813887bfe71a377b5a9955e4c826c8ffd341b494e3ab25260"}, +] +bcrypt = [ + {file = "bcrypt-4.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3"}, + {file = "bcrypt-4.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2"}, + {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535"}, + {file = "bcrypt-4.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e"}, + {file = "bcrypt-4.0.1-cp36-abi3-win32.whl", hash = "sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab"}, + {file = "bcrypt-4.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b"}, + {file = "bcrypt-4.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215"}, + {file = "bcrypt-4.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665"}, + {file = "bcrypt-4.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71"}, + {file = "bcrypt-4.0.1.tar.gz", hash = "sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd"}, +] +beautifulsoup4 = [ + {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"}, + {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"}, +] +billiard = [ + {file = "billiard-3.6.4.0-py3-none-any.whl", hash = "sha256:87103ea78fa6ab4d5c751c4909bcff74617d985de7fa8b672cf8618afd5a875b"}, + {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"}, +] +black = [ + {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"}, + {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"}, + {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"}, + {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"}, + {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"}, + {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"}, + {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"}, + {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"}, + {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"}, + {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"}, + {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"}, + {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"}, + {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"}, + {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"}, + {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"}, + {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"}, + {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"}, + {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"}, + {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"}, + {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"}, + {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"}, +] +blinker = [ + {file = "blinker-1.5-py2.py3-none-any.whl", hash = "sha256:1eb563df6fdbc39eeddc177d953203f99f097e9bf0e2b8f9f3cf18b6ca425e36"}, + {file = "blinker-1.5.tar.gz", hash = "sha256:923e5e2f69c155f2cc42dafbbd70e16e3fde24d2d4aa2ab72fbe386238892462"}, +] +celery = [ + {file = "celery-5.2.7-py3-none-any.whl", hash = "sha256:138420c020cd58d6707e6257b6beda91fd39af7afde5d36c6334d175302c0e14"}, + {file = "celery-5.2.7.tar.gz", hash = "sha256:fafbd82934d30f8a004f81e8f7a062e31413a23d444be8ee3326553915958c6d"}, +] +certifi = [ + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, +] +cfgv = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, + {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, +] +classify-imports = [ + {file = "classify_imports-4.2.0-py2.py3-none-any.whl", hash = "sha256:dbbc264b70a470ed8c6c95976a11dfb8b7f63df44ed1af87328bbed2663f5161"}, + {file = "classify_imports-4.2.0.tar.gz", hash = "sha256:7abfb7ea92149b29d046bd34573d247ba6e68cc28100c801eba4af17964fc40e"}, +] +click = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] +click-didyoumean = [ + {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"}, + {file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"}, +] +click-plugins = [ + {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, + {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, +] +click-repl = [ + {file = "click-repl-0.2.0.tar.gz", hash = "sha256:cd12f68d745bf6151210790540b4cb064c7b13e571bc64b6957d98d120dacfd8"}, + {file = "click_repl-0.2.0-py3-none-any.whl", hash = "sha256:94b3fbbc9406a236f176e0506524b2937e4b23b6f4c0c0b2a0a83f8a64e9194b"}, +] +clickclick = [ + {file = "clickclick-20.10.2-py2.py3-none-any.whl", hash = "sha256:c8f33e6d9ec83f68416dd2136a7950125bd256ec39ccc9a85c6e280a16be2bb5"}, + {file = "clickclick-20.10.2.tar.gz", hash = "sha256:4efb13e62353e34c5eef7ed6582c4920b418d7dedc86d819e22ee089ba01802c"}, +] +colorama = [ + {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, + {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, +] +configparser = [ + {file = "configparser-5.3.0-py3-none-any.whl", hash = "sha256:b065779fd93c6bf4cee42202fa4351b4bb842e96a3fb469440e484517a49b9fa"}, + {file = "configparser-5.3.0.tar.gz", hash = "sha256:8be267824b541c09b08db124917f48ab525a6c3e837011f3130781a224c57090"}, +] +connexion = [ + {file = "connexion-2.14.1-py2.py3-none-any.whl", hash = "sha256:f343717241b4c4802a694c38fee66fb1693c897fe4ea5a957fa9b3b07caf6394"}, + {file = "connexion-2.14.1.tar.gz", hash = "sha256:99aa5781e70a7b94f8ffae8cf89f309d49cdb811bbd65a8e2f2546f3b19a01e6"}, +] +coverage = [ {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"}, {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"}, {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"}, @@ -596,530 +2435,117 @@ files = [ {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, ] - -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "darglint" -version = "1.8.1" -description = "A utility for ensuring Google-style docstrings stay up to date with the source code." -category = "dev" -optional = false -python-versions = ">=3.6,<4.0" -files = [ +darglint = [ {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, ] - -[[package]] -name = "dateparser" -version = "1.1.2" -description = "Date parsing library designed to parse dates from HTML pages" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ +dateparser = [ {file = "dateparser-1.1.2-py2.py3-none-any.whl", hash = "sha256:d31659dc806a7d88e2b510b2c74f68b525ae531f145c62a57a99bd616b7f90cf"}, {file = "dateparser-1.1.2.tar.gz", hash = "sha256:3821bf191f95b2658c4abd91571c09821ce7a2bc179bf6cefd8b4515c3ccf9ef"}, ] - -[package.dependencies] -python-dateutil = "*" -pytz = "*" -regex = "<2019.02.19 || >2019.02.19,<2021.8.27 || >2021.8.27,<2022.3.15" -tzlocal = "*" - -[package.extras] -calendars = ["convertdate", "convertdate", "hijri-converter"] -fasttext = ["fasttext"] -langdetect = ["langdetect"] - -[[package]] -name = "dill" -version = "0.3.6" -description = "serialize all of python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +dill = [ {file = "dill-0.3.6-py3-none-any.whl", hash = "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0"}, {file = "dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"}, ] - -[package.extras] -graph = ["objgraph (>=1.7.2)"] - -[[package]] -name = "distlib" -version = "0.3.6" -description = "Distribution utilities" -category = "dev" -optional = false -python-versions = "*" -files = [ +distlib = [ {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, ] - -[[package]] -name = "docutils" -version = "0.19" -description = "Docutils -- Python Documentation Utilities" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +docutils = [ {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"}, {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"}, ] - -[[package]] -name = "dparse" -version = "0.6.2" -description = "A parser for Python dependency files" -category = "dev" -optional = false -python-versions = ">=3.5" -files = [ +dparse = [ {file = "dparse-0.6.2-py3-none-any.whl", hash = "sha256:8097076f1dd26c377f30d4745e6ec18fef42f3bf493933b842ac5bafad8c345f"}, {file = "dparse-0.6.2.tar.gz", hash = "sha256:d45255bda21f998bc7ddf2afd5e62505ba6134756ba2d42a84c56b0826614dfe"}, ] - -[package.dependencies] -packaging = "*" -toml = "*" - -[package.extras] -conda = ["pyyaml"] -pipenv = ["pipenv"] - -[[package]] -name = "exceptiongroup" -version = "1.0.4" -description = "Backport of PEP 654 (exception groups)" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +exceptiongroup = [ {file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"}, {file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"}, ] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "filelock" -version = "3.8.0" -description = "A platform independent file lock." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ +filelock = [ {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, ] - -[package.extras] -docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"] -testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"] - -[[package]] -name = "flake8" -version = "4.0.1" -description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ +flake8 = [ {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, ] - -[package.dependencies] -mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.8.0,<2.9.0" -pyflakes = ">=2.4.0,<2.5.0" - -[[package]] -name = "flake8-bandit" -version = "2.1.2" -description = "Automated security testing with bandit and flake8." -category = "dev" -optional = false -python-versions = "*" -files = [ +flake8-bandit = [ {file = "flake8_bandit-2.1.2.tar.gz", hash = "sha256:687fc8da2e4a239b206af2e54a90093572a60d0954f3054e23690739b0b0de3b"}, ] - -[package.dependencies] -bandit = "*" -flake8 = "*" -flake8-polyfill = "*" -pycodestyle = "*" - -[[package]] -name = "flake8-bugbear" -version = "22.10.25" -description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ +flake8-bugbear = [ {file = "flake8-bugbear-22.10.25.tar.gz", hash = "sha256:89e51284eb929fbb7f23fbd428491e7427f7cdc8b45a77248daffe86a039d696"}, {file = "flake8_bugbear-22.10.25-py3-none-any.whl", hash = "sha256:584631b608dc0d7d3f9201046d5840a45502da4732d5e8df6c7ac1694a91cb9e"}, ] - -[package.dependencies] -attrs = ">=19.2.0" -flake8 = ">=3.0.0" - -[package.extras] -dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "tox"] - -[[package]] -name = "flake8-docstrings" -version = "1.6.0" -description = "Extension for flake8 which uses pydocstyle to check docstrings" -category = "dev" -optional = false -python-versions = "*" -files = [ +flake8-docstrings = [ {file = "flake8-docstrings-1.6.0.tar.gz", hash = "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"}, {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"}, ] - -[package.dependencies] -flake8 = ">=3" -pydocstyle = ">=2.1" - -[[package]] -name = "flake8-polyfill" -version = "1.0.2" -description = "Polyfill package for Flake8 plugins" -category = "dev" -optional = false -python-versions = "*" -files = [ +flake8-polyfill = [ {file = "flake8-polyfill-1.0.2.tar.gz", hash = "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"}, {file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"}, ] - -[package.dependencies] -flake8 = "*" - -[[package]] -name = "flake8-rst-docstrings" -version = "0.2.7" -description = "Python docstring reStructuredText (RST) validator" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ +flake8-rst-docstrings = [ {file = "flake8-rst-docstrings-0.2.7.tar.gz", hash = "sha256:2740067ab9237559dd45a3434d8c987792c7b259ca563621a3b95efe201f5382"}, {file = "flake8_rst_docstrings-0.2.7-py3-none-any.whl", hash = "sha256:5d56075dce360bcc9c6775bfe7cb431aa395de600ca7e8d40580a28d50b2a803"}, ] - -[package.dependencies] -flake8 = ">=3.0.0" -pygments = "*" -restructuredtext-lint = "*" - -[[package]] -name = "Flask" -version = "2.2.2" -description = "A simple framework for building complex web applications." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +Flask = [ {file = "Flask-2.2.2-py3-none-any.whl", hash = "sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526"}, {file = "Flask-2.2.2.tar.gz", hash = "sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b"}, ] - -[package.dependencies] -click = ">=8.0" -importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} -itsdangerous = ">=2.0" -Jinja2 = ">=3.0" -Werkzeug = ">=2.2.2" - -[package.extras] -async = ["asgiref (>=3.2)"] -dotenv = ["python-dotenv"] - -[[package]] -name = "Flask-Admin" -version = "1.6.0" -description = "Simple and extensible admin interface framework for Flask" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ +Flask-Admin = [ {file = "Flask-Admin-1.6.0.tar.gz", hash = "sha256:424ffc79b7b0dfff051555686ea12e86e48dffacac14beaa319fb4502ac40988"}, ] - -[package.dependencies] -Flask = ">=0.7" -wtforms = "*" - -[package.extras] -aws = ["boto"] -azure = ["azure-storage-blob"] - -[[package]] -name = "Flask-Bcrypt" -version = "1.0.1" -description = "Brcrypt hashing for Flask." -category = "main" -optional = false -python-versions = "*" -files = [ +Flask-Bcrypt = [ {file = "Flask-Bcrypt-1.0.1.tar.gz", hash = "sha256:f07b66b811417ea64eb188ae6455b0b708a793d966e1a80ceec4a23bc42a4369"}, {file = "Flask_Bcrypt-1.0.1-py3-none-any.whl", hash = "sha256:062fd991dc9118d05ac0583675507b9fe4670e44416c97e0e6819d03d01f808a"}, ] - -[package.dependencies] -bcrypt = ">=3.1.1" -Flask = "*" - -[[package]] -name = "flask-bpmn" -version = "0.0.0" -description = "Flask Bpmn" -category = "main" -optional = false -python-versions = "^3.7" -files = [] -develop = false - -[package.dependencies] -click = "^8.0.1" -flask = "*" -flask-admin = "*" -flask-bcrypt = "*" -flask-cors = "*" -flask-mail = "*" -flask-marshmallow = "*" -flask-migrate = "*" -flask-restful = "*" -greenlet = "^2.0.1" -sentry-sdk = "*" -sphinx-autoapi = "^2.0.0" -spiffworkflow = "*" -werkzeug = "*" - -[package.source] -type = "git" -url = "https://github.com/sartography/flask-bpmn" -reference = "main" -resolved_reference = "c18306300f4312b8d36e0197fd6b62399180d0b1" - -[[package]] -name = "Flask-Cors" -version = "3.0.10" -description = "A Flask extension adding a decorator for CORS support" -category = "main" -optional = false -python-versions = "*" -files = [ +flask-bpmn = [] +Flask-Cors = [ {file = "Flask-Cors-3.0.10.tar.gz", hash = "sha256:b60839393f3b84a0f3746f6cdca56c1ad7426aa738b70d6c61375857823181de"}, {file = "Flask_Cors-3.0.10-py2.py3-none-any.whl", hash = "sha256:74efc975af1194fc7891ff5cd85b0f7478be4f7f59fe158102e91abb72bb4438"}, ] - -[package.dependencies] -Flask = ">=0.9" -Six = "*" - -[[package]] -name = "flask-jwt-extended" -version = "4.4.4" -description = "Extended JWT integration with Flask" -category = "main" -optional = false -python-versions = ">=3.7,<4" -files = [ +flask-jwt-extended = [ {file = "Flask-JWT-Extended-4.4.4.tar.gz", hash = "sha256:62b521d75494c290a646ae8acc77123721e4364790f1e64af0038d823961fbf0"}, {file = "Flask_JWT_Extended-4.4.4-py2.py3-none-any.whl", hash = "sha256:a85eebfa17c339a7260c4643475af444784ba6de5588adda67406f0a75599553"}, ] - -[package.dependencies] -Flask = ">=2.0,<3.0" -PyJWT = ">=2.0,<3.0" -Werkzeug = ">=0.14" - -[package.extras] -asymmetric-crypto = ["cryptography (>=3.3.1)"] - -[[package]] -name = "Flask-Mail" -version = "0.9.1" -description = "Flask extension for sending email" -category = "main" -optional = false -python-versions = "*" -files = [ +Flask-Mail = [ {file = "Flask-Mail-0.9.1.tar.gz", hash = "sha256:22e5eb9a940bf407bcf30410ecc3708f3c56cc44b29c34e1726fe85006935f41"}, ] - -[package.dependencies] -blinker = "*" -Flask = "*" - -[[package]] -name = "flask-marshmallow" -version = "0.14.0" -description = "Flask + marshmallow for beautiful APIs" -category = "main" -optional = false -python-versions = "*" -files = [ +flask-marshmallow = [ {file = "flask-marshmallow-0.14.0.tar.gz", hash = "sha256:bd01a6372cbe50e36f205cfff0fc5dab0b7b662c4c8b2c4fc06a3151b2950950"}, {file = "flask_marshmallow-0.14.0-py2.py3-none-any.whl", hash = "sha256:2adcd782b5a4a6c5ae3c96701f320d8ca6997995a52b2661093c56cc3ed24754"}, ] - -[package.dependencies] -Flask = "*" -marshmallow = ">=2.0.0" -six = ">=1.9.0" - -[package.extras] -dev = ["flake8 (==3.8.3)", "flake8-bugbear (==20.1.4)", "flask-sqlalchemy", "marshmallow-sqlalchemy (>=0.13.0)", "marshmallow-sqlalchemy (>=0.13.0,<0.19.0)", "mock", "pre-commit (>=2.4,<3.0)", "pytest", "tox"] -docs = ["Sphinx (==3.2.1)", "marshmallow-sqlalchemy (>=0.13.0)", "sphinx-issues (==1.2.0)"] -lint = ["flake8 (==3.8.3)", "flake8-bugbear (==20.1.4)", "pre-commit (>=2.4,<3.0)"] -sqlalchemy = ["flask-sqlalchemy", "marshmallow-sqlalchemy (>=0.13.0)", "marshmallow-sqlalchemy (>=0.13.0,<0.19.0)"] -tests = ["flask-sqlalchemy", "marshmallow-sqlalchemy (>=0.13.0)", "marshmallow-sqlalchemy (>=0.13.0,<0.19.0)", "mock", "pytest"] - -[[package]] -name = "Flask-Migrate" -version = "3.1.0" -description = "SQLAlchemy database migrations for Flask applications using Alembic." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ +Flask-Migrate = [ {file = "Flask-Migrate-3.1.0.tar.gz", hash = "sha256:57d6060839e3a7f150eaab6fe4e726d9e3e7cffe2150fb223d73f92421c6d1d9"}, {file = "Flask_Migrate-3.1.0-py3-none-any.whl", hash = "sha256:a6498706241aba6be7a251078de9cf166d74307bca41a4ca3e403c9d39e2f897"}, ] - -[package.dependencies] -alembic = ">=0.7" -Flask = ">=0.9" -Flask-SQLAlchemy = ">=1.0" - -[[package]] -name = "Flask-RESTful" -version = "0.3.9" -description = "Simple framework for creating REST APIs" -category = "main" -optional = false -python-versions = "*" -files = [ +Flask-RESTful = [ {file = "Flask-RESTful-0.3.9.tar.gz", hash = "sha256:ccec650b835d48192138c85329ae03735e6ced58e9b2d9c2146d6c84c06fa53e"}, {file = "Flask_RESTful-0.3.9-py2.py3-none-any.whl", hash = "sha256:4970c49b6488e46c520b325f54833374dc2b98e211f1b272bd4b0c516232afe2"}, ] - -[package.dependencies] -aniso8601 = ">=0.82" -Flask = ">=0.8" -pytz = "*" -six = ">=1.3.0" - -[package.extras] -docs = ["sphinx"] - -[[package]] -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 = [ +flask-simple-crypt = [ {file = "Flask-Simple-Crypt-0.3.3.tar.gz", hash = "sha256:0d4033b6c9a03ac85d10f0fd213914390217dc53b2d41d153fa050fee9723594"}, {file = "Flask_Simple_Crypt-0.3.3-py3-none-any.whl", hash = "sha256:08c3fcad955ac148bb885b1de4798c1cfce8512452072beee414bacf1552e8ef"}, ] - -[package.dependencies] -Flask = "*" -pycryptodome = "*" - -[[package]] -name = "flask-sqlalchemy" -version = "3.0.2" -description = "Add SQLAlchemy support to your Flask application." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +flask-sqlalchemy = [ {file = "Flask-SQLAlchemy-3.0.2.tar.gz", hash = "sha256:16199f5b3ddfb69e0df2f52ae4c76aedbfec823462349dabb21a1b2e0a2b65e9"}, {file = "Flask_SQLAlchemy-3.0.2-py3-none-any.whl", hash = "sha256:7d0cd9cf73e64a996bb881a1ebd01633fc5a6d11c36ea27f7b5e251dc45476e7"}, ] - -[package.dependencies] -Flask = ">=2.2" -SQLAlchemy = ">=1.4.18" - -[[package]] -name = "furo" -version = "2022.9.29" -description = "A clean customisable Sphinx documentation theme." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ +furo = [ {file = "furo-2022.9.29-py3-none-any.whl", hash = "sha256:559ee17999c0f52728481dcf6b1b0cf8c9743e68c5e3a18cb45a7992747869a9"}, {file = "furo-2022.9.29.tar.gz", hash = "sha256:d4238145629c623609c2deb5384f8d036e2a1ee2a101d64b67b4348112470dbd"}, ] - -[package.dependencies] -beautifulsoup4 = "*" -pygments = ">=2.7" -sphinx = ">=4.0,<6.0" -sphinx-basic-ng = "*" - -[[package]] -name = "gitdb" -version = "4.0.9" -description = "Git Object Database" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ +gitdb = [ {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, ] - -[package.dependencies] -smmap = ">=3.0.1,<6" - -[[package]] -name = "GitPython" -version = "3.1.29" -description = "GitPython is a python library used to interact with Git repositories" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ +GitPython = [ {file = "GitPython-3.1.29-py3-none-any.whl", hash = "sha256:41eea0deec2deea139b459ac03656f0dd28fc4a3387240ec1d3c259a2c47850f"}, {file = "GitPython-3.1.29.tar.gz", hash = "sha256:cc36bfc4a3f913e66805a28e84703e419d9c264c1077e537b54f0e1af85dbefd"}, ] - -[package.dependencies] -gitdb = ">=4.0.1,<5" - -[[package]] -name = "greenlet" -version = "2.0.1" -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 = [ +greenlet = [ {file = "greenlet-2.0.1-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:9ed358312e63bf683b9ef22c8e442ef6c5c02973f0c2a939ec1d7b50c974015c"}, {file = "greenlet-2.0.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:4f09b0010e55bec3239278f642a8a506b91034f03a4fb28289a7d448a67f1515"}, {file = "greenlet-2.0.1-cp27-cp27m-win32.whl", hash = "sha256:1407fe45246632d0ffb7a3f4a520ba4e6051fc2cbd61ba1f806900c27f47706a"}, @@ -1181,223 +2607,55 @@ files = [ {file = "greenlet-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b23d2a46d53210b498e5b701a1913697671988f4bf8e10f935433f6e7c332fb6"}, {file = "greenlet-2.0.1.tar.gz", hash = "sha256:42e602564460da0e8ee67cb6d7236363ee5e131aa15943b6670e44e5c2ed0f67"}, ] - -[package.extras] -docs = ["Sphinx", "docutils (<0.18)"] -test = ["faulthandler", "objgraph", "psutil"] - -[[package]] -name = "gunicorn" -version = "20.1.0" -description = "WSGI HTTP Server for UNIX" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ +gunicorn = [ {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, ] - -[package.dependencies] -setuptools = ">=3.0" - -[package.extras] -eventlet = ["eventlet (>=0.24.1)"] -gevent = ["gevent (>=1.4.0)"] -setproctitle = ["setproctitle"] -tornado = ["tornado (>=0.2)"] - -[[package]] -name = "identify" -version = "2.5.6" -description = "File identification library for Python" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ +identify = [ {file = "identify-2.5.6-py2.py3-none-any.whl", hash = "sha256:b276db7ec52d7e89f5bc4653380e33054ddc803d25875952ad90b0f012cbcdaa"}, {file = "identify-2.5.6.tar.gz", hash = "sha256:6c32dbd747aa4ceee1df33f25fed0b0f6e0d65721b15bd151307ff7056d50245"}, ] - -[package.extras] -license = ["ukkonen"] - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ +idna = [ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] - -[[package]] -name = "imagesize" -version = "1.4.1" -description = "Getting image size from png/jpeg/jpeg2000/gif file" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ +imagesize = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] - -[[package]] -name = "importlib-metadata" -version = "4.13.0" -description = "Read metadata from Python packages" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +importlib-metadata = [ {file = "importlib_metadata-4.13.0-py3-none-any.whl", hash = "sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116"}, {file = "importlib_metadata-4.13.0.tar.gz", hash = "sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d"}, ] - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] -perf = ["ipython"] -testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] - -[[package]] -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 = [ +inflection = [ {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, ] - -[[package]] -name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" -category = "main" -optional = false -python-versions = "*" -files = [ +iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] - -[[package]] -name = "isort" -version = "5.11.4" -description = "A Python utility / library to sort Python imports." -category = "main" -optional = false -python-versions = ">=3.7.0" -files = [ +isort = [ {file = "isort-5.11.4-py3-none-any.whl", hash = "sha256:c033fd0edb91000a7f09527fe5c75321878f98322a77ddcc81adbd83724afb7b"}, {file = "isort-5.11.4.tar.gz", hash = "sha256:6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6"}, ] - -[package.extras] -colors = ["colorama (>=0.4.3,<0.5.0)"] -pipfile-deprecated-finder = ["pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] - -[[package]] -name = "itsdangerous" -version = "2.1.2" -description = "Safely pass data to untrusted environments and back." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +itsdangerous = [ {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, ] - -[[package]] -name = "Jinja2" -version = "3.1.2" -description = "A very fast and expressive template engine." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +Jinja2 = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "jsonschema" -version = "4.16.0" -description = "An implementation of JSON Schema validation for Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +jsonschema = [ {file = "jsonschema-4.16.0-py3-none-any.whl", hash = "sha256:9e74b8f9738d6a946d70705dc692b74b5429cd0960d58e79ffecfc43b2221eb9"}, {file = "jsonschema-4.16.0.tar.gz", hash = "sha256:165059f076eff6971bae5b742fc029a7b4ef3f9bcf04c14e4776a7605de14b23"}, ] - -[package.dependencies] -attrs = ">=17.4.0" -pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] - -[[package]] -name = "kombu" -version = "5.2.4" -description = "Messaging library for Python." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +kombu = [ {file = "kombu-5.2.4-py3-none-any.whl", hash = "sha256:8b213b24293d3417bcf0d2f5537b7f756079e3ea232a8386dcc89a59fd2361a4"}, {file = "kombu-5.2.4.tar.gz", hash = "sha256:37cee3ee725f94ea8bb173eaab7c1760203ea53bbebae226328600f9d2799610"}, ] - -[package.dependencies] -amqp = ">=5.0.9,<6.0.0" -vine = "*" - -[package.extras] -azureservicebus = ["azure-servicebus (>=7.0.0)"] -azurestoragequeues = ["azure-storage-queue"] -consul = ["python-consul (>=0.6.0)"] -librabbitmq = ["librabbitmq (>=2.0.0)"] -mongodb = ["pymongo (>=3.3.0,<3.12.1)"] -msgpack = ["msgpack"] -pyro = ["pyro4"] -qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] -redis = ["redis (>=3.4.1,!=4.0.0,!=4.0.1)"] -slmq = ["softlayer-messaging (>=1.0.3)"] -sqlalchemy = ["sqlalchemy"] -sqs = ["boto3 (>=1.9.12)", "pycurl (>=7.44.1,<7.45.0)", "urllib3 (>=1.26.7)"] -yaml = ["PyYAML (>=3.10)"] -zookeeper = ["kazoo (>=1.3.1)"] - -[[package]] -name = "lazy-object-proxy" -version = "1.7.1" -description = "A fast and thorough lazy object proxy." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ +lazy-object-proxy = [ {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"}, {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"}, {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"}, @@ -1436,31 +2694,11 @@ files = [ {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"}, {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"}, ] - -[[package]] -name = "livereload" -version = "2.6.3" -description = "Python LiveReload is an awesome tool for web developers" -category = "dev" -optional = false -python-versions = "*" -files = [ +livereload = [ {file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"}, {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, ] - -[package.dependencies] -six = "*" -tornado = {version = "*", markers = "python_version > \"2.7\""} - -[[package]] -name = "lxml" -version = "4.9.1" -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 = [ +lxml = [ {file = "lxml-4.9.1-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:98cafc618614d72b02185ac583c6f7796202062c41d2eeecdf07820bad3295ed"}, {file = "lxml-4.9.1-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c62e8dd9754b7debda0c5ba59d34509c4688f853588d75b53c3791983faa96fc"}, {file = "lxml-4.9.1-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:21fb3d24ab430fc538a96e9fbb9b150029914805d551deeac7d7822f64631dfc"}, @@ -1532,41 +2770,11 @@ files = [ {file = "lxml-4.9.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:287605bede6bd36e930577c5925fcea17cb30453d96a7b4c63c14a257118dbb9"}, {file = "lxml-4.9.1.tar.gz", hash = "sha256:fe749b052bb7233fe5d072fcb549221a8cb1a16725c47c37e42b0b9cb3ff2c3f"}, ] - -[package.extras] -cssselect = ["cssselect (>=0.7)"] -html5 = ["html5lib"] -htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=0.29.7)"] - -[[package]] -name = "Mako" -version = "1.2.3" -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 = [ +Mako = [ {file = "Mako-1.2.3-py3-none-any.whl", hash = "sha256:c413a086e38cd885088d5e165305ee8eed04e8b3f8f62df343480da0a385735f"}, {file = "Mako-1.2.3.tar.gz", hash = "sha256:7fde96466fcfeedb0eed94f187f20b23d85e4cb41444be0e542e2c8c65c396cd"}, ] - -[package.dependencies] -MarkupSafe = ">=0.9.2" - -[package.extras] -babel = ["Babel"] -lingua = ["lingua"] -testing = ["pytest"] - -[[package]] -name = "MarkupSafe" -version = "2.1.1" -description = "Safely add untrusted strings to HTML/XML markup." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +MarkupSafe = [ {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, @@ -1608,86 +2816,23 @@ files = [ {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, ] - -[[package]] -name = "marshmallow" -version = "3.18.0" -description = "A lightweight library for converting complex datatypes to and from native Python datatypes." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +marshmallow = [ {file = "marshmallow-3.18.0-py3-none-any.whl", hash = "sha256:35e02a3a06899c9119b785c12a22f4cda361745d66a71ab691fd7610202ae104"}, {file = "marshmallow-3.18.0.tar.gz", hash = "sha256:6804c16114f7fce1f5b4dadc31f4674af23317fcc7f075da21e35c1a35d781f7"}, ] - -[package.dependencies] -packaging = ">=17.0" - -[package.extras] -dev = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)", "pytest", "pytz", "simplejson", "tox"] -docs = ["alabaster (==0.7.12)", "autodocsumm (==0.2.9)", "sphinx (==5.1.1)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"] -lint = ["flake8 (==5.0.4)", "flake8-bugbear (==22.9.11)", "mypy (==0.971)", "pre-commit (>=2.4,<3.0)"] -tests = ["pytest", "pytz", "simplejson"] - -[[package]] -name = "marshmallow-enum" -version = "1.5.1" -description = "Enum field for Marshmallow" -category = "main" -optional = false -python-versions = "*" -files = [ +marshmallow-enum = [ {file = "marshmallow-enum-1.5.1.tar.gz", hash = "sha256:38e697e11f45a8e64b4a1e664000897c659b60aa57bfa18d44e226a9920b6e58"}, {file = "marshmallow_enum-1.5.1-py2.py3-none-any.whl", hash = "sha256:57161ab3dbfde4f57adeb12090f39592e992b9c86d206d02f6bd03ebec60f072"}, ] - -[package.dependencies] -marshmallow = ">=2.0.0" - -[[package]] -name = "marshmallow-sqlalchemy" -version = "0.28.1" -description = "SQLAlchemy integration with the marshmallow (de)serialization library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +marshmallow-sqlalchemy = [ {file = "marshmallow-sqlalchemy-0.28.1.tar.gz", hash = "sha256:aa376747296780a56355e3067b9c8bf43a2a1c44ff985de82b3a5d9e161ca2b8"}, {file = "marshmallow_sqlalchemy-0.28.1-py2.py3-none-any.whl", hash = "sha256:dbb061c19375eca3a7d18358d2ca8bbaee825fc3000a3f114e2698282362b536"}, ] - -[package.dependencies] -marshmallow = ">=3.0.0" -packaging = ">=21.3" -SQLAlchemy = ">=1.3.0" - -[package.extras] -dev = ["flake8 (==4.0.1)", "flake8-bugbear (==22.7.1)", "pre-commit (>=2.0,<3.0)", "pytest", "pytest-lazy-fixture (>=0.6.2)", "tox"] -docs = ["alabaster (==0.7.12)", "sphinx (==4.4.0)", "sphinx-issues (==3.0.1)"] -lint = ["flake8 (==4.0.1)", "flake8-bugbear (==22.7.1)", "pre-commit (>=2.0,<3.0)"] -tests = ["pytest", "pytest-lazy-fixture (>=0.6.2)"] - -[[package]] -name = "mccabe" -version = "0.6.1" -description = "McCabe checker, plugin for flake8" -category = "main" -optional = false -python-versions = "*" -files = [ +mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] - -[[package]] -name = "mypy" -version = "0.982" -description = "Optional static typing for Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +mypy = [ {file = "mypy-0.982-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5085e6f442003fa915aeb0a46d4da58128da69325d8213b4b35cc7054090aed5"}, {file = "mypy-0.982-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:41fd1cf9bc0e1c19b9af13a6580ccb66c381a5ee2cf63ee5ebab747a4badeba3"}, {file = "mypy-0.982-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f793e3dd95e166b66d50e7b63e69e58e88643d80a3dcc3bcd81368e0478b089c"}, @@ -1713,37 +2858,11 @@ files = [ {file = "mypy-0.982-py3-none-any.whl", hash = "sha256:1021c241e8b6e1ca5a47e4d52601274ac078a89845cfde66c6d5f769819ffa1d"}, {file = "mypy-0.982.tar.gz", hash = "sha256:85f7a343542dc8b1ed0a888cdd34dca56462654ef23aa673907305b260b3d746"}, ] - -[package.dependencies] -mypy-extensions = ">=0.4.3" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=3.10" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -python2 = ["typed-ast (>=1.4.0,<2)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." -category = "main" -optional = false -python-versions = "*" -files = [ +mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] - -[[package]] -name = "mysql-connector-python" -version = "8.0.32" -description = "MySQL driver written in Python" -category = "main" -optional = false -python-versions = "*" -files = [ +mysql-connector-python = [ {file = "mysql-connector-python-8.0.32.tar.gz", hash = "sha256:c2d20b29fd096a0633f9360c275bd2434d4bcf597281991c4b7f1c820cd07b84"}, {file = "mysql_connector_python-8.0.32-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:4df11c683924ef34c177a54887dc4844ae735b01c8a29ce6ab92d6d3db7a2757"}, {file = "mysql_connector_python-8.0.32-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:4b2d00c9e2cb9e3d11c57ec411226f43aa627607085fbed661cfea1c4dc57f61"}, @@ -1770,175 +2889,47 @@ files = [ {file = "mysql_connector_python-8.0.32-cp39-cp39-win_amd64.whl", hash = "sha256:8c334c41cd1c5bcfa3550340253ef7d9d3b962211f33327c20f69706a0bcce06"}, {file = "mysql_connector_python-8.0.32-py2.py3-none-any.whl", hash = "sha256:e0299236297b63bf6cbb61d81a9d400bc01cad4743d1abe5296ef349de15ee53"}, ] - -[package.dependencies] -protobuf = ">=3.11.0,<=3.20.3" - -[package.extras] -compression = ["lz4 (>=2.1.6,<=3.1.3)", "zstandard (>=0.12.0,<=0.19.0)"] -dns-srv = ["dnspython (>=1.16.0,<=2.1.0)"] -gssapi = ["gssapi (>=1.6.9,<=1.8.2)"] - -[[package]] -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 = [ +nodeenv = [ {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, ] - -[package.dependencies] -setuptools = "*" - -[[package]] -name = "packaging" -version = "21.3" -description = "Core utilities for Python packages" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ +packaging = [ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, ] - -[package.dependencies] -pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" - -[[package]] -name = "pathspec" -version = "0.10.1" -description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ +pathspec = [ {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, ] - -[[package]] -name = "pbr" -version = "5.10.0" -description = "Python Build Reasonableness" -category = "dev" -optional = false -python-versions = ">=2.6" -files = [ +pbr = [ {file = "pbr-5.10.0-py2.py3-none-any.whl", hash = "sha256:da3e18aac0a3c003e9eea1a81bd23e5a3a75d745670dcf736317b7d966887fdf"}, {file = "pbr-5.10.0.tar.gz", hash = "sha256:cfcc4ff8e698256fc17ea3ff796478b050852585aa5bae79ecd05b2ab7b39b9a"}, ] - -[[package]] -name = "pep8-naming" -version = "0.13.2" -description = "Check PEP-8 naming conventions, plugin for flake8" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ +pep8-naming = [ {file = "pep8-naming-0.13.2.tar.gz", hash = "sha256:93eef62f525fd12a6f8c98f4dcc17fa70baae2f37fa1f73bec00e3e44392fa48"}, {file = "pep8_naming-0.13.2-py3-none-any.whl", hash = "sha256:59e29e55c478db69cffbe14ab24b5bd2cd615c0413edf790d47d3fb7ba9a4e23"}, ] - -[package.dependencies] -flake8 = ">=3.9.1" - -[[package]] -name = "platformdirs" -version = "2.5.2" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +platformdirs = [ {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, ] - -[package.extras] -docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] -test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] - -[[package]] -name = "pluggy" -version = "1.0.0" -description = "plugin and hook calling mechanisms for python" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ +pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pre-commit" -version = "2.20.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ +pre-commit = [ {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, ] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -toml = "*" -virtualenv = ">=20.0.8" - -[[package]] -name = "pre-commit-hooks" -version = "4.3.0" -description = "Some out-of-the-box hooks for pre-commit." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ +pre-commit-hooks = [ {file = "pre_commit_hooks-4.3.0-py2.py3-none-any.whl", hash = "sha256:9ccaf7c98794659d345080ee1ea0256a55ae059675045eebdbbc17c0be8c7e4b"}, {file = "pre_commit_hooks-4.3.0.tar.gz", hash = "sha256:fda598a4c834d030727e6a615722718b47510f4bed72df4c949f95ba9f5aaf88"}, ] - -[package.dependencies] -"ruamel.yaml" = ">=0.15" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "prompt-toolkit" -version = "3.0.31" -description = "Library for building powerful interactive command lines in Python" -category = "main" -optional = false -python-versions = ">=3.6.2" -files = [ +prompt-toolkit = [ {file = "prompt_toolkit-3.0.31-py3-none-any.whl", hash = "sha256:9696f386133df0fc8ca5af4895afe5d78f5fcfe5258111c2a79a1c3e41ffa96d"}, {file = "prompt_toolkit-3.0.31.tar.gz", hash = "sha256:9ada952c9d1787f52ff6d5f3484d0b4df8952787c087edf6a1f7c2cb1ea88148"}, ] - -[package.dependencies] -wcwidth = "*" - -[[package]] -name = "protobuf" -version = "3.20.3" -description = "Protocol Buffers" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +protobuf = [ {file = "protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"}, {file = "protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"}, {file = "protobuf-3.20.3-cp310-cp310-win32.whl", hash = "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c"}, @@ -1962,15 +2953,7 @@ files = [ {file = "protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db"}, {file = "protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2"}, ] - -[[package]] -name = "psycopg2" -version = "2.9.4" -description = "psycopg2 - Python-PostgreSQL Database Adapter" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ +psycopg2 = [ {file = "psycopg2-2.9.4-cp310-cp310-win32.whl", hash = "sha256:8de6a9fc5f42fa52f559e65120dcd7502394692490c98fed1221acf0819d7797"}, {file = "psycopg2-2.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:1da77c061bdaab450581458932ae5e469cc6e36e0d62f988376e9f513f11cb5c"}, {file = "psycopg2-2.9.4-cp36-cp36m-win32.whl", hash = "sha256:a11946bad3557ca254f17357d5a4ed63bdca45163e7a7d2bfb8e695df069cc3a"}, @@ -1983,27 +2966,11 @@ files = [ {file = "psycopg2-2.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:849bd868ae3369932127f0771c08d1109b254f08d48dc42493c3d1b87cb2d308"}, {file = "psycopg2-2.9.4.tar.gz", hash = "sha256:d529926254e093a1b669f692a3aa50069bc71faf5b0ecd91686a78f62767d52f"}, ] - -[[package]] -name = "pycodestyle" -version = "2.8.0" -description = "Python style guide checker" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ +pycodestyle = [ {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, ] - -[[package]] -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 = [ +pycryptodome = [ {file = "pycryptodome-3.17-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:2c5631204ebcc7ae33d11c43037b2dafe25e2ab9c1de6448eb6502ac69c19a56"}, {file = "pycryptodome-3.17-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:04779cc588ad8f13c80a060b0b1c9d1c203d051d8a43879117fe6b8aaf1cd3fa"}, {file = "pycryptodome-3.17-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:f812d58c5af06d939b2baccdda614a3ffd80531a26e5faca2c9f8b1770b2b7af"}, @@ -2038,123 +3005,31 @@ files = [ {file = "pycryptodome-3.17-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:74794a2e2896cd0cf56fdc9db61ef755fa812b4a4900fa46c49045663a92b8d0"}, {file = "pycryptodome-3.17.tar.gz", hash = "sha256:bce2e2d8e82fcf972005652371a3e8731956a0c1fbb719cc897943b3695ad91b"}, ] - -[[package]] -name = "pydocstyle" -version = "6.1.1" -description = "Python docstring style checker" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ +pydocstyle = [ {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, ] - -[package.dependencies] -snowballstemmer = "*" - -[package.extras] -toml = ["toml"] - -[[package]] -name = "pyflakes" -version = "2.4.0" -description = "passive checker of Python programs" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ +pyflakes = [ {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, ] - -[[package]] -name = "Pygments" -version = "2.13.0" -description = "Pygments is a syntax highlighting package written in Python." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ +Pygments = [ {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, ] - -[package.extras] -plugins = ["importlib-metadata"] - -[[package]] -name = "pyjwt" -version = "2.6.0" -description = "JSON Web Token implementation in Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +pyjwt = [ {file = "PyJWT-2.6.0-py3-none-any.whl", hash = "sha256:d83c3d892a77bbb74d3e1a2cfa90afaadb60945205d1095d9221f04466f64c14"}, {file = "PyJWT-2.6.0.tar.gz", hash = "sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd"}, ] - -[package.extras] -crypto = ["cryptography (>=3.4.0)"] -dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] -docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] - -[[package]] -name = "pylint" -version = "2.15.10" -description = "python code static checker" -category = "main" -optional = false -python-versions = ">=3.7.2" -files = [ +pylint = [ {file = "pylint-2.15.10-py3-none-any.whl", hash = "sha256:9df0d07e8948a1c3ffa3b6e2d7e6e63d9fb457c5da5b961ed63106594780cc7e"}, {file = "pylint-2.15.10.tar.gz", hash = "sha256:b3dc5ef7d33858f297ac0d06cc73862f01e4f2e74025ec3eff347ce0bc60baf5"}, ] - -[package.dependencies] -astroid = ">=2.12.13,<=2.14.0-dev0" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = [ - {version = ">=0.2", markers = "python_version < \"3.11\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, -] -isort = ">=4.2.5,<6" -mccabe = ">=0.6,<0.8" -platformdirs = ">=2.2.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -tomlkit = ">=0.10.1" -typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} - -[package.extras] -spelling = ["pyenchant (>=3.2,<4.0)"] -testutils = ["gitpython (>3)"] - -[[package]] -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 = [ +pyparsing = [ {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, ] - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - -[[package]] -name = "pyrsistent" -version = "0.18.1" -description = "Persistent/Functional/Immutable data structures" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +pyrsistent = [ {file = "pyrsistent-0.18.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1"}, {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26"}, {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e"}, @@ -2177,156 +3052,39 @@ files = [ {file = "pyrsistent-0.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07"}, {file = "pyrsistent-0.18.1.tar.gz", hash = "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96"}, ] - -[[package]] -name = "pytest" -version = "7.2.0" -description = "pytest: simple powerful testing with Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +pytest = [ {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, ] - -[package.dependencies] -attrs = ">=19.2.0" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} - -[package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] - -[[package]] -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 = [ +pytest-flask = [ {file = "pytest-flask-1.2.0.tar.gz", hash = "sha256:46fde652f77777bf02dc91205aec4ce20cdf2acbbbd66a918ab91f5c14693d3d"}, {file = "pytest_flask-1.2.0-py3-none-any.whl", hash = "sha256:fe25b39ad0db09c3d1fe728edecf97ced85e774c775db259a6d25f0270a4e7c9"}, ] - -[package.dependencies] -Flask = "*" -pytest = ">=5.2" -Werkzeug = ">=0.7" - -[package.extras] -docs = ["Sphinx", "sphinx-rtd-theme"] - -[[package]] -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 = [ +pytest-flask-sqlalchemy = [ {file = "pytest-flask-sqlalchemy-1.1.0.tar.gz", hash = "sha256:db71a57b90435e5d854b21c37a2584056d6fc3ddb28c09d8d0a2546bd6e390ff"}, {file = "pytest_flask_sqlalchemy-1.1.0-py3-none-any.whl", hash = "sha256:b9f272d5c4092fcbe4a6284e402a37cad84f5b9be3c0bbe1a11927f24c99ff83"}, ] - -[package.dependencies] -Flask-SQLAlchemy = ">=2.3" -packaging = ">=14.1" -pytest = ">=3.2.1" -pytest-mock = ">=1.6.2" -SQLAlchemy = ">=1.2.2" - -[package.extras] -tests = ["psycopg2-binary", "pytest (>=6.0.1)", "pytest-postgresql (>=2.4.0,<4.0.0)"] - -[[package]] -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 = [ +pytest-mock = [ {file = "pytest-mock-3.10.0.tar.gz", hash = "sha256:fbbdb085ef7c252a326fd8cdcac0aa3b1333d8811f131bdcc701002e1be7ed4f"}, {file = "pytest_mock-3.10.0-py3-none-any.whl", hash = "sha256:f4c973eeae0282963eb293eb173ce91b091a79c1334455acfac9ddee8a1c784b"}, ] - -[package.dependencies] -pytest = ">=5.0" - -[package.extras] -dev = ["pre-commit", "pytest-asyncio", "tox"] - -[[package]] -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 = [ +python-dateutil = [ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, ] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pytz" -version = "2022.7.1" -description = "World timezone definitions, modern and historical" -category = "main" -optional = false -python-versions = "*" -files = [ +pytz = [ {file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"}, {file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"}, ] - -[[package]] -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 = [ +pytz-deprecation-shim = [ {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"}, {file = "pytz_deprecation_shim-0.1.0.post0.tar.gz", hash = "sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"}, ] - -[package.dependencies] -tzdata = {version = "*", markers = "python_version >= \"3.6\""} - -[[package]] -name = "pyupgrade" -version = "3.1.0" -description = "A tool to automatically upgrade syntax for newer versions." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ +pyupgrade = [ {file = "pyupgrade-3.1.0-py2.py3-none-any.whl", hash = "sha256:77c6101a710be3e24804891e43388cedbee617258e93b09c8c5e58de08617758"}, {file = "pyupgrade-3.1.0.tar.gz", hash = "sha256:7a8d393d85e15e0e2753e90b7b2e173b9d29dfd71e61f93d93e985b242627ed3"}, ] - -[package.dependencies] -tokenize-rt = ">=3.2.0" - -[[package]] -name = "PyYAML" -version = "6.0" -description = "YAML parser and emitter for Python" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ +PyYAML = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, @@ -2368,15 +3126,7 @@ files = [ {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] - -[[package]] -name = "regex" -version = "2022.3.2" -description = "Alternative regular expression module, to replace re." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ +regex = [ {file = "regex-2022.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab69b4fe09e296261377d209068d52402fb85ef89dc78a9ac4a29a895f4e24a7"}, {file = "regex-2022.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5bc5f921be39ccb65fdda741e04b2555917a4bced24b4df14eddc7569be3b493"}, {file = "regex-2022.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43eba5c46208deedec833663201752e865feddc840433285fbadee07b84b464d"}, @@ -2452,101 +3202,26 @@ files = [ {file = "regex-2022.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:9efa41d1527b366c88f265a227b20bcec65bda879962e3fc8a2aee11e81266d7"}, {file = "regex-2022.3.2.tar.gz", hash = "sha256:79e5af1ff258bc0fe0bdd6f69bc4ae33935a898e3cbefbbccf22e88a27fa053b"}, ] - -[[package]] -name = "reorder-python-imports" -version = "3.9.0" -description = "Tool for reordering python imports" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ +reorder-python-imports = [ {file = "reorder_python_imports-3.9.0-py2.py3-none-any.whl", hash = "sha256:3f9c16e8781f54c944756d0d1eb34a8c863554f7a4eb3693f574fe19b1a29b56"}, {file = "reorder_python_imports-3.9.0.tar.gz", hash = "sha256:49292ed537829a6bece9fb3746fc1bbe98f52643be5de01a4e13680268a5b0ec"}, ] - -[package.dependencies] -classify-imports = ">=4.1" - -[[package]] -name = "requests" -version = "2.28.1" -description = "Python HTTP for Humans." -category = "main" -optional = false -python-versions = ">=3.7, <4" -files = [ +requests = [ {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, ] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<3" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "restrictedpython" -version = "6.0" -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 = [ +restrictedpython = [ {file = "RestrictedPython-6.0-py3-none-any.whl", hash = "sha256:3479303f7bff48a7dedad76f96e7704993c5e86c5adbd67f607295d5352f0fb8"}, {file = "RestrictedPython-6.0.tar.gz", hash = "sha256:405cf0bd9eec2f19b1326b5f48228efe56d6590b4e91826b8cc3b2cd400a96ad"}, ] - -[package.extras] -docs = ["Sphinx", "sphinx-rtd-theme"] -test = ["pytest", "pytest-mock"] - -[[package]] -name = "restructuredtext-lint" -version = "1.4.0" -description = "reStructuredText linter" -category = "dev" -optional = false -python-versions = "*" -files = [ +restructuredtext-lint = [ {file = "restructuredtext_lint-1.4.0.tar.gz", hash = "sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45"}, ] - -[package.dependencies] -docutils = ">=0.11,<1.0" - -[[package]] -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 = [ +"ruamel.yaml" = [ {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, ] - -[package.dependencies] -"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} - -[package.extras] -docs = ["ryd"] -jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] - -[[package]] -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 = [ +ruamel-yaml-clib = [ {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5859983f26d8cd7bb5c287ef452e8aacc86501487634573d260968f753e1d71"}, {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:debc87a9516b237d0466a711b18b6ebeb17ba9f391eb7f91c649c5c4ec5006c7"}, {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:df5828871e6648db72d1c19b4bd24819b80a755c4541d3409f0f7acd0f335c80"}, @@ -2582,97 +3257,19 @@ files = [ {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:184faeaec61dbaa3cace407cffc5819f7b977e75360e8d5ca19461cd851a5fc5"}, {file = "ruamel.yaml.clib-0.2.7.tar.gz", hash = "sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497"}, ] - -[[package]] -name = "safety" -version = "2.3.1" -description = "Checks installed dependencies for known vulnerabilities and licenses." -category = "dev" -optional = false -python-versions = "*" -files = [ +safety = [ {file = "safety-2.3.1-py3-none-any.whl", hash = "sha256:8f098d12b607db2756886280e85c28ece8db1bba4f45fc5f981f4663217bd619"}, {file = "safety-2.3.1.tar.gz", hash = "sha256:6e6fcb7d4e8321098cf289f59b65051cafd3467f089c6e57c9f894ae32c23b71"}, ] - -[package.dependencies] -Click = ">=8.0.2" -dparse = ">=0.6.2" -packaging = ">=21.0" -requests = "*" -"ruamel.yaml" = ">=0.17.21" -setuptools = ">=19.3" - -[package.extras] -github = ["jinja2 (>=3.1.0)", "pygithub (>=1.43.3)"] -gitlab = ["python-gitlab (>=1.3.0)"] - -[[package]] -name = "sentry-sdk" -version = "1.16.0" -description = "Python client for Sentry (https://sentry.io)" -category = "main" -optional = false -python-versions = "*" -files = [ +sentry-sdk = [ {file = "sentry-sdk-1.16.0.tar.gz", hash = "sha256:a900845bd78c263d49695d48ce78a4bce1030bbd917e0b6cc021fc000c901113"}, {file = "sentry_sdk-1.16.0-py2.py3-none-any.whl", hash = "sha256:633edefead34d976ff22e7edc367cdf57768e24bc714615ccae746d9d91795ae"}, ] - -[package.dependencies] -certifi = "*" -urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} - -[package.extras] -aiohttp = ["aiohttp (>=3.5)"] -arq = ["arq (>=0.23)"] -beam = ["apache-beam (>=2.12)"] -bottle = ["bottle (>=0.12.13)"] -celery = ["celery (>=3)"] -chalice = ["chalice (>=1.16.0)"] -django = ["django (>=1.8)"] -falcon = ["falcon (>=1.4)"] -fastapi = ["fastapi (>=0.79.0)"] -flask = ["blinker (>=1.1)", "flask (>=0.11)"] -httpx = ["httpx (>=0.16.0)"] -huey = ["huey (>=2)"] -opentelemetry = ["opentelemetry-distro (>=0.35b0)"] -pure-eval = ["asttokens", "executing", "pure-eval"] -pymongo = ["pymongo (>=3.1)"] -pyspark = ["pyspark (>=2.4.4)"] -quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] -rq = ["rq (>=0.6)"] -sanic = ["sanic (>=0.8)"] -sqlalchemy = ["sqlalchemy (>=1.2)"] -starlette = ["starlette (>=0.19.1)"] -starlite = ["starlite (>=1.48)"] -tornado = ["tornado (>=5)"] - -[[package]] -name = "setuptools" -version = "65.5.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +setuptools = [ {file = "setuptools-65.5.0-py3-none-any.whl", hash = "sha256:f62ea9da9ed6289bfe868cd6845968a2c854d1427f8548d52cae02a42b4f0356"}, {file = "setuptools-65.5.0.tar.gz", hash = "sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17"}, ] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "simplejson" -version = "3.17.6" -description = "Simple, fast, extensible JSON encoder/decoder for Python" -category = "main" -optional = false -python-versions = ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ +simplejson = [ {file = "simplejson-3.17.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a89acae02b2975b1f8e4974cb8cdf9bf9f6c91162fb8dec50c259ce700f2770a"}, {file = "simplejson-3.17.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:82ff356ff91be0ab2293fc6d8d262451eb6ac4fd999244c4b5f863e049ba219c"}, {file = "simplejson-3.17.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:0de783e9c2b87bdd75b57efa2b6260c24b94605b5c9843517577d40ee0c3cc8a"}, @@ -2735,294 +3332,68 @@ files = [ {file = "simplejson-3.17.6-cp39-cp39-win_amd64.whl", hash = "sha256:3fe87570168b2ae018391e2b43fbf66e8593a86feccb4b0500d134c998983ccc"}, {file = "simplejson-3.17.6.tar.gz", hash = "sha256:cf98038d2abf63a1ada5730e91e84c642ba6c225b0198c3684151b1f80c5f8a6"}, ] - -[[package]] -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 = [ +six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] - -[[package]] -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 = [ +smmap = [ {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, ] - -[[package]] -name = "snowballstemmer" -version = "2.2.0" -description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -category = "main" -optional = false -python-versions = "*" -files = [ +snowballstemmer = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] - -[[package]] -name = "soupsieve" -version = "2.3.2.post1" -description = "A modern CSS selector implementation for Beautiful Soup." -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ +soupsieve = [ {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"}, {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"}, ] - -[[package]] -name = "Sphinx" -version = "5.3.0" -description = "Python documentation generator" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ +Sphinx = [ {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, ] - -[package.dependencies] -alabaster = ">=0.7,<0.8" -babel = ">=2.9" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.14,<0.20" -imagesize = ">=1.3" -importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} -Jinja2 = ">=3.0" -packaging = ">=21.0" -Pygments = ">=2.12" -requests = ">=2.5.0" -snowballstemmer = ">=2.0" -sphinxcontrib-applehelp = "*" -sphinxcontrib-devhelp = "*" -sphinxcontrib-htmlhelp = ">=2.0.0" -sphinxcontrib-jsmath = "*" -sphinxcontrib-qthelp = "*" -sphinxcontrib-serializinghtml = ">=1.1.5" - -[package.extras] -docs = ["sphinxcontrib-websupport"] -lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "flake8-simplify", "isort", "mypy (>=0.981)", "sphinx-lint", "types-requests", "types-typed-ast"] -test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"] - -[[package]] -name = "sphinx-autoapi" -version = "2.0.0" -description = "Sphinx API documentation generator" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +sphinx-autoapi = [ {file = "sphinx-autoapi-2.0.0.tar.gz", hash = "sha256:97dcf1b5b54cd0d8efef867594e4a4f3e2d3a2c0ec1e5a891e0a61bc77046006"}, {file = "sphinx_autoapi-2.0.0-py2.py3-none-any.whl", hash = "sha256:dab2753a38cad907bf4e61473c0da365a26bfbe69fbf5aa6e4f7d48e1cf8a148"}, ] - -[package.dependencies] -astroid = ">=2.7" -Jinja2 = "*" -PyYAML = "*" -sphinx = ">=4.0" -unidecode = "*" - -[package.extras] -docs = ["sphinx", "sphinx-rtd-theme"] -dotnet = ["sphinxcontrib-dotnetdomain"] -go = ["sphinxcontrib-golangdomain"] - -[[package]] -name = "sphinx-autobuild" -version = "2021.3.14" -description = "Rebuild Sphinx documentation on changes, with live-reload in the browser." -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ +sphinx-autobuild = [ {file = "sphinx-autobuild-2021.3.14.tar.gz", hash = "sha256:de1ca3b66e271d2b5b5140c35034c89e47f263f2cd5db302c9217065f7443f05"}, {file = "sphinx_autobuild-2021.3.14-py3-none-any.whl", hash = "sha256:8fe8cbfdb75db04475232f05187c776f46f6e9e04cacf1e49ce81bdac649ccac"}, ] - -[package.dependencies] -colorama = "*" -livereload = "*" -sphinx = "*" - -[package.extras] -test = ["pytest", "pytest-cov"] - -[[package]] -name = "sphinx-basic-ng" -version = "1.0.0b1" -description = "A modern skeleton for Sphinx themes." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ +sphinx-basic-ng = [ {file = "sphinx_basic_ng-1.0.0b1-py3-none-any.whl", hash = "sha256:ade597a3029c7865b24ad0eda88318766bcc2f9f4cef60df7e28126fde94db2a"}, {file = "sphinx_basic_ng-1.0.0b1.tar.gz", hash = "sha256:89374bd3ccd9452a301786781e28c8718e99960f2d4f411845ea75fc7bb5a9b0"}, ] - -[package.dependencies] -sphinx = ">=4.0" - -[package.extras] -docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] - -[[package]] -name = "sphinx-click" -version = "4.3.0" -description = "Sphinx extension that automatically documents click applications" -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ +sphinx-click = [ {file = "sphinx-click-4.3.0.tar.gz", hash = "sha256:bd4db5d3c1bec345f07af07b8e28a76cfc5006d997984e38ae246bbf8b9a3b38"}, {file = "sphinx_click-4.3.0-py3-none-any.whl", hash = "sha256:23e85a3cb0b728a421ea773699f6acadefae171d1a764a51dd8ec5981503ccbe"}, ] - -[package.dependencies] -click = ">=7.0" -docutils = "*" -sphinx = ">=2.0" - -[[package]] -name = "sphinxcontrib-applehelp" -version = "1.0.2" -description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ +sphinxcontrib-applehelp = [ {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, ] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "1.0.2" -description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." -category = "main" -optional = false -python-versions = ">=3.5" -files = [ +sphinxcontrib-devhelp = [ {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, ] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.0.0" -description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ +sphinxcontrib-htmlhelp = [ {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, ] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["html5lib", "pytest"] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -description = "A sphinx extension which renders display math in HTML via JavaScript" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ +sphinxcontrib-jsmath = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, ] - -[package.extras] -test = ["flake8", "mypy", "pytest"] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "1.0.3" -description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." -category = "main" -optional = false -python-versions = ">=3.5" -files = [ +sphinxcontrib-qthelp = [ {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, ] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "1.1.5" -description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." -category = "main" -optional = false -python-versions = ">=3.5" -files = [ +sphinxcontrib-serializinghtml = [ {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, ] - -[package.extras] -lint = ["docutils-stubs", "flake8", "mypy"] -test = ["pytest"] - -[[package]] -name = "SpiffWorkflow" -version = "1.2.1" -description = "A workflow framework and BPMN/DMN Processor" -category = "main" -optional = false -python-versions = "*" -files = [] -develop = false - -[package.dependencies] -celery = "*" -configparser = "*" -lxml = "*" - -[package.source] -type = "git" -url = "https://github.com/sartography/SpiffWorkflow" -reference = "main" -resolved_reference = "42b483054b3f1b86f7d3a9eb1a193345c578d86d" - -[[package]] -name = "SQLAlchemy" -version = "1.4.42" -description = "Database Abstraction Library" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ +SpiffWorkflow = [] +SQLAlchemy = [ {file = "SQLAlchemy-1.4.42-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:28e881266a172a4d3c5929182fde6bb6fba22ac93f137d5380cc78a11a9dd124"}, {file = "SQLAlchemy-1.4.42-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ca9389a00f639383c93ed00333ed763812f80b5ae9e772ea32f627043f8c9c88"}, {file = "SQLAlchemy-1.4.42-cp27-cp27m-win32.whl", hash = "sha256:1d0c23ecf7b3bc81e29459c34a3f4c68ca538de01254e24718a7926810dc39a6"}, @@ -3065,137 +3436,32 @@ files = [ {file = "SQLAlchemy-1.4.42-cp39-cp39-win_amd64.whl", hash = "sha256:5f966b64c852592469a7eb759615bbd351571340b8b344f1d3fa2478b5a4c934"}, {file = "SQLAlchemy-1.4.42.tar.gz", hash = "sha256:177e41914c476ed1e1b77fd05966ea88c094053e17a85303c4ce007f88eff363"}, ] - -[package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (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\")"} - -[package.extras] -aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] -aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing-extensions (!=3.10.0.1)"] -asyncio = ["greenlet (!=0.4.17)"] -asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"] -mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"] -mssql = ["pyodbc"] -mssql-pymssql = ["pymssql"] -mssql-pyodbc = ["pyodbc"] -mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"] -mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] -mysql-connector = ["mysql-connector-python"] -oracle = ["cx-oracle (>=7)", "cx-oracle (>=7,<8)"] -postgresql = ["psycopg2 (>=2.7)"] -postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] -postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"] -postgresql-psycopg2binary = ["psycopg2-binary"] -postgresql-psycopg2cffi = ["psycopg2cffi"] -pymysql = ["pymysql", "pymysql (<1)"] -sqlcipher = ["sqlcipher3-binary"] - -[[package]] -name = "sqlalchemy-stubs" -version = "0.4" -description = "SQLAlchemy stubs and mypy plugin" -category = "main" -optional = false -python-versions = "*" -files = [] -develop = false - -[package.dependencies] -mypy = ">=0.790" -typing-extensions = ">=3.7.4" - -[package.source] -type = "git" -url = "https://github.com/burnettk/sqlalchemy-stubs.git" -reference = "scoped-session-delete" -resolved_reference = "d1176931684ce5b327539cc9567d4a1cd8ef1efd" - -[[package]] -name = "stevedore" -version = "4.0.1" -description = "Manage dynamic plugins for Python applications" -category = "dev" -optional = false -python-versions = ">=3.8" -files = [ +sqlalchemy-stubs = [] +stevedore = [ {file = "stevedore-4.0.1-py3-none-any.whl", hash = "sha256:01645addb67beff04c7cfcbb0a6af8327d2efc3380b0f034aa316d4576c4d470"}, {file = "stevedore-4.0.1.tar.gz", hash = "sha256:9a23111a6e612270c591fd31ff3321c6b5f3d5f3dabb1427317a5ab608fc261a"}, ] - -[package.dependencies] -pbr = ">=2.0.0,<2.1.0 || >2.1.0" - -[[package]] -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 = [ +swagger-ui-bundle = [ {file = "swagger_ui_bundle-0.0.9-py3-none-any.whl", hash = "sha256:cea116ed81147c345001027325c1ddc9ca78c1ee7319935c3c75d3669279d575"}, {file = "swagger_ui_bundle-0.0.9.tar.gz", hash = "sha256:b462aa1460261796ab78fd4663961a7f6f347ce01760f1303bbbdf630f11f516"}, ] - -[package.dependencies] -Jinja2 = ">=2.0" - -[[package]] -name = "tokenize-rt" -version = "4.2.1" -description = "A wrapper around the stdlib `tokenize` which roundtrips." -category = "dev" -optional = false -python-versions = ">=3.6.1" -files = [ +tokenize-rt = [ {file = "tokenize_rt-4.2.1-py2.py3-none-any.whl", hash = "sha256:08a27fa032a81cf45e8858d0ac706004fcd523e8463415ddf1442be38e204ea8"}, {file = "tokenize_rt-4.2.1.tar.gz", hash = "sha256:0d4f69026fed520f8a1e0103aa36c406ef4661417f20ca643f913e33531b3b94"}, ] - -[[package]] -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 = [ +toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] - -[[package]] -name = "tomlkit" -version = "0.11.6" -description = "Style preserving TOML library" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ +tomlkit = [ {file = "tomlkit-0.11.6-py3-none-any.whl", hash = "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b"}, {file = "tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"}, ] - -[[package]] -name = "tornado" -version = "6.2" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -category = "dev" -optional = false -python-versions = ">= 3.7" -files = [ +tornado = [ {file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"}, {file = "tornado-6.2-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9"}, {file = "tornado-6.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac"}, @@ -3208,298 +3474,87 @@ files = [ {file = "tornado-6.2-cp37-abi3-win_amd64.whl", hash = "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"}, {file = "tornado-6.2.tar.gz", hash = "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13"}, ] - -[[package]] -name = "typeguard" -version = "2.13.3" -description = "Run-time type checker for Python" -category = "dev" -optional = false -python-versions = ">=3.5.3" -files = [ +typeguard = [ {file = "typeguard-2.13.3-py3-none-any.whl", hash = "sha256:5e3e3be01e887e7eafae5af63d1f36c849aaa94e3a0112097312aabfa16284f1"}, {file = "typeguard-2.13.3.tar.gz", hash = "sha256:00edaa8da3a133674796cf5ea87d9f4b4c367d77476e185e80251cc13dfbb8c4"}, ] - -[package.extras] -doc = ["sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["mypy", "pytest", "typing-extensions"] - -[[package]] -name = "types-click" -version = "7.1.8" -description = "Typing stubs for click" -category = "main" -optional = false -python-versions = "*" -files = [ +types-click = [ {file = "types-click-7.1.8.tar.gz", hash = "sha256:b6604968be6401dc516311ca50708a0a28baa7a0cb840efd7412f0dbbff4e092"}, {file = "types_click-7.1.8-py3-none-any.whl", hash = "sha256:8cb030a669e2e927461be9827375f83c16b8178c365852c060a34e24871e7e81"}, ] - -[[package]] -name = "types-dateparser" -version = "1.1.4.1" -description = "Typing stubs for dateparser" -category = "main" -optional = false -python-versions = "*" -files = [ +types-dateparser = [ {file = "types-dateparser-1.1.4.1.tar.gz", hash = "sha256:0f76578bbae15c8b8701b5efd94db98a97ce0a27aedfe6f14a531170de6db97d"}, {file = "types_dateparser-1.1.4.1-py3-none-any.whl", hash = "sha256:dd7b2343bb06225c0e358533609b66a8edfb95e5426d8f658664e7d0f27dea68"}, ] - -[[package]] -name = "types-Flask" -version = "1.1.6" -description = "Typing stubs for Flask" -category = "main" -optional = false -python-versions = "*" -files = [ +types-Flask = [ {file = "types-Flask-1.1.6.tar.gz", hash = "sha256:aac777b3abfff9436e6b01f6d08171cf23ea6e5be71cbf773aaabb1c5763e9cf"}, {file = "types_Flask-1.1.6-py3-none-any.whl", hash = "sha256:6ab8a9a5e258b76539d652f6341408867298550b19b81f0e41e916825fc39087"}, ] - -[package.dependencies] -types-click = "*" -types-Jinja2 = "*" -types-Werkzeug = "*" - -[[package]] -name = "types-Jinja2" -version = "2.11.9" -description = "Typing stubs for Jinja2" -category = "main" -optional = false -python-versions = "*" -files = [ +types-Jinja2 = [ {file = "types-Jinja2-2.11.9.tar.gz", hash = "sha256:dbdc74a40aba7aed520b7e4d89e8f0fe4286518494208b35123bcf084d4b8c81"}, {file = "types_Jinja2-2.11.9-py3-none-any.whl", hash = "sha256:60a1e21e8296979db32f9374d8a239af4cb541ff66447bb915d8ad398f9c63b2"}, ] - -[package.dependencies] -types-MarkupSafe = "*" - -[[package]] -name = "types-MarkupSafe" -version = "1.1.10" -description = "Typing stubs for MarkupSafe" -category = "main" -optional = false -python-versions = "*" -files = [ +types-MarkupSafe = [ {file = "types-MarkupSafe-1.1.10.tar.gz", hash = "sha256:85b3a872683d02aea3a5ac2a8ef590193c344092032f58457287fbf8e06711b1"}, {file = "types_MarkupSafe-1.1.10-py3-none-any.whl", hash = "sha256:ca2bee0f4faafc45250602567ef38d533e877d2ddca13003b319c551ff5b3cc5"}, ] - -[[package]] -name = "types-pytz" -version = "2022.5.0.0" -description = "Typing stubs for pytz" -category = "main" -optional = false -python-versions = "*" -files = [ +types-pytz = [ {file = "types-pytz-2022.5.0.0.tar.gz", hash = "sha256:0c163b15d3e598e6cc7074a99ca9ec72b25dc1b446acc133b827667af0b7b09a"}, {file = "types_pytz-2022.5.0.0-py3-none-any.whl", hash = "sha256:a8e1fe6a1b270fbfaf2553b20ad0f1316707cc320e596da903bb17d7373fed2d"}, ] - -[[package]] -name = "types-PyYAML" -version = "6.0.12" -description = "Typing stubs for PyYAML" -category = "main" -optional = false -python-versions = "*" -files = [ +types-PyYAML = [ {file = "types-PyYAML-6.0.12.tar.gz", hash = "sha256:f6f350418125872f3f0409d96a62a5a5ceb45231af5cc07ee0034ec48a3c82fa"}, {file = "types_PyYAML-6.0.12-py3-none-any.whl", hash = "sha256:29228db9f82df4f1b7febee06bbfb601677882e98a3da98132e31c6874163e15"}, ] - -[[package]] -name = "types-requests" -version = "2.28.11.2" -description = "Typing stubs for requests" -category = "main" -optional = false -python-versions = "*" -files = [ +types-requests = [ {file = "types-requests-2.28.11.2.tar.gz", hash = "sha256:fdcd7bd148139fb8eef72cf4a41ac7273872cad9e6ada14b11ff5dfdeee60ed3"}, {file = "types_requests-2.28.11.2-py3-none-any.whl", hash = "sha256:14941f8023a80b16441b3b46caffcbfce5265fd14555844d6029697824b5a2ef"}, ] - -[package.dependencies] -types-urllib3 = "<1.27" - -[[package]] -name = "types-urllib3" -version = "1.26.25.1" -description = "Typing stubs for urllib3" -category = "main" -optional = false -python-versions = "*" -files = [ +types-urllib3 = [ {file = "types-urllib3-1.26.25.1.tar.gz", hash = "sha256:a948584944b2412c9a74b9cf64f6c48caf8652cb88b38361316f6d15d8a184cd"}, {file = "types_urllib3-1.26.25.1-py3-none-any.whl", hash = "sha256:f6422596cc9ee5fdf68f9d547f541096a20c2dcfd587e37c804c9ea720bf5cb2"}, ] - -[[package]] -name = "types-Werkzeug" -version = "1.0.9" -description = "Typing stubs for Werkzeug" -category = "main" -optional = false -python-versions = "*" -files = [ +types-Werkzeug = [ {file = "types-Werkzeug-1.0.9.tar.gz", hash = "sha256:5cc269604c400133d452a40cee6397655f878fc460e03fde291b9e3a5eaa518c"}, {file = "types_Werkzeug-1.0.9-py3-none-any.whl", hash = "sha256:194bd5715a13c598f05c63e8a739328657590943bce941e8a3619a6b5d4a54ec"}, ] - -[[package]] -name = "typing-extensions" -version = "4.4.0" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +typing-extensions = [ {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] - -[[package]] -name = "tzdata" -version = "2022.5" -description = "Provider of IANA time zone data" -category = "main" -optional = false -python-versions = ">=2" -files = [ +tzdata = [ {file = "tzdata-2022.5-py2.py3-none-any.whl", hash = "sha256:323161b22b7802fdc78f20ca5f6073639c64f1a7227c40cd3e19fd1d0ce6650a"}, {file = "tzdata-2022.5.tar.gz", hash = "sha256:e15b2b3005e2546108af42a0eb4ccab4d9e225e2dfbf4f77aad50c70a4b1f3ab"}, ] - -[[package]] -name = "tzlocal" -version = "4.2" -description = "tzinfo object for the local timezone" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ +tzlocal = [ {file = "tzlocal-4.2-py3-none-any.whl", hash = "sha256:89885494684c929d9191c57aa27502afc87a579be5cdd3225c77c463ea043745"}, {file = "tzlocal-4.2.tar.gz", hash = "sha256:ee5842fa3a795f023514ac2d801c4a81d1743bbe642e3940143326b3a00addd7"}, ] - -[package.dependencies] -pytz-deprecation-shim = "*" -tzdata = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -devenv = ["black", "pyroma", "pytest-cov", "zest.releaser"] -test = ["pytest (>=4.3)", "pytest-mock (>=3.3)"] - -[[package]] -name = "Unidecode" -version = "1.3.6" -description = "ASCII transliterations of Unicode text" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ +Unidecode = [ {file = "Unidecode-1.3.6-py3-none-any.whl", hash = "sha256:547d7c479e4f377b430dd91ac1275d593308dce0fc464fb2ab7d41f82ec653be"}, {file = "Unidecode-1.3.6.tar.gz", hash = "sha256:fed09cf0be8cf415b391642c2a5addfc72194407caee4f98719e40ec2a72b830"}, ] - -[[package]] -name = "urllib3" -version = "1.26.12" -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.*, <4" -files = [ +urllib3 = [ {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"}, {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"}, ] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[[package]] -name = "vine" -version = "5.0.0" -description = "Promises, promises, promises." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ +vine = [ {file = "vine-5.0.0-py2.py3-none-any.whl", hash = "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30"}, {file = "vine-5.0.0.tar.gz", hash = "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"}, ] - -[[package]] -name = "virtualenv" -version = "20.16.5" -description = "Virtual Python Environment builder" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ +virtualenv = [ {file = "virtualenv-20.16.5-py3-none-any.whl", hash = "sha256:d07dfc5df5e4e0dbc92862350ad87a36ed505b978f6c39609dc489eadd5b0d27"}, {file = "virtualenv-20.16.5.tar.gz", hash = "sha256:227ea1b9994fdc5ea31977ba3383ef296d7472ea85be9d6732e42a91c04e80da"}, ] - -[package.dependencies] -distlib = ">=0.3.5,<1" -filelock = ">=3.4.1,<4" -platformdirs = ">=2.4,<3" - -[package.extras] -docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"] -testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] - -[[package]] -name = "wcwidth" -version = "0.2.5" -description = "Measures the displayed width of unicode strings in a terminal" -category = "main" -optional = false -python-versions = "*" -files = [ +wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, ] - -[[package]] -name = "Werkzeug" -version = "2.2.2" -description = "The comprehensive WSGI web application library." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +Werkzeug = [ {file = "Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"}, {file = "Werkzeug-2.2.2.tar.gz", hash = "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f"}, ] - -[package.dependencies] -MarkupSafe = ">=2.1.1" - -[package.extras] -watchdog = ["watchdog"] - -[[package]] -name = "wrapt" -version = "1.14.1" -description = "Module for decorators, wrappers and monkey patching." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -files = [ +wrapt = [ {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, @@ -3565,70 +3620,15 @@ files = [ {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, ] - -[[package]] -name = "WTForms" -version = "3.0.1" -description = "Form validation and rendering for Python web development." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +WTForms = [ {file = "WTForms-3.0.1-py3-none-any.whl", hash = "sha256:837f2f0e0ca79481b92884962b914eba4e72b7a2daaf1f939c890ed0124b834b"}, {file = "WTForms-3.0.1.tar.gz", hash = "sha256:6b351bbb12dd58af57ffef05bc78425d08d1914e0fd68ee14143b7ade023c5bc"}, ] - -[package.dependencies] -MarkupSafe = "*" - -[package.extras] -email = ["email-validator"] - -[[package]] -name = "xdoctest" -version = "1.1.0" -description = "A rewrite of the builtin doctest module" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ +xdoctest = [ {file = "xdoctest-1.1.0-py3-none-any.whl", hash = "sha256:da330c4dacee51f3c785820bc743188fb6f7c64c5fa1c54bff8836b3cf23d69b"}, {file = "xdoctest-1.1.0.tar.gz", hash = "sha256:0fd4fad7932f0a2f082dfdfb857dd6ca41603757586c39b1e5b4d333fc389f8a"}, ] - -[package.dependencies] -colorama = {version = "*", optional = true, markers = "platform_system == \"Windows\" and extra == \"colors\""} -Pygments = {version = "*", optional = true, markers = "python_version >= \"3.5.0\" and extra == \"colors\""} -six = "*" - -[package.extras] -all = ["IPython", "IPython", "Pygments", "Pygments", "attrs", "cmake", "codecov", "colorama", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert", "ninja", "pybind11", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest-cov", "pytest-cov", "pytest-cov", "pytest-cov", "scikit-build", "six", "typing"] -all-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "cmake (==3.21.2)", "codecov (==2.0.15)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==5.2.0)", "ipykernel (==6.0.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==6.1.5)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "ninja (==1.10.2)", "pybind11 (==2.7.1)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "scikit-build (==0.11.1)", "six (==1.11.0)", "typing (==3.7.4)"] -colors = ["Pygments", "Pygments", "colorama"] -jupyter = ["IPython", "IPython", "attrs", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert"] -optional = ["IPython", "IPython", "Pygments", "Pygments", "attrs", "colorama", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert", "pyflakes", "tomli"] -optional-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==5.2.0)", "ipykernel (==6.0.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==6.1.5)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "pyflakes (==2.2.0)", "tomli (==0.2.0)"] -runtime-strict = ["six (==1.11.0)"] -tests = ["cmake", "codecov", "ninja", "pybind11", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest", "pytest-cov", "pytest-cov", "pytest-cov", "pytest-cov", "scikit-build", "typing"] -tests-strict = ["cmake (==3.21.2)", "codecov (==2.0.15)", "ninja (==1.10.2)", "pybind11 (==2.7.1)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==2.8.1)", "pytest-cov (==2.8.1)", "pytest-cov (==2.9.0)", "pytest-cov (==3.0.0)", "scikit-build (==0.11.1)", "typing (==3.7.4)"] - -[[package]] -name = "zipp" -version = "3.10.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ +zipp = [ {file = "zipp-3.10.0-py3-none-any.whl", hash = "sha256:4fcb6f278987a6605757302a6e40e896257570d11c51628968ccb2a47e80c6c1"}, {file = "zipp-3.10.0.tar.gz", hash = "sha256:7a7262fd930bd3e36c50b9a64897aec3fafff3dfdeec9623ae22b40e93f99bb8"}, ] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] -testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] - -[metadata] -lock-version = "2.0" -python-versions = ">=3.9,<3.12" -content-hash = "2fd5138221eabec441b601bb3769be478bed42099e72e20f7b8aaa1c1a888909" diff --git a/spiffworkflow-backend/pyproject.toml b/spiffworkflow-backend/pyproject.toml index 4f47921b..fbaf1127 100644 --- a/spiffworkflow-backend/pyproject.toml +++ b/spiffworkflow-backend/pyproject.toml @@ -27,7 +27,7 @@ flask-marshmallow = "*" flask-migrate = "*" flask-restful = "*" werkzeug = "*" -SpiffWorkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "main"} +SpiffWorkflow = {git = "https://github.com/sartography/SpiffWorkflow", rev = "feature/remove-loop-reset"} # SpiffWorkflow = {develop = true, path = "../SpiffWorkflow" } sentry-sdk = "^1.10" sphinx-autoapi = "^2.0" diff --git a/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/manual_task_with_subprocesses.bpmn b/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/manual_task_with_subprocesses.bpmn index d1b462f1..11d2dbb0 100644 --- a/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/manual_task_with_subprocesses.bpmn +++ b/spiffworkflow-backend/tests/data/manual_task_with_subprocesses/manual_task_with_subprocesses.bpmn @@ -5,13 +5,14 @@ Flow_0stlaxe - Flow_1and8ze + Flow_1ygcsbt ## Hello Flow_1fktmf7 + Flow_1t9ywmr Flow_09gjylo @@ -22,7 +23,6 @@ - Flow_09gjylo @@ -38,33 +38,61 @@ Flow_00k1tii Flow_1b4o55k - set_in_top_level_subprocess = 1 + set_in_top_level_subprocess = 1 + +try: + a = set_in_test_process_to_call_script + we_move_on = True +except: + we_move_on = False Flow_1i7syph - Flow_1and8ze + Flow_187mcqe + + Flow_187mcqe + Flow_0lw7sda + Flow_1t9ywmr + + + + we_move_on == True + + + + Flow_0lw7sda + Flow_1ygcsbt + set_top_level_process_script_after_gate = 1 + + - - + + - - + + + + + - - + + + + + @@ -78,14 +106,28 @@ - - - - + + + + + + + + + + + + + + + + + + From 3855f168a4673ffc600ff10b84b433ccd4bd7e8c Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 10 Mar 2023 17:58:31 -0500 Subject: [PATCH 22/48] set permissions does not run without this --- .../services/workflow_execution_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index ef484fe6..4c107b65 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -96,7 +96,7 @@ class TaskModelSavingDelegate(EngineStepDelegate): def after_engine_steps(self, bpmn_process_instance: BpmnWorkflow) -> None: for waiting_spiff_task in bpmn_process_instance.get_tasks( - TaskState.WAITING | TaskState.CANCELLED + TaskState.WAITING | TaskState.CANCELLED | TaskState.READY ): task_model = TaskModel.query.filter_by( guid=str(waiting_spiff_task.id) From 90fa5e50d321ca2c04aacf3dda0763594fd8140d Mon Sep 17 00:00:00 2001 From: burnettk Date: Fri, 10 Mar 2023 18:17:25 -0500 Subject: [PATCH 23/48] undo much of our performance improvement, though hopefully using children will be more precise --- spiffworkflow-backend/keycloak/test_user_lists/status | 1 - .../services/workflow_execution_service.py | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/spiffworkflow-backend/keycloak/test_user_lists/status b/spiffworkflow-backend/keycloak/test_user_lists/status index 151cfdf7..3b57deff 100644 --- a/spiffworkflow-backend/keycloak/test_user_lists/status +++ b/spiffworkflow-backend/keycloak/test_user_lists/status @@ -42,7 +42,6 @@ desktop3.sme@status.im,196 desktop4.sme@status.im,197 desktop5.sme@status.im,198 fin@status.im,118 -finance.lead@status.im,128 finance_user1@status.im fluffy.project-lead@status.im,162 harmeet@status.im,109 diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index 4c107b65..7b17cb82 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -95,8 +95,13 @@ class TaskModelSavingDelegate(EngineStepDelegate): db.session.commit() def after_engine_steps(self, bpmn_process_instance: BpmnWorkflow) -> None: + # excludes FUTURE and COMPLETED. the others were required to get PP1 to go to completion. for waiting_spiff_task in bpmn_process_instance.get_tasks( - TaskState.WAITING | TaskState.CANCELLED | TaskState.READY + TaskState.WAITING + | TaskState.CANCELLED + | TaskState.READY + | TaskState.MAYBE + | TaskState.LIKELY ): task_model = TaskModel.query.filter_by( guid=str(waiting_spiff_task.id) From 812e93047c364683d3dbd028303f68e605063828 Mon Sep 17 00:00:00 2001 From: jasquat Date: Sat, 11 Mar 2023 13:22:30 -0500 Subject: [PATCH 24/48] fixed tests and added some comments for future fixes we will need with spiff without loop reset --- .../services/process_instance_processor.py | 1 + .../services/workflow_execution_service.py | 37 +++++----- .../unit/test_process_instance_processor.py | 72 ++++++++++++------- 3 files changed, 68 insertions(+), 42 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 5242c066..3d7b1a20 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -1833,6 +1833,7 @@ class ProcessInstanceProcessor: human_task.completed_by_user_id = user.id human_task.completed = True + human_task.task_status = spiff_task.get_state_name() db.session.add(human_task) # FIXME: remove when we switch over to using tasks only diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index 7b17cb82..1d12b976 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -95,25 +95,26 @@ class TaskModelSavingDelegate(EngineStepDelegate): db.session.commit() def after_engine_steps(self, bpmn_process_instance: BpmnWorkflow) -> None: - # excludes FUTURE and COMPLETED. the others were required to get PP1 to go to completion. - for waiting_spiff_task in bpmn_process_instance.get_tasks( - TaskState.WAITING - | TaskState.CANCELLED - | TaskState.READY - | TaskState.MAYBE - | TaskState.LIKELY - ): - task_model = TaskModel.query.filter_by( - guid=str(waiting_spiff_task.id) - ).first() - if task_model is None: - task_model = TaskService.find_or_create_task_model_from_spiff_task( - waiting_spiff_task, self.process_instance, self.serializer + if self.should_update_task_model(): + # excludes FUTURE and COMPLETED. the others were required to get PP1 to go to completion. + for waiting_spiff_task in bpmn_process_instance.get_tasks( + TaskState.WAITING + | TaskState.CANCELLED + | TaskState.READY + | TaskState.MAYBE + | TaskState.LIKELY + ): + task_model = TaskModel.query.filter_by( + guid=str(waiting_spiff_task.id) + ).first() + if task_model is None: + task_model = TaskService.find_or_create_task_model_from_spiff_task( + waiting_spiff_task, self.process_instance, self.serializer + ) + TaskService.update_task_model_and_add_to_db_session( + task_model, waiting_spiff_task, self.serializer ) - TaskService.update_task_model_and_add_to_db_session( - task_model, waiting_spiff_task, self.serializer - ) - db.session.commit() + db.session.commit() class StepDetailLoggingDelegate(EngineStepDelegate): diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py index f124bcd9..e1618f61 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py @@ -1,4 +1,6 @@ """Test_process_instance_processor.""" +from uuid import UUID + import pytest from flask import g from flask.app import Flask @@ -340,6 +342,18 @@ class TestProcessInstanceProcessor(BaseTest): processor, spiff_manual_task, {}, initiator_user, human_task_one ) + process_instance = ProcessInstanceModel.query.filter_by( + id=process_instance.id + ).first() + processor = ProcessInstanceProcessor(process_instance) + human_task_one = process_instance.active_human_tasks[0] + spiff_manual_task = processor.bpmn_process_instance.get_task( + UUID(human_task_one.task_id) + ) + ProcessInstanceService.complete_form_task( + processor, spiff_manual_task, {}, initiator_user, human_task_one + ) + # recreate variables to ensure all bpmn json was recreated from scratch from the db process_instance_relookup = ProcessInstanceModel.query.filter_by( id=process_instance.id @@ -347,34 +361,41 @@ class TestProcessInstanceProcessor(BaseTest): processor_final = ProcessInstanceProcessor(process_instance_relookup) assert process_instance_relookup.status == "complete" - first_data_set = {"set_in_top_level_script": 1} - second_data_set = {**first_data_set, **{"set_in_top_level_subprocess": 1}} - third_data_set = { - **second_data_set, - **{"set_in_test_process_to_call_script": 1}, - } - expected_task_data = { - "top_level_script": first_data_set, - "manual_task": first_data_set, - "top_level_subprocess_script": second_data_set, - "top_level_subprocess": second_data_set, - "test_process_to_call_script": third_data_set, - "top_level_call_activity": third_data_set, - "end_event_of_manual_task_model": third_data_set, - } + # first_data_set = {"set_in_top_level_script": 1} + # second_data_set = {**first_data_set, **{"set_in_top_level_subprocess": 1}} + # third_data_set = { + # **second_data_set, + # **{"set_in_test_process_to_call_script": 1}, + # } + # expected_task_data = { + # "top_level_script": first_data_set, + # "manual_task": first_data_set, + # "top_level_subprocess_script": second_data_set, + # "top_level_subprocess": second_data_set, + # "test_process_to_call_script": third_data_set, + # "top_level_call_activity": third_data_set, + # "end_event_of_manual_task_model": third_data_set, + # } all_spiff_tasks = processor_final.bpmn_process_instance.get_tasks() assert len(all_spiff_tasks) > 1 for spiff_task in all_spiff_tasks: assert spiff_task.state == TaskState.COMPLETED - spiff_task_name = spiff_task.task_spec.name - if spiff_task_name in expected_task_data: - spiff_task_data = expected_task_data[spiff_task_name] - failure_message = ( - f"Found unexpected task data on {spiff_task_name}. " - f"Expected: {spiff_task_data}, Found: {spiff_task.data}" - ) - assert spiff_task.data == spiff_task_data, failure_message + # FIXME: Checking task data cannot work with the feature/remove-loop-reset branch + # of SiffWorkflow. This is because it saves script data to the python_env and NOT + # to task.data. We may need to either create a new column on TaskModel to put the python_env + # data or we could just shove it back onto the task data when adding to the database. + # Right now everything works in practice because the python_env data is on the top level workflow + # and so is always there but is also always the most recent. If we want to replace spiff_step_details + # with TaskModel then we'll need some way to store python_env on each task. + # spiff_task_name = spiff_task.task_spec.name + # if spiff_task_name in expected_task_data: + # spiff_task_data = expected_task_data[spiff_task_name] + # failure_message = ( + # f"Found unexpected task data on {spiff_task_name}. " + # f"Expected: {spiff_task_data}, Found: {spiff_task.data}" + # ) + # assert spiff_task.data == spiff_task_data, failure_message def test_does_not_recreate_human_tasks_on_multiple_saves( self, @@ -491,4 +512,7 @@ class TestProcessInstanceProcessor(BaseTest): # this is just asserting the way the functionality currently works in spiff. # we would actually expect this to change one day if we stop reusing the same guid # when we re-do a task. - assert human_task_two.task_id == human_task_one.task_id + # assert human_task_two.task_id == human_task_one.task_id + + # EDIT: when using feature/remove-loop-reset branch of SpiffWorkflow, these should be different. + assert human_task_two.task_id != human_task_one.task_id From 0decfd9237eb20c744bba72d95d229a0f015548c Mon Sep 17 00:00:00 2001 From: Dan Date: Sat, 11 Mar 2023 14:38:57 -0500 Subject: [PATCH 25/48] update the lock file on the connector proxy to handle a bug fix on the http-connector --- connector-proxy-demo/poetry.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/connector-proxy-demo/poetry.lock b/connector-proxy-demo/poetry.lock index 5747b12e..301489b7 100644 --- a/connector-proxy-demo/poetry.lock +++ b/connector-proxy-demo/poetry.lock @@ -101,7 +101,7 @@ version = "0.1.0" description = "Make HTTP Requests available to SpiffWorkflow Service Tasks" category = "main" optional = false -python-versions = "^3.11" +python-versions = "^3.9" develop = false [package.dependencies] @@ -111,7 +111,7 @@ requests = "^2.28.1" type = "git" url = "https://github.com/sartography/connector-http.git" reference = "HEAD" -resolved_reference = "337671b38f47bd8a3113bc6fa85b987828c4ee66" +resolved_reference = "8ae0b27d5a3e79562f3d1da01be2dcadef49a195" [[package]] name = "connector-slack" @@ -333,7 +333,7 @@ version = "0.1.0" description = "A blueprint that can allow (and limit) SpiffWorkflow's Service Tasks access to an organizations API's, such as connections to AWS Services and existing applications." category = "main" optional = false -python-versions = "^3.10" +python-versions = "^3.9" develop = false [package.dependencies] @@ -344,7 +344,7 @@ Flask-OAuthlib = "^0.9.6" type = "git" url = "https://github.com/sartography/spiffworkflow-proxy" reference = "HEAD" -resolved_reference = "cfe9b93665e10390a2e64c492c57bd2613364588" +resolved_reference = "6cb2bbea923946cb2db3bf571a4ce28c776434b9" [[package]] name = "urllib3" From c591dbfefe8efd86410977e3d1a6ea004312d737 Mon Sep 17 00:00:00 2001 From: burnettk Date: Sun, 12 Mar 2023 18:20:29 -0400 Subject: [PATCH 26/48] no op cipher for testing --- spiffworkflow-backend/bin/deploy | 48 ------ spiffworkflow-backend/bin/run_server_locally | 3 +- spiffworkflow-backend/conftest.py | 2 +- spiffworkflow-backend/docker-compose.yml | 2 +- spiffworkflow-backend/noxfile.py | 2 +- spiffworkflow-backend/poetry.lock | 137 +++++++++++++++++- spiffworkflow-backend/pyproject.toml | 1 + .../src/spiffworkflow_backend/__init__.py | 30 +++- .../spiffworkflow_backend/config/default.py | 7 + .../services/secret_service.py | 11 +- .../services/service_task_service.py | 86 +++++------ 11 files changed, 229 insertions(+), 100 deletions(-) delete mode 100755 spiffworkflow-backend/bin/deploy diff --git a/spiffworkflow-backend/bin/deploy b/spiffworkflow-backend/bin/deploy deleted file mode 100755 index ebfd9a55..00000000 --- a/spiffworkflow-backend/bin/deploy +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash - -function error_handler() { - >&2 echo "Exited with BAD EXIT CODE '${2}' in ${0} script at line: ${1}." - exit "$2" -} -trap 'error_handler ${LINENO} $?' ERR -set -o errtrace -o errexit -o nounset -o pipefail - -if [[ -z "${SPIFFWORKFLOW_BACKEND_ENV:-}" ]]; then - export SPIFFWORKFLOW_BACKEND_ENV=staging -fi - -if [[ -z "${FLASK_SESSION_SECRET_KEY:-}" ]]; then - export FLASK_SESSION_SECRET_KEY=staging_super_secret_key_dont_tell_anyone -fi - -if [[ -z "${SPIFFWORKFLOW_BACKEND_MYSQL_ROOT_PASSWORD:-}" ]]; then - export SPIFFWORKFLOW_BACKEND_MYSQL_ROOT_PASSWORD=St4g3Th1515 -fi - -if [[ -z "${SPIFFWORKFLOW_BACKEND_DATABASE_NAME:-}" ]]; then - export SPIFFWORKFLOW_BACKEND_DATABASE_NAME=spiffworkflow_backend_staging -fi - -if [[ -z "${SPIFFWORKFLOW_BACKEND_DATABASE_DOCKER_RESTART_POLICY:-}" ]]; then - export SPIFFWORKFLOW_BACKEND_DATABASE_DOCKER_RESTART_POLICY=always -fi - -if [[ -z "${SPIFFWORKFLOW_BACKEND_DOCKER_COMPOSE_PROFILE:-}" ]]; then - export SPIFFWORKFLOW_BACKEND_DOCKER_COMPOSE_PROFILE=run -fi - -if [[ -z "${SPIFFWORKFLOW_BACKEND_URL_FOR_FRONTEND:-}" ]]; then - export SPIFFWORKFLOW_BACKEND_URL_FOR_FRONTEND='http://167.172.242.138:7001' -fi - -if [[ -z "${SPIFFWORKFLOW_BACKEND_URL:-}" ]]; then - export SPIFFWORKFLOW_BACKEND_URL='http://167.172.242.138:7000' -fi - -if [[ -z "${SPIFFWORKFLOW_BACKEND_OPEN_ID_SERVER_URL:-}" ]]; then - export SPIFFWORKFLOW_BACKEND_OPEN_ID_SERVER_URL='http://167.172.242.138:7002' -fi - -git pull -./bin/build_and_run_with_docker_compose -./bin/wait_for_server_to_be_up diff --git a/spiffworkflow-backend/bin/run_server_locally b/spiffworkflow-backend/bin/run_server_locally index d27ddf3b..67443551 100755 --- a/spiffworkflow-backend/bin/run_server_locally +++ b/spiffworkflow-backend/bin/run_server_locally @@ -20,7 +20,8 @@ fi SPIFFWORKFLOW_BACKEND_BPMN_SPEC_ABSOLUTE_DIR=$(./bin/find_sample_process_models) export SPIFFWORKFLOW_BACKEND_BPMN_SPEC_ABSOLUTE_DIR -export FLASK_SESSION_SECRET_KEY=super_secret_key +# export FLASK_SESSION_SECRET_KEY="super_secret_key" +export FLASK_SESSION_SECRET_KEY="e7711a3ba96c46c68e084a86952de16f" export SPIFFWORKFLOW_BACKEND_APPLICATION_ROOT="/" if [[ -n "${SPIFFWORKFLOW_BACKEND_LOAD_FIXTURE_DATA:-}" ]]; then diff --git a/spiffworkflow-backend/conftest.py b/spiffworkflow-backend/conftest.py index 3adf8725..304008d0 100644 --- a/spiffworkflow-backend/conftest.py +++ b/spiffworkflow-backend/conftest.py @@ -37,7 +37,7 @@ from spiffworkflow_backend import create_app # noqa: E402 def app() -> Flask: """App.""" os.environ["SPIFFWORKFLOW_BACKEND_ENV"] = "unit_testing" - os.environ["FLASK_SESSION_SECRET_KEY"] = "super_secret_key" + os.environ["FLASK_SESSION_SECRET_KEY"] = "e7711a3ba96c46c68e084a86952de16f" app = create_app() return app diff --git a/spiffworkflow-backend/docker-compose.yml b/spiffworkflow-backend/docker-compose.yml index 8fe55281..7100a536 100644 --- a/spiffworkflow-backend/docker-compose.yml +++ b/spiffworkflow-backend/docker-compose.yml @@ -51,7 +51,7 @@ services: context: . environment: - FLASK_DEBUG=0 - - FLASK_SESSION_SECRET_KEY=${FLASK_SESSION_SECRET_KEY:-super_secret_key} + - FLASK_SESSION_SECRET_KEY=${FLASK_SESSION_SECRET_KEY:-e7711a3ba96c46c68e084a86952de16f} - SPIFFWORKFLOW_BACKEND_APPLICATION_ROOT=/ - SPIFFWORKFLOW_BACKEND_BPMN_SPEC_ABSOLUTE_DIR=/app/process_models - SPIFFWORKFLOW_BACKEND_DATABASE_URI=mysql+mysqlconnector://root:${SPIFFWORKFLOW_BACKEND_MYSQL_ROOT_DATABASE:-my-secret-pw}@localhost:7003/${SPIFFWORKFLOW_BACKEND_DATABASE_NAME:-spiffworkflow_backend_development} diff --git a/spiffworkflow-backend/noxfile.py b/spiffworkflow-backend/noxfile.py index e47f7005..632f33d4 100644 --- a/spiffworkflow-backend/noxfile.py +++ b/spiffworkflow-backend/noxfile.py @@ -40,7 +40,7 @@ def setup_database(session: Session) -> None: os.getcwd(), "instance", "testing" ) flask_env_key = "FLASK_SESSION_SECRET_KEY" - session.env[flask_env_key] = "super_secret_key" + session.env[flask_env_key] = "e7711a3ba96c46c68e084a86952de16f" session.env["FLASK_APP"] = "src/spiffworkflow_backend" session.env["SPIFFWORKFLOW_BACKEND_ENV"] = "unit_testing" session.run("flask", "db", "upgrade") diff --git a/spiffworkflow-backend/poetry.lock b/spiffworkflow-backend/poetry.lock index 338f96dd..33503bb7 100644 --- a/spiffworkflow-backend/poetry.lock +++ b/spiffworkflow-backend/poetry.lock @@ -254,6 +254,17 @@ category = "main" optional = false python-versions = ">=3.6" +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pycparser = "*" + [[package]] name = "cfgv" version = "3.3.1" @@ -403,6 +414,27 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1 [package.extras] toml = ["tomli"] +[[package]] +name = "cryptography" +version = "39.0.2" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +pep8test = ["black", "check-manifest", "mypy", "ruff", "types-pytz", "types-requests"] +sdist = ["setuptools-rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-shard (>=0.1.2)", "pytest-subtests", "pytest-xdist", "pytz"] +test-randomorder = ["pytest-randomly"] +tox = ["tox"] + [[package]] name = "darglint" version = "1.8.1" @@ -1261,6 +1293,14 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +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.*" + [[package]] name = "pycryptodome" version = "3.17" @@ -2234,7 +2274,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools" [metadata] lock-version = "1.1" python-versions = ">=3.9,<3.12" -content-hash = "eac3b5aa78efea376a9e23e32f9e6853cc22c17a2a21b41e30800cb7c807d017" +content-hash = "7ab6d5021406b573edfdca4f9e0f5e62c41a6f6ea09d34154df72454887e3670" [metadata.files] alabaster = [ @@ -2339,6 +2379,72 @@ certifi = [ {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, ] +cffi = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] cfgv = [ {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, @@ -2435,6 +2541,31 @@ coverage = [ {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"}, {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"}, ] +cryptography = [ + {file = "cryptography-39.0.2-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:2725672bb53bb92dc7b4150d233cd4b8c59615cd8288d495eaa86db00d4e5c06"}, + {file = "cryptography-39.0.2-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:23df8ca3f24699167daf3e23e51f7ba7334d504af63a94af468f468b975b7dd7"}, + {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:eb40fe69cfc6f5cdab9a5ebd022131ba21453cf7b8a7fd3631f45bbf52bed612"}, + {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc0521cce2c1d541634b19f3ac661d7a64f9555135e9d8af3980965be717fd4a"}, + {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffd394c7896ed7821a6d13b24657c6a34b6e2650bd84ae063cf11ccffa4f1a97"}, + {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:e8a0772016feeb106efd28d4a328e77dc2edae84dfbac06061319fdb669ff828"}, + {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8f35c17bd4faed2bc7797d2a66cbb4f986242ce2e30340ab832e5d99ae60e011"}, + {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b49a88ff802e1993b7f749b1eeb31134f03c8d5c956e3c125c75558955cda536"}, + {file = "cryptography-39.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c682e736513db7d04349b4f6693690170f95aac449c56f97415c6980edef5"}, + {file = "cryptography-39.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:d7d84a512a59f4412ca8549b01f94be4161c94efc598bf09d027d67826beddc0"}, + {file = "cryptography-39.0.2-cp36-abi3-win32.whl", hash = "sha256:c43ac224aabcbf83a947eeb8b17eaf1547bce3767ee2d70093b461f31729a480"}, + {file = "cryptography-39.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:788b3921d763ee35dfdb04248d0e3de11e3ca8eb22e2e48fef880c42e1f3c8f9"}, + {file = "cryptography-39.0.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d15809e0dbdad486f4ad0979753518f47980020b7a34e9fc56e8be4f60702fac"}, + {file = "cryptography-39.0.2-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:50cadb9b2f961757e712a9737ef33d89b8190c3ea34d0fb6675e00edbe35d074"}, + {file = "cryptography-39.0.2-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:103e8f7155f3ce2ffa0049fe60169878d47a4364b277906386f8de21c9234aa1"}, + {file = "cryptography-39.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6236a9610c912b129610eb1a274bdc1350b5df834d124fa84729ebeaf7da42c3"}, + {file = "cryptography-39.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e944fe07b6f229f4c1a06a7ef906a19652bdd9fd54c761b0ff87e83ae7a30354"}, + {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:35d658536b0a4117c885728d1a7032bdc9a5974722ae298d6c533755a6ee3915"}, + {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:30b1d1bfd00f6fc80d11300a29f1d8ab2b8d9febb6ed4a38a76880ec564fae84"}, + {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e029b844c21116564b8b61216befabca4b500e6816fa9f0ba49527653cae2108"}, + {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fa507318e427169ade4e9eccef39e9011cdc19534f55ca2f36ec3f388c1f70f3"}, + {file = "cryptography-39.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8bc0008ef798231fac03fe7d26e82d601d15bd16f3afaad1c6113771566570f3"}, + {file = "cryptography-39.0.2.tar.gz", hash = "sha256:bc5b871e977c8ee5a1bbc42fa8d19bcc08baf0c51cbf1586b0e87a2694dde42f"}, +] darglint = [ {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, @@ -2970,6 +3101,10 @@ pycodestyle = [ {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, ] +pycparser = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] pycryptodome = [ {file = "pycryptodome-3.17-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:2c5631204ebcc7ae33d11c43037b2dafe25e2ab9c1de6448eb6502ac69c19a56"}, {file = "pycryptodome-3.17-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:04779cc588ad8f13c80a060b0b1c9d1c203d051d8a43879117fe6b8aaf1cd3fa"}, diff --git a/spiffworkflow-backend/pyproject.toml b/spiffworkflow-backend/pyproject.toml index fbaf1127..3b3f09aa 100644 --- a/spiffworkflow-backend/pyproject.toml +++ b/spiffworkflow-backend/pyproject.toml @@ -73,6 +73,7 @@ types-dateparser = "^1.1.4.1" flask-jwt-extended = "^4.4.4" pylint = "^2.15.10" flask-simple-crypt = "^0.3.3" +cryptography = "^39.0.2" [tool.poetry.dev-dependencies] diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/__init__.py b/spiffworkflow-backend/src/spiffworkflow_backend/__init__.py index ed0b5c05..24020538 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/__init__.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/__init__.py @@ -1,4 +1,5 @@ """__init__.""" +import base64 import faulthandler import os import sys @@ -85,6 +86,14 @@ def start_scheduler( scheduler.start() +class NoOpCipher: + def encrypt(self, value: str) -> bytes: + return str.encode(value) + + def decrypt(self, value: str) -> str: + return value + + def create_app() -> flask.app.Flask: """Create_app.""" faulthandler.enable() @@ -134,10 +143,23 @@ def create_app() -> flask.app.Flask: configure_sentry(app) - cipher = SimpleCrypt() - app.config["FSC_EXPANSION_COUNT"] = 2048 - cipher.init_app(app) - app.config["CIPHER"] = cipher + encryption_lib = app.config.get("SPIFFWORKFLOW_BACKEND_ENCRYPTION_LIB") + if encryption_lib == "cryptography": + from cryptography.fernet import Fernet + app_secret_key = app.config.get("SECRET_KEY") + app_secret_key_bytes = app_secret_key.encode() + base64_key = base64.b64encode(app_secret_key_bytes) + fernet_cipher = Fernet(base64_key) + app.config["CIPHER"] = fernet_cipher + # for comparison against possibly-slow encryption libraries + elif encryption_lib == "no_op_cipher": + no_op_cipher = NoOpCipher() + app.config["CIPHER"] = no_op_cipher + else: + simple_crypt_cipher = SimpleCrypt() + app.config["FSC_EXPANSION_COUNT"] = 2048 + simple_crypt_cipher.init_app(app) + app.config["CIPHER"] = simple_crypt_cipher app.before_request(verify_token) app.before_request(AuthorizationService.check_for_permission) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py b/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py index fa9ad7a0..04136d36 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py @@ -55,6 +55,13 @@ SPIFFWORKFLOW_BACKEND_OPEN_ID_TENANT_SPECIFIC_FIELDS = environ.get( "SPIFFWORKFLOW_BACKEND_OPEN_ID_TENANT_SPECIFIC_FIELDS" ) +# cryptography or simple-crypt +SPIFFWORKFLOW_BACKEND_ENCRYPTION_LIB = environ.get( + # "SPIFFWORKFLOW_BACKEND_ENCRYPTION_LIB", default="cryptography" + "SPIFFWORKFLOW_BACKEND_ENCRYPTION_LIB", + default="no_op_cipher", +) + SPIFFWORKFLOW_BACKEND_LOG_TO_FILE = ( environ.get("SPIFFWORKFLOW_BACKEND_LOG_TO_FILE", default="false") == "true" ) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/secret_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/secret_service.py index f7c20e86..ca6a7e84 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/secret_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/secret_service.py @@ -15,7 +15,16 @@ class SecretService: @classmethod def _encrypt(cls, value: str) -> str: - encrypted_bytes: bytes = current_app.config["CIPHER"].encrypt(value) + encrypted_bytes: bytes = b"" + if ( + current_app.config.get("SPIFFWORKFLOW_BACKEND_ENCRYPTION_LIB") + == "cryptography" + ): + # cryptography needs a bytes object + value_as_bytes = str.encode(value) + encrypted_bytes = current_app.config["CIPHER"].encrypt(value_as_bytes) + else: + encrypted_bytes = current_app.config["CIPHER"].encrypt(value) return encrypted_bytes.decode(cls.CIPHER_ENCODING) @classmethod diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/service_task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/service_task_service.py index 11387fe1..6862a5eb 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/service_task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/service_task_service.py @@ -91,54 +91,56 @@ class ServiceTaskDelegate: def call_connector(name: str, bpmn_params: Any, task_data: Any) -> str: """Calls a connector via the configured proxy.""" call_url = f"{connector_proxy_url()}/v1/do/{name}" - with sentry_sdk.start_span(op="call-connector", description=call_url): - params = { - k: ServiceTaskDelegate.check_prefixes(v["value"]) - for k, v in bpmn_params.items() - } - params["spiff__task_data"] = task_data + with sentry_sdk.start_span(op="connector_by_name", description=name): + with sentry_sdk.start_span(op="call-connector", description=call_url): + params = { + k: ServiceTaskDelegate.check_prefixes(v["value"]) + for k, v in bpmn_params.items() + } + params["spiff__task_data"] = task_data - proxied_response = requests.post(call_url, json=params) - response_text = proxied_response.text - json_parse_error = None + proxied_response = requests.post(call_url, json=params) + response_text = proxied_response.text + json_parse_error = None - if response_text == "": - response_text = "{}" - try: - parsed_response = json.loads(response_text) - except Exception as e: - json_parse_error = e - parsed_response = {} + if response_text == "": + response_text = "{}" + try: + parsed_response = json.loads(response_text) + except Exception as e: + json_parse_error = e + parsed_response = {} - if proxied_response.status_code >= 300: - message = ServiceTaskDelegate.get_message_for_status( - proxied_response.status_code - ) - error = ( - f"Received an unexpected response from service {name} : {message}" - ) - if "error" in parsed_response: - error += parsed_response["error"] - if json_parse_error: - error += ( - "A critical component (The connector proxy) is not responding" - " correctly." + if proxied_response.status_code >= 300: + message = ServiceTaskDelegate.get_message_for_status( + proxied_response.status_code + ) + error = ( + f"Received an unexpected response from service {name} :" + f" {message}" + ) + if "error" in parsed_response: + error += parsed_response["error"] + if json_parse_error: + error += ( + "A critical component (The connector proxy) is not" + " responding correctly." + ) + raise ConnectorProxyError(error) + elif json_parse_error: + raise ConnectorProxyError( + f"There is a problem with this connector: '{name}'. " + "Responses for connectors must be in JSON format. " ) - raise ConnectorProxyError(error) - elif json_parse_error: - raise ConnectorProxyError( - f"There is a problem with this connector: '{name}'. " - "Responses for connectors must be in JSON format. " - ) - if "refreshed_token_set" not in parsed_response: - return response_text + if "refreshed_token_set" not in parsed_response: + return response_text - secret_key = parsed_response["auth"] - refreshed_token_set = json.dumps(parsed_response["refreshed_token_set"]) - user_id = g.user.id if UserService.has_user() else None - SecretService.update_secret(secret_key, refreshed_token_set, user_id) - return json.dumps(parsed_response["api_response"]) + secret_key = parsed_response["auth"] + refreshed_token_set = json.dumps(parsed_response["refreshed_token_set"]) + user_id = g.user.id if UserService.has_user() else None + SecretService.update_secret(secret_key, refreshed_token_set, user_id) + return json.dumps(parsed_response["api_response"]) class ServiceTaskService: From bc4a199248db871c867dcf489837478d11b4d271 Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 13 Mar 2023 10:59:18 -0400 Subject: [PATCH 27/48] load tasks all at once when instantiating a process instance w/ burnettk --- .../src/spiffworkflow_backend/__init__.py | 1 + .../services/process_instance_processor.py | 33 ++++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/__init__.py b/spiffworkflow-backend/src/spiffworkflow_backend/__init__.py index 24020538..3266ae76 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/__init__.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/__init__.py @@ -146,6 +146,7 @@ def create_app() -> flask.app.Flask: encryption_lib = app.config.get("SPIFFWORKFLOW_BACKEND_ENCRYPTION_LIB") if encryption_lib == "cryptography": from cryptography.fernet import Fernet + app_secret_key = app.config.get("SECRET_KEY") app_secret_key_bytes = app_secret_key.encode() base64_key = base64.b64encode(app_secret_key_bytes) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 3d7b1a20..dec1e993 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -605,7 +605,9 @@ class ProcessInstanceProcessor: ] = task_definition.properties_json @classmethod - def _get_bpmn_process_dict(cls, bpmn_process: BpmnProcessModel) -> dict: + def _get_bpmn_process_dict( + cls, bpmn_process: BpmnProcessModel, get_tasks: bool = False + ) -> dict: json_data = JsonDataModel.query.filter_by( hash=bpmn_process.json_data_hash ).first() @@ -645,17 +647,38 @@ class ProcessInstanceProcessor: bpmn_process = process_instance_model.bpmn_process if bpmn_process is not None: - bpmn_process_dict = cls._get_bpmn_process_dict(bpmn_process) - spiff_bpmn_process_dict.update(bpmn_process_dict) + single_bpmn_process_dict = cls._get_bpmn_process_dict( + bpmn_process, get_tasks=True + ) + spiff_bpmn_process_dict.update(single_bpmn_process_dict) bpmn_subprocesses = BpmnProcessModel.query.filter_by( parent_process_id=bpmn_process.id ).all() + bpmn_subprocess_ids = {} for bpmn_subprocess in bpmn_subprocesses: - bpmn_process_dict = cls._get_bpmn_process_dict(bpmn_subprocess) + bpmn_subprocess_ids[bpmn_subprocess.id] = bpmn_subprocess.guid + single_bpmn_process_dict = cls._get_bpmn_process_dict( + bpmn_subprocess + ) spiff_bpmn_process_dict["subprocesses"][ bpmn_subprocess.guid - ] = bpmn_process_dict + ] = single_bpmn_process_dict + + tasks = TaskModel.query.filter( + TaskModel.bpmn_process_id.in_(bpmn_subprocess_ids.keys()) # type: ignore + ).all() + for task in tasks: + bpmn_subprocess_guid = bpmn_subprocess_ids[task.bpmn_process_id] + json_data = JsonDataModel.query.filter_by( + hash=task.json_data_hash + ).first() + spiff_bpmn_process_dict["subprocesses"][bpmn_subprocess_guid][ + "tasks" + ][task.guid] = task.properties_json + spiff_bpmn_process_dict["subprocesses"][bpmn_subprocess_guid][ + "tasks" + ][task.guid]["data"] = json_data.data return spiff_bpmn_process_dict From ce3ce4fb451c6d40933095a39566be4064c74e2f Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 13 Mar 2023 11:50:18 -0400 Subject: [PATCH 28/48] select intiator in filters when you don't have permission to search users. --- connector-proxy-demo/app.py | 2 +- .../routes/process_instances_controller.py | 1 + .../components/ProcessInstanceListTable.tsx | 76 ++++++++++++++----- .../src/hooks/UriListForPermissions.tsx | 1 + 4 files changed, 61 insertions(+), 19 deletions(-) diff --git a/connector-proxy-demo/app.py b/connector-proxy-demo/app.py index 8b2cff71..ed398b50 100644 --- a/connector-proxy-demo/app.py +++ b/connector-proxy-demo/app.py @@ -15,4 +15,4 @@ if app.config["ENV"] != "production": app.register_blueprint(proxy_blueprint) if __name__ == "__main__": - app.run(host="localhost", port=5000) \ No newline at end of file + app.run(host="localhost", port=7004) \ No newline at end of file diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py index 32d35ba3..f6c9ff66 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py @@ -272,6 +272,7 @@ def process_instance_list_for_me( with_relation_to_me=True, report_columns=report_columns, report_filter_by=report_filter_by, + process_initiator_username=process_initiator_username, ) diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx index 8c8f818f..794c6d24 100644 --- a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx +++ b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx @@ -37,6 +37,7 @@ import { modifyProcessIdentifierForPathParam, refreshAtInterval, } from '../helpers'; +import { useUriListForPermissions } from '../hooks/UriListForPermissions'; import PaginationForTable from './PaginationForTable'; import 'react-datepicker/dist/react-datepicker.css'; @@ -56,6 +57,7 @@ import { ReportFilter, User, ErrorForDisplay, + PermissionsToCheck, } from '../interfaces'; import ProcessModelSearch from './ProcessModelSearch'; import ProcessInstanceReportSearch from './ProcessInstanceReportSearch'; @@ -63,6 +65,8 @@ import ProcessInstanceListDeleteReport from './ProcessInstanceListDeleteReport'; import ProcessInstanceListSaveAsReport from './ProcessInstanceListSaveAsReport'; import { Notification } from './Notification'; import useAPIError from '../hooks/UseApiError'; +import { usePermissionFetcher } from '../hooks/PermissionService'; +import { Can } from '../contexts/Can'; const REFRESH_INTERVAL = 5; const REFRESH_TIMEOUT = 600; @@ -107,6 +111,13 @@ export default function ProcessInstanceListTable({ const navigate = useNavigate(); const { addError, removeError } = useAPIError(); + const { targetUris } = useUriListForPermissions(); + const permissionRequestData: PermissionsToCheck = { + [targetUris.userSearch]: ['GET'], + }; + const { ability } = usePermissionFetcher(permissionRequestData); + const canSearchUsers: boolean = ability.can('GET', targetUris.userSearch); + const [processInstances, setProcessInstances] = useState([]); const [reportMetadata, setReportMetadata] = useState(); const [pagination, setPagination] = useState(null); @@ -167,6 +178,9 @@ export default function ProcessInstanceListTable({ useState([]); const [processInitiatorSelection, setProcessInitiatorSelection] = useState(null); + const [processInitiatorText, setProcessInitiatorText] = + useState(null); + const lastRequestedInitatorSearchTerm = useRef(); const dateParametersToAlwaysFilterBy: dateParameters = useMemo(() => { @@ -202,7 +216,7 @@ export default function ProcessInstanceListTable({ }; const searchForProcessInitiator = (inputText: string) => { - if (inputText) { + if (inputText && canSearchUsers) { lastRequestedInitatorSearchTerm.current = inputText; HttpService.makeCallToBackend({ path: `/users/search?username_prefix=${inputText}`, @@ -590,8 +604,11 @@ export default function ProcessInstanceListTable({ if (processInitiatorSelection) { queryParamString += `&process_initiator_username=${processInitiatorSelection.username}`; + } else if (processInitiatorText) { + queryParamString += `&process_initiator_username=${processInitiatorText}`; } + const reportColumnsBase64 = encodeBase64(JSON.stringify(reportColumns())); queryParamString += `&report_columns=${reportColumnsBase64}`; const reportFilterByBase64 = encodeBase64(JSON.stringify(reportFilterBy())); @@ -1077,24 +1094,48 @@ export default function ProcessInstanceListTable({ /> - { - setProcessInitiatorSelection(event.selectedItem); - }} - id="process-instance-initiator-search" - data-qa="process-instance-initiator-search" - items={processInstanceInitiatorOptions} - itemToString={(processInstanceInitatorOption: User) => { - if (processInstanceInitatorOption) { - return processInstanceInitatorOption.username; + + {(hasAccess: boolean) => { + if (hasAccess) { + return ( + { + setProcessInitiatorSelection(event.selectedItem); + }} + id="process-instance-initiator-search" + data-qa="process-instance-initiator-search" + items={processInstanceInitiatorOptions} + itemToString={(processInstanceInitatorOption: User) => { + if (processInstanceInitatorOption) { + return processInstanceInitatorOption.username; + } + return null; + }} + placeholder="Start typing username" + titleText="Process Initiator" + selectedItem={processInitiatorSelection} + /> + ); + } else { + return ( + + setProcessInitiatorText(event.target.value) + } + /> + ); } - return null; }} - placeholder="Starting typing username" - titleText="Process Initiator" - selectedItem={processInitiatorSelection} - /> + {processStatusSearch()} @@ -1248,7 +1289,6 @@ export default function ProcessInstanceListTable({ ); } - console.log(column.accessor); if (column.accessor === 'process_model_display_name') { const pmStyle = { background: 'rgba(0, 0, 0, .02)' }; return ( diff --git a/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx b/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx index 92fe3639..d8c85534 100644 --- a/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx +++ b/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx @@ -29,6 +29,7 @@ export const useUriListForPermissions = () => { processModelPublishPath: `/v1.0/process-models/${params.process_model_id}/publish`, processModelShowPath: `/v1.0/process-models/${params.process_model_id}`, secretListPath: `/v1.0/secrets`, + userSearch: `/v1.0/users/search`, }; }, [params]); From 75910bcb8db1bf64a12866b16504c168e195aa84 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 13 Mar 2023 11:53:42 -0400 Subject: [PATCH 29/48] run_pyl --- .../config/permissions/example.yml | 7 +++++ .../components/ProcessInstanceListTable.tsx | 27 +++++++++---------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/example.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/example.yml index 26162510..a11578bd 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/example.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/example.yml @@ -52,6 +52,13 @@ permissions: allowed_permissions: [create, read, update, delete] uri: /tasks/* + # Everybody can start all intstances + create-test-instances: + groups: [ everybody ] + users: [ ] + allowed_permissions: [ create ] + uri: /process-instances/* + # Everyone can see everything (all groups, and processes are visible) read-all-process-groups: groups: [ everybody ] diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx index 794c6d24..4b0498b7 100644 --- a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx +++ b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx @@ -178,8 +178,9 @@ export default function ProcessInstanceListTable({ useState([]); const [processInitiatorSelection, setProcessInitiatorSelection] = useState(null); - const [processInitiatorText, setProcessInitiatorText] = - useState(null); + const [processInitiatorText, setProcessInitiatorText] = useState< + string | null + >(null); const lastRequestedInitatorSearchTerm = useRef(); @@ -608,7 +609,6 @@ export default function ProcessInstanceListTable({ queryParamString += `&process_initiator_username=${processInitiatorText}`; } - const reportColumnsBase64 = encodeBase64(JSON.stringify(reportColumns())); queryParamString += `&report_columns=${reportColumnsBase64}`; const reportFilterByBase64 = encodeBase64(JSON.stringify(reportFilterBy())); @@ -1122,18 +1122,17 @@ export default function ProcessInstanceListTable({ selectedItem={processInitiatorSelection} /> ); - } else { - return ( - - setProcessInitiatorText(event.target.value) - } - /> - ); } + return ( + + setProcessInitiatorText(event.target.value) + } + /> + ); }} From 1a25fbff88c5b4d4915c557d8851cf209fe77358 Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 13 Mar 2023 13:49:55 -0400 Subject: [PATCH 30/48] our main test is passing w/ burnettk --- .flake8 | 1 + spiffworkflow-backend/.flake8 | 1 + .../services/process_instance_processor.py | 12 +++-- .../services/task_service.py | 45 +++++++++++-------- .../services/workflow_execution_service.py | 39 +++++++++------- 5 files changed, 61 insertions(+), 37 deletions(-) diff --git a/.flake8 b/.flake8 index 9c54dc0e..6e5fa533 100644 --- a/.flake8 +++ b/.flake8 @@ -2,6 +2,7 @@ select = B,B9,C,D,DAR,E,F,N,RST,S,W ignore = E203,E501,RST201,RST203,RST301,W503,S410,S320 max-line-length = 120 +extend-ignore = E203 max-complexity = 30 docstring-convention = google rst-roles = class,const,func,meth,mod,ref diff --git a/spiffworkflow-backend/.flake8 b/spiffworkflow-backend/.flake8 index 2e6554e5..d73f1dba 100644 --- a/spiffworkflow-backend/.flake8 +++ b/spiffworkflow-backend/.flake8 @@ -2,6 +2,7 @@ select = B,B9,C,D,DAR,E,F,N,RST,S,W ignore = E203,E501,RST201,RST203,RST301,W503,S410,S320 max-line-length = 120 +extend-ignore = E203 max-complexity = 30 docstring-convention = google rst-roles = class,const,func,meth,mod,ref diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index dec1e993..589940d3 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -1084,16 +1084,20 @@ class ProcessInstanceProcessor: self._add_bpmn_process_definitions(bpmn_spec_dict) subprocesses = process_instance_data_dict.pop("subprocesses") - bpmn_process_parent = TaskService.add_bpmn_process( + bpmn_process_parent, new_task_models, new_json_data_models = TaskService.add_bpmn_process( process_instance_data_dict, self.process_instance_model ) for subprocess_task_id, subprocess_properties in subprocesses.items(): - TaskService.add_bpmn_process( + _bpmn_subprocess, subprocess_new_task_models, subprocess_new_json_data_models = TaskService.add_bpmn_process( subprocess_properties, self.process_instance_model, bpmn_process_parent, bpmn_process_guid=subprocess_task_id, ) + new_task_models.update(subprocess_new_task_models) + new_json_data_models.update(subprocess_new_json_data_models) + db.session.bulk_save_objects(new_task_models.values()) + db.session.bulk_save_objects(new_json_data_models.values()) def save(self) -> None: """Saves the current state of this processor to the database.""" @@ -1881,9 +1885,11 @@ class ProcessInstanceProcessor: db.session.add(details_model) # ####### - TaskService.update_task_model_and_add_to_db_session( + json_data = TaskService.update_task_model_and_add_to_db_session( task_model, spiff_task, self._serializer ) + if json_data is not None: + db.session.add(json_data) # this is the thing that actually commits the db transaction (on behalf of the other updates above as well) self.save() diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py index 775e2909..4495ba71 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py @@ -19,9 +19,10 @@ class TaskService: @classmethod def update_task_data_on_task_model( cls, task_model: TaskModel, task_data_dict: dict - ) -> None: + ) -> Optional[JsonDataModel]: task_data_json = json.dumps(task_data_dict, sort_keys=True) task_data_hash = sha256(task_data_json.encode("utf8")).hexdigest() + json_data_to_return = None if task_model.json_data_hash != task_data_hash: json_data = ( db.session.query(JsonDataModel.id) @@ -30,8 +31,9 @@ class TaskService: ) if json_data is None: json_data = JsonDataModel(hash=task_data_hash, data=task_data_dict) - db.session.add(json_data) + json_data_to_return = json_data task_model.json_data_hash = task_data_hash + return json_data_to_return @classmethod def update_task_model_and_add_to_db_session( @@ -39,7 +41,7 @@ class TaskService: task_model: TaskModel, spiff_task: SpiffTask, serializer: BpmnWorkflowSerializer, - ) -> None: + ) -> Optional[JsonDataModel]: """Updates properties_json and data on given task_model. This will NOT update start_in_seconds or end_in_seconds. @@ -48,8 +50,8 @@ class TaskService: spiff_task_data = new_properties_json.pop("data") task_model.properties_json = new_properties_json task_model.state = TaskStateNames[new_properties_json["state"]] - cls.update_task_data_on_task_model(task_model, spiff_task_data) - db.session.add(task_model) + json_data = cls.update_task_data_on_task_model(task_model, spiff_task_data) + return json_data @classmethod def find_or_create_task_model_from_spiff_task( @@ -57,13 +59,16 @@ class TaskService: spiff_task: SpiffTask, process_instance: ProcessInstanceModel, serializer: BpmnWorkflowSerializer, - ) -> TaskModel: + ) -> Tuple[Optional[BpmnProcessModel], TaskModel, dict[str, TaskModel], dict[str, JsonDataModel]]: spiff_task_guid = str(spiff_task.id) task_model: Optional[TaskModel] = TaskModel.query.filter_by( guid=spiff_task_guid ).first() + bpmn_process = None + new_task_models: dict[str, TaskModel] = {} + new_json_data_models: dict[str, JsonDataModel] = {} if task_model is None: - bpmn_process = cls.task_bpmn_process( + bpmn_process, new_task_models, new_json_data_models = cls.task_bpmn_process( spiff_task, process_instance, serializer ) task_model = TaskModel.query.filter_by(guid=spiff_task_guid).first() @@ -71,7 +76,7 @@ class TaskService: task_model = TaskModel( guid=spiff_task_guid, bpmn_process_id=bpmn_process.id ) - return task_model + return (bpmn_process, task_model, new_task_models, new_json_data_models) @classmethod def task_subprocess( @@ -96,34 +101,34 @@ class TaskService: spiff_task: SpiffTask, process_instance: ProcessInstanceModel, serializer: BpmnWorkflowSerializer, - ) -> BpmnProcessModel: + ) -> Tuple[BpmnProcessModel, dict[str, TaskModel], dict[str, JsonDataModel]]: subprocess_guid, subprocess = cls.task_subprocess(spiff_task) bpmn_process: Optional[BpmnProcessModel] = None + new_task_models: dict[str, TaskModel] = {} + new_json_data_models: dict[str, JsonDataModel] = {} if subprocess is None: bpmn_process = process_instance.bpmn_process # This is the top level workflow, which has no guid # check for bpmn_process_id because mypy doesn't realize bpmn_process can be None if process_instance.bpmn_process_id is None: - bpmn_process = cls.add_bpmn_process( + bpmn_process, new_task_models, new_json_data_models = cls.add_bpmn_process( serializer.workflow_to_dict( spiff_task.workflow._get_outermost_workflow() ), process_instance, ) - db.session.commit() else: bpmn_process = BpmnProcessModel.query.filter_by( guid=subprocess_guid ).first() if bpmn_process is None: - bpmn_process = cls.add_bpmn_process( + bpmn_process, new_task_models, new_json_data_models = cls.add_bpmn_process( serializer.workflow_to_dict(subprocess), process_instance, process_instance.bpmn_process, subprocess_guid, ) - db.session.commit() - return bpmn_process + return (bpmn_process, new_task_models, new_json_data_models) @classmethod def add_bpmn_process( @@ -132,7 +137,7 @@ class TaskService: process_instance: ProcessInstanceModel, bpmn_process_parent: Optional[BpmnProcessModel] = None, bpmn_process_guid: Optional[str] = None, - ) -> BpmnProcessModel: + ) -> Tuple[BpmnProcessModel, dict[str, TaskModel], dict[str, JsonDataModel]]: tasks = bpmn_process_dict.pop("tasks") bpmn_process_data_dict = bpmn_process_dict.pop("data") @@ -174,6 +179,8 @@ class TaskService: bpmn_process.parent_process_id = bpmn_process_parent.id db.session.add(bpmn_process) + new_task_models = {} + new_json_data_models = {} if bpmn_process_is_new: for task_id, task_properties in tasks.items(): task_data_dict = task_properties.pop("data") @@ -194,7 +201,9 @@ class TaskService: task_model.state = TaskStateNames[state_int] task_model.properties_json = task_properties - TaskService.update_task_data_on_task_model(task_model, task_data_dict) - db.session.add(task_model) + json_data = TaskService.update_task_data_on_task_model(task_model, task_data_dict) + new_task_models[task_model.guid] = task_model + if json_data is not None: + new_json_data_models[json_data.hash] = json_data - return bpmn_process + return (bpmn_process, new_task_models, new_json_data_models) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index 1d12b976..f9e78bd5 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -12,6 +12,7 @@ from SpiffWorkflow.task import TaskState from spiffworkflow_backend.exceptions.api_error import ApiError from spiffworkflow_backend.models.db import db +from spiffworkflow_backend.models.json_data import JsonDataModel from spiffworkflow_backend.models.message_instance import MessageInstanceModel from spiffworkflow_backend.models.message_instance_correlation import ( MessageInstanceCorrelationRuleModel, @@ -58,6 +59,8 @@ class TaskModelSavingDelegate(EngineStepDelegate): self.process_instance = process_instance self.current_task_model: Optional[TaskModel] = None + self.task_models: dict[str, TaskModel] = {} + self.json_data_models: dict[str, JsonDataModel] = {} self.serializer = serializer def should_update_task_model(self) -> bool: @@ -66,15 +69,15 @@ class TaskModelSavingDelegate(EngineStepDelegate): Use the bpmn_process_id to do this. """ return self.process_instance.bpmn_process_id is not None - # return False def will_complete_task(self, spiff_task: SpiffTask) -> None: if self.should_update_task_model(): - self.current_task_model = ( - TaskService.find_or_create_task_model_from_spiff_task( - spiff_task, self.process_instance, self.serializer - ) + _bpmn_process, task_model, new_task_models, new_json_data_models = TaskService.find_or_create_task_model_from_spiff_task( + spiff_task, self.process_instance, self.serializer ) + self.current_task_model = task_model + self.task_models.update(new_task_models) + self.json_data_models.update(new_json_data_models) self.current_task_model.start_in_seconds = time.time() if self.secondary_engine_step_delegate: self.secondary_engine_step_delegate.will_complete_task(spiff_task) @@ -82,14 +85,18 @@ class TaskModelSavingDelegate(EngineStepDelegate): def did_complete_task(self, spiff_task: SpiffTask) -> None: if self.current_task_model and self.should_update_task_model(): self.current_task_model.end_in_seconds = time.time() - TaskService.update_task_model_and_add_to_db_session( + json_data = TaskService.update_task_model_and_add_to_db_session( self.current_task_model, spiff_task, self.serializer ) - db.session.add(self.current_task_model) + if json_data is not None: + self.json_data_models[json_data.hash] = json_data + self.task_models[self.current_task_model.guid] = self.current_task_model if self.secondary_engine_step_delegate: self.secondary_engine_step_delegate.did_complete_task(spiff_task) def save(self, _commit: bool = True) -> None: + db.session.bulk_save_objects(self.task_models.values()) + db.session.bulk_save_objects(self.json_data_models.values()) if self.secondary_engine_step_delegate: self.secondary_engine_step_delegate.save(commit=False) db.session.commit() @@ -104,17 +111,17 @@ class TaskModelSavingDelegate(EngineStepDelegate): | TaskState.MAYBE | TaskState.LIKELY ): - task_model = TaskModel.query.filter_by( - guid=str(waiting_spiff_task.id) - ).first() - if task_model is None: - task_model = TaskService.find_or_create_task_model_from_spiff_task( - waiting_spiff_task, self.process_instance, self.serializer - ) - TaskService.update_task_model_and_add_to_db_session( + _bpmn_process, task_model, new_task_models, new_json_data_models = TaskService.find_or_create_task_model_from_spiff_task( + waiting_spiff_task, self.process_instance, self.serializer + ) + self.task_models.update(new_task_models) + self.json_data_models.update(new_json_data_models) + json_data = TaskService.update_task_model_and_add_to_db_session( task_model, waiting_spiff_task, self.serializer ) - db.session.commit() + self.task_models[task_model.guid] = task_model + if json_data is not None: + self.json_data_models[json_data.hash] = json_data class StepDetailLoggingDelegate(EngineStepDelegate): From 285767bf5a21011f2371f93ecb10997c5df50cb4 Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 13 Mar 2023 14:04:03 -0400 Subject: [PATCH 31/48] pyl w/ burnettk --- .../services/process_instance_processor.py | 12 ++++-- .../services/task_service.py | 42 ++++++++++++------- .../services/workflow_execution_service.py | 12 ++++-- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 589940d3..2b7f0b3b 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -1084,11 +1084,17 @@ class ProcessInstanceProcessor: self._add_bpmn_process_definitions(bpmn_spec_dict) subprocesses = process_instance_data_dict.pop("subprocesses") - bpmn_process_parent, new_task_models, new_json_data_models = TaskService.add_bpmn_process( - process_instance_data_dict, self.process_instance_model + bpmn_process_parent, new_task_models, new_json_data_models = ( + TaskService.add_bpmn_process( + process_instance_data_dict, self.process_instance_model + ) ) for subprocess_task_id, subprocess_properties in subprocesses.items(): - _bpmn_subprocess, subprocess_new_task_models, subprocess_new_json_data_models = TaskService.add_bpmn_process( + ( + _bpmn_subprocess, + subprocess_new_task_models, + subprocess_new_json_data_models, + ) = TaskService.add_bpmn_process( subprocess_properties, self.process_instance_model, bpmn_process_parent, diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py index 4495ba71..75a04f5d 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py @@ -59,7 +59,12 @@ class TaskService: spiff_task: SpiffTask, process_instance: ProcessInstanceModel, serializer: BpmnWorkflowSerializer, - ) -> Tuple[Optional[BpmnProcessModel], TaskModel, dict[str, TaskModel], dict[str, JsonDataModel]]: + ) -> Tuple[ + Optional[BpmnProcessModel], + TaskModel, + dict[str, TaskModel], + dict[str, JsonDataModel], + ]: spiff_task_guid = str(spiff_task.id) task_model: Optional[TaskModel] = TaskModel.query.filter_by( guid=spiff_task_guid @@ -111,22 +116,26 @@ class TaskService: # This is the top level workflow, which has no guid # check for bpmn_process_id because mypy doesn't realize bpmn_process can be None if process_instance.bpmn_process_id is None: - bpmn_process, new_task_models, new_json_data_models = cls.add_bpmn_process( - serializer.workflow_to_dict( - spiff_task.workflow._get_outermost_workflow() - ), - process_instance, + bpmn_process, new_task_models, new_json_data_models = ( + cls.add_bpmn_process( + serializer.workflow_to_dict( + spiff_task.workflow._get_outermost_workflow() + ), + process_instance, + ) ) else: bpmn_process = BpmnProcessModel.query.filter_by( guid=subprocess_guid ).first() if bpmn_process is None: - bpmn_process, new_task_models, new_json_data_models = cls.add_bpmn_process( - serializer.workflow_to_dict(subprocess), - process_instance, - process_instance.bpmn_process, - subprocess_guid, + bpmn_process, new_task_models, new_json_data_models = ( + cls.add_bpmn_process( + serializer.workflow_to_dict(subprocess), + process_instance, + process_instance.bpmn_process, + subprocess_guid, + ) ) return (bpmn_process, new_task_models, new_json_data_models) @@ -141,6 +150,9 @@ class TaskService: tasks = bpmn_process_dict.pop("tasks") bpmn_process_data_dict = bpmn_process_dict.pop("data") + new_task_models = {} + new_json_data_models = {} + bpmn_process = None if bpmn_process_parent is not None: bpmn_process = BpmnProcessModel.query.filter_by( @@ -170,7 +182,7 @@ class TaskService: json_data = JsonDataModel( hash=bpmn_process_data_hash, data=bpmn_process_data_dict ) - db.session.add(json_data) + new_json_data_models[bpmn_process_data_hash] = json_data bpmn_process.json_data_hash = bpmn_process_data_hash if bpmn_process_parent is None: @@ -179,8 +191,6 @@ class TaskService: bpmn_process.parent_process_id = bpmn_process_parent.id db.session.add(bpmn_process) - new_task_models = {} - new_json_data_models = {} if bpmn_process_is_new: for task_id, task_properties in tasks.items(): task_data_dict = task_properties.pop("data") @@ -201,7 +211,9 @@ class TaskService: task_model.state = TaskStateNames[state_int] task_model.properties_json = task_properties - json_data = TaskService.update_task_data_on_task_model(task_model, task_data_dict) + json_data = TaskService.update_task_data_on_task_model( + task_model, task_data_dict + ) new_task_models[task_model.guid] = task_model if json_data is not None: new_json_data_models[json_data.hash] = json_data diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index f9e78bd5..41dcdbc0 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -72,8 +72,10 @@ class TaskModelSavingDelegate(EngineStepDelegate): def will_complete_task(self, spiff_task: SpiffTask) -> None: if self.should_update_task_model(): - _bpmn_process, task_model, new_task_models, new_json_data_models = TaskService.find_or_create_task_model_from_spiff_task( - spiff_task, self.process_instance, self.serializer + _bpmn_process, task_model, new_task_models, new_json_data_models = ( + TaskService.find_or_create_task_model_from_spiff_task( + spiff_task, self.process_instance, self.serializer + ) ) self.current_task_model = task_model self.task_models.update(new_task_models) @@ -111,8 +113,10 @@ class TaskModelSavingDelegate(EngineStepDelegate): | TaskState.MAYBE | TaskState.LIKELY ): - _bpmn_process, task_model, new_task_models, new_json_data_models = TaskService.find_or_create_task_model_from_spiff_task( - waiting_spiff_task, self.process_instance, self.serializer + _bpmn_process, task_model, new_task_models, new_json_data_models = ( + TaskService.find_or_create_task_model_from_spiff_task( + waiting_spiff_task, self.process_instance, self.serializer + ) ) self.task_models.update(new_task_models) self.json_data_models.update(new_json_data_models) From 80b9d4190a3d151f22861e622a9b6c531f659641 Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 13 Mar 2023 14:42:39 -0400 Subject: [PATCH 32/48] some updates to comments w/ burnettk --- .../services/process_instance_processor.py | 2 +- .../spiffworkflow_backend/services/task_service.py | 11 ++++++++++- .../services/workflow_execution_service.py | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 2b7f0b3b..269a6a9d 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -1891,7 +1891,7 @@ class ProcessInstanceProcessor: db.session.add(details_model) # ####### - json_data = TaskService.update_task_model_and_add_to_db_session( + json_data = TaskService.update_task_model( task_model, spiff_task, self._serializer ) if json_data is not None: diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py index 75a04f5d..402ff8ee 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py @@ -36,7 +36,7 @@ class TaskService: return json_data_to_return @classmethod - def update_task_model_and_add_to_db_session( + def update_task_model( cls, task_model: TaskModel, spiff_task: SpiffTask, @@ -45,6 +45,7 @@ class TaskService: """Updates properties_json and data on given task_model. This will NOT update start_in_seconds or end_in_seconds. + It also returns the relating json_data object so they can be imported later. """ new_properties_json = serializer.task_to_dict(spiff_task) spiff_task_data = new_properties_json.pop("data") @@ -147,6 +148,11 @@ class TaskService: bpmn_process_parent: Optional[BpmnProcessModel] = None, bpmn_process_guid: Optional[str] = None, ) -> Tuple[BpmnProcessModel, dict[str, TaskModel], dict[str, JsonDataModel]]: + """This creates and adds a bpmn_process to the Db session. + + It will also add tasks and relating json_data entries if the bpmn_process is new. + It returns tasks and json data records in dictionaries to be added to the session later. + """ tasks = bpmn_process_dict.pop("tasks") bpmn_process_data_dict = bpmn_process_dict.pop("data") @@ -189,6 +195,9 @@ class TaskService: process_instance.bpmn_process = bpmn_process elif bpmn_process.parent_process_id is None: bpmn_process.parent_process_id = bpmn_process_parent.id + + # Since we bulk insert tasks later we need to add the bpmn_process to the session + # to ensure we have an id. db.session.add(bpmn_process) if bpmn_process_is_new: diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index 41dcdbc0..2f90d5b8 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -87,7 +87,7 @@ class TaskModelSavingDelegate(EngineStepDelegate): def did_complete_task(self, spiff_task: SpiffTask) -> None: if self.current_task_model and self.should_update_task_model(): self.current_task_model.end_in_seconds = time.time() - json_data = TaskService.update_task_model_and_add_to_db_session( + json_data = TaskService.update_task_model( self.current_task_model, spiff_task, self.serializer ) if json_data is not None: @@ -120,7 +120,7 @@ class TaskModelSavingDelegate(EngineStepDelegate): ) self.task_models.update(new_task_models) self.json_data_models.update(new_json_data_models) - json_data = TaskService.update_task_model_and_add_to_db_session( + json_data = TaskService.update_task_model( task_model, waiting_spiff_task, self.serializer ) self.task_models[task_model.guid] = task_model From 6a38328904085e0dc72454018cc352ce04cf6d8d Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 13 Mar 2023 15:23:12 -0400 Subject: [PATCH 33/48] avoid additional db calls to get tasks and json data w/ burnettk --- .../services/process_instance_processor.py | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 269a6a9d..de103aa9 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -613,14 +613,37 @@ class ProcessInstanceProcessor: ).first() bpmn_process_dict = {"data": json_data.data, "tasks": {}} bpmn_process_dict.update(bpmn_process.properties_json) - tasks = TaskModel.query.filter_by(bpmn_process_id=bpmn_process.id).all() - for task in tasks: - json_data = JsonDataModel.query.filter_by(hash=task.json_data_hash).first() - bpmn_process_dict["tasks"][task.guid] = task.properties_json - bpmn_process_dict["tasks"][task.guid]["data"] = json_data.data - + if get_tasks: + tasks = TaskModel.query.filter_by(bpmn_process_id=bpmn_process.id).all() + cls._get_tasks_dict(tasks, bpmn_process_dict) return bpmn_process_dict + @classmethod + def _get_tasks_dict( + cls, + tasks: list[TaskModel], + spiff_bpmn_process_dict: dict, + bpmn_subprocess_id_to_guid_mappings: Optional[dict] = None, + ) -> None: + json_data_hashes = set() + for task in tasks: + json_data_hashes.add(task.json_data_hash) + json_data_records = JsonDataModel.query.filter(JsonDataModel.hash.in_(json_data_hashes)).all() # type: ignore + json_data_mappings = {} + for json_data_record in json_data_records: + json_data_mappings[json_data_record.hash] = json_data_record.data + for task in tasks: + tasks_dict = spiff_bpmn_process_dict["tasks"] + if bpmn_subprocess_id_to_guid_mappings: + bpmn_subprocess_guid = bpmn_subprocess_id_to_guid_mappings[ + task.bpmn_process_id + ] + tasks_dict = spiff_bpmn_process_dict["subprocesses"][ + bpmn_subprocess_guid + ]["tasks"] + tasks_dict[task.guid] = task.properties_json + tasks_dict[task.guid]["data"] = json_data_mappings[task.json_data_hash] + @classmethod def _get_full_bpmn_process_dict( cls, process_instance_model: ProcessInstanceModel @@ -655,9 +678,11 @@ class ProcessInstanceProcessor: bpmn_subprocesses = BpmnProcessModel.query.filter_by( parent_process_id=bpmn_process.id ).all() - bpmn_subprocess_ids = {} + bpmn_subprocess_id_to_guid_mappings = {} for bpmn_subprocess in bpmn_subprocesses: - bpmn_subprocess_ids[bpmn_subprocess.id] = bpmn_subprocess.guid + bpmn_subprocess_id_to_guid_mappings[bpmn_subprocess.id] = ( + bpmn_subprocess.guid + ) single_bpmn_process_dict = cls._get_bpmn_process_dict( bpmn_subprocess ) @@ -666,19 +691,11 @@ class ProcessInstanceProcessor: ] = single_bpmn_process_dict tasks = TaskModel.query.filter( - TaskModel.bpmn_process_id.in_(bpmn_subprocess_ids.keys()) # type: ignore + TaskModel.bpmn_process_id.in_(bpmn_subprocess_id_to_guid_mappings.keys()) # type: ignore ).all() - for task in tasks: - bpmn_subprocess_guid = bpmn_subprocess_ids[task.bpmn_process_id] - json_data = JsonDataModel.query.filter_by( - hash=task.json_data_hash - ).first() - spiff_bpmn_process_dict["subprocesses"][bpmn_subprocess_guid][ - "tasks" - ][task.guid] = task.properties_json - spiff_bpmn_process_dict["subprocesses"][bpmn_subprocess_guid][ - "tasks" - ][task.guid]["data"] = json_data.data + cls._get_tasks_dict( + tasks, spiff_bpmn_process_dict, bpmn_subprocess_id_to_guid_mappings + ) return spiff_bpmn_process_dict From bdb57cb69679aa36e0e5212b9ff94b76b46543a7 Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 13 Mar 2023 15:50:34 -0400 Subject: [PATCH 34/48] added configs to get ready for setting black to allow longer line lengths w/ burnettk --- .pre-commit-config.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3f1c6dbb..9353025e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,9 +12,13 @@ repos: # exclude: ^migrations/ exclude: "/migrations/" - # otherwise it will not fix long lines if the long lines contain long strings + # --preview because otherwise it will not fix long lines if the long lines contain long strings # https://github.com/psf/black/pull/1132 # https://github.com/psf/black/pull/1609 + # --line-length because then we can avoid the fancy line wrapping in more instances and jason, kb, and elizabeth + # kind of prefer long lines rather than cutely-formatted sets of lines. + # TODO: enable when its safe to update the files + # args: [--preview, --line-length, "110"] args: [--preview] - id: check-added-large-files From 2169fc598be652c9d349717f0f560025a9b23a23 Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 13 Mar 2023 17:25:21 -0400 Subject: [PATCH 35/48] works with mysql currently w/ burnettk --- .../services/process_instance_processor.py | 20 ++-- .../services/task_service.py | 96 ++++++++++--------- .../services/workflow_execution_service.py | 29 +++--- 3 files changed, 82 insertions(+), 63 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index de103aa9..37bf2942 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -1101,7 +1101,7 @@ class ProcessInstanceProcessor: self._add_bpmn_process_definitions(bpmn_spec_dict) subprocesses = process_instance_data_dict.pop("subprocesses") - bpmn_process_parent, new_task_models, new_json_data_models = ( + bpmn_process_parent, new_task_models, new_json_data_dicts = ( TaskService.add_bpmn_process( process_instance_data_dict, self.process_instance_model ) @@ -1118,9 +1118,10 @@ class ProcessInstanceProcessor: bpmn_process_guid=subprocess_task_id, ) new_task_models.update(subprocess_new_task_models) - new_json_data_models.update(subprocess_new_json_data_models) + new_json_data_dicts.update(subprocess_new_json_data_models) db.session.bulk_save_objects(new_task_models.values()) - db.session.bulk_save_objects(new_json_data_models.values()) + + TaskService.insert_or_update_json_data_records(new_json_data_dicts) def save(self) -> None: """Saves the current state of this processor to the database.""" @@ -1908,11 +1909,18 @@ class ProcessInstanceProcessor: db.session.add(details_model) # ####### - json_data = TaskService.update_task_model( + json_data_dict = TaskService.update_task_model( task_model, spiff_task, self._serializer ) - if json_data is not None: - db.session.add(json_data) + if json_data_dict is not None: + json_data = ( + db.session.query(JsonDataModel.id) + .filter_by(hash=json_data_dict['hash']) + .first() + ) + if json_data is None: + json_data = JsonDataModel(**json_data_dict) + db.session.add(json_data) # this is the thing that actually commits the db transaction (on behalf of the other updates above as well) self.save() diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py index 402ff8ee..83c34748 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py @@ -1,5 +1,6 @@ import json from hashlib import sha256 +from typing import TypedDict from typing import Optional from typing import Tuple @@ -14,26 +15,44 @@ from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401 from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.task import TaskModel # noqa: F401 +from sqlalchemy.dialects.mysql import insert + + +class JsonDataDict(TypedDict): + hash: str + data: dict + class TaskService: @classmethod - def update_task_data_on_task_model( - cls, task_model: TaskModel, task_data_dict: dict - ) -> Optional[JsonDataModel]: - task_data_json = json.dumps(task_data_dict, sort_keys=True) - task_data_hash = sha256(task_data_json.encode("utf8")).hexdigest() - json_data_to_return = None - if task_model.json_data_hash != task_data_hash: - json_data = ( - db.session.query(JsonDataModel.id) - .filter_by(hash=task_data_hash) - .first() + def insert_or_update_json_data_records(cls, json_data_hash_to_json_data_dict_mapping: dict[str, JsonDataDict]) -> None: + list_of_dicts = [*json_data_hash_to_json_data_dict_mapping.values()] + if len(list_of_dicts) > 0: + # db.session.execute(JsonDataModel.__table__.insert(), list_of_dicts) + + # insert_stmt = insert(my_table).values( + # id='some_existing_id', + # data='inserted value') + insert_stmt = insert(JsonDataModel).values(list_of_dicts) + + on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update( + data=insert_stmt.inserted.data, + status='U' ) - if json_data is None: - json_data = JsonDataModel(hash=task_data_hash, data=task_data_dict) - json_data_to_return = json_data + + db.session.execute(on_duplicate_key_stmt) + + @classmethod + def _update_task_data_on_task_model( + cls, task_model: TaskModel, task_data_dict: dict + ) -> Optional[JsonDataDict]: + task_data_json = json.dumps(task_data_dict, sort_keys=True) + task_data_hash: str = sha256(task_data_json.encode("utf8")).hexdigest() + json_data_dict: Optional[JsonDataDict] = None + if task_model.json_data_hash != task_data_hash: + json_data_dict = {"hash": task_data_hash, "data": task_data_dict} task_model.json_data_hash = task_data_hash - return json_data_to_return + return json_data_dict @classmethod def update_task_model( @@ -41,7 +60,7 @@ class TaskService: task_model: TaskModel, spiff_task: SpiffTask, serializer: BpmnWorkflowSerializer, - ) -> Optional[JsonDataModel]: + ) -> Optional[JsonDataDict]: """Updates properties_json and data on given task_model. This will NOT update start_in_seconds or end_in_seconds. @@ -51,8 +70,8 @@ class TaskService: spiff_task_data = new_properties_json.pop("data") task_model.properties_json = new_properties_json task_model.state = TaskStateNames[new_properties_json["state"]] - json_data = cls.update_task_data_on_task_model(task_model, spiff_task_data) - return json_data + json_data_dict = cls._update_task_data_on_task_model(task_model, spiff_task_data) + return json_data_dict @classmethod def find_or_create_task_model_from_spiff_task( @@ -64,7 +83,7 @@ class TaskService: Optional[BpmnProcessModel], TaskModel, dict[str, TaskModel], - dict[str, JsonDataModel], + dict[str, JsonDataDict], ]: spiff_task_guid = str(spiff_task.id) task_model: Optional[TaskModel] = TaskModel.query.filter_by( @@ -72,9 +91,9 @@ class TaskService: ).first() bpmn_process = None new_task_models: dict[str, TaskModel] = {} - new_json_data_models: dict[str, JsonDataModel] = {} + new_json_data_dicts: dict[str, JsonDataDict] = {} if task_model is None: - bpmn_process, new_task_models, new_json_data_models = cls.task_bpmn_process( + bpmn_process, new_task_models, new_json_data_dicts = cls.task_bpmn_process( spiff_task, process_instance, serializer ) task_model = TaskModel.query.filter_by(guid=spiff_task_guid).first() @@ -82,7 +101,7 @@ class TaskService: task_model = TaskModel( guid=spiff_task_guid, bpmn_process_id=bpmn_process.id ) - return (bpmn_process, task_model, new_task_models, new_json_data_models) + return (bpmn_process, task_model, new_task_models, new_json_data_dicts) @classmethod def task_subprocess( @@ -107,17 +126,17 @@ class TaskService: spiff_task: SpiffTask, process_instance: ProcessInstanceModel, serializer: BpmnWorkflowSerializer, - ) -> Tuple[BpmnProcessModel, dict[str, TaskModel], dict[str, JsonDataModel]]: + ) -> Tuple[BpmnProcessModel, dict[str, TaskModel], dict[str, JsonDataDict]]: subprocess_guid, subprocess = cls.task_subprocess(spiff_task) bpmn_process: Optional[BpmnProcessModel] = None new_task_models: dict[str, TaskModel] = {} - new_json_data_models: dict[str, JsonDataModel] = {} + new_json_data_dicts: dict[str, JsonDataDict] = {} if subprocess is None: bpmn_process = process_instance.bpmn_process # This is the top level workflow, which has no guid # check for bpmn_process_id because mypy doesn't realize bpmn_process can be None if process_instance.bpmn_process_id is None: - bpmn_process, new_task_models, new_json_data_models = ( + bpmn_process, new_task_models, new_json_data_dicts = ( cls.add_bpmn_process( serializer.workflow_to_dict( spiff_task.workflow._get_outermost_workflow() @@ -130,7 +149,7 @@ class TaskService: guid=subprocess_guid ).first() if bpmn_process is None: - bpmn_process, new_task_models, new_json_data_models = ( + bpmn_process, new_task_models, new_json_data_dicts = ( cls.add_bpmn_process( serializer.workflow_to_dict(subprocess), process_instance, @@ -138,7 +157,7 @@ class TaskService: subprocess_guid, ) ) - return (bpmn_process, new_task_models, new_json_data_models) + return (bpmn_process, new_task_models, new_json_data_dicts) @classmethod def add_bpmn_process( @@ -147,7 +166,7 @@ class TaskService: process_instance: ProcessInstanceModel, bpmn_process_parent: Optional[BpmnProcessModel] = None, bpmn_process_guid: Optional[str] = None, - ) -> Tuple[BpmnProcessModel, dict[str, TaskModel], dict[str, JsonDataModel]]: + ) -> Tuple[BpmnProcessModel, dict[str, TaskModel], dict[str, JsonDataDict]]: """This creates and adds a bpmn_process to the Db session. It will also add tasks and relating json_data entries if the bpmn_process is new. @@ -157,7 +176,7 @@ class TaskService: bpmn_process_data_dict = bpmn_process_dict.pop("data") new_task_models = {} - new_json_data_models = {} + new_json_data_dicts: dict[str, JsonDataDict] = {} bpmn_process = None if bpmn_process_parent is not None: @@ -179,16 +198,7 @@ class TaskService: bpmn_process_data_json.encode("utf8") ).hexdigest() if bpmn_process.json_data_hash != bpmn_process_data_hash: - json_data = ( - db.session.query(JsonDataModel.id) - .filter_by(hash=bpmn_process_data_hash) - .first() - ) - if json_data is None: - json_data = JsonDataModel( - hash=bpmn_process_data_hash, data=bpmn_process_data_dict - ) - new_json_data_models[bpmn_process_data_hash] = json_data + new_json_data_dicts[bpmn_process_data_hash] = {"hash": bpmn_process_data_hash, "data": bpmn_process_data_dict} bpmn_process.json_data_hash = bpmn_process_data_hash if bpmn_process_parent is None: @@ -220,11 +230,11 @@ class TaskService: task_model.state = TaskStateNames[state_int] task_model.properties_json = task_properties - json_data = TaskService.update_task_data_on_task_model( + json_data_dict = TaskService._update_task_data_on_task_model( task_model, task_data_dict ) new_task_models[task_model.guid] = task_model - if json_data is not None: - new_json_data_models[json_data.hash] = json_data + if json_data_dict is not None: + new_json_data_dicts[json_data_dict['hash']] = json_data_dict - return (bpmn_process, new_task_models, new_json_data_models) + return (bpmn_process, new_task_models, new_json_data_dicts) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index 2f90d5b8..d7d3a83a 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -12,7 +12,6 @@ from SpiffWorkflow.task import TaskState from spiffworkflow_backend.exceptions.api_error import ApiError from spiffworkflow_backend.models.db import db -from spiffworkflow_backend.models.json_data import JsonDataModel from spiffworkflow_backend.models.message_instance import MessageInstanceModel from spiffworkflow_backend.models.message_instance_correlation import ( MessageInstanceCorrelationRuleModel, @@ -20,7 +19,7 @@ from spiffworkflow_backend.models.message_instance_correlation import ( from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel from spiffworkflow_backend.models.task import TaskModel # noqa: F401 -from spiffworkflow_backend.services.task_service import TaskService +from spiffworkflow_backend.services.task_service import JsonDataDict, TaskService class EngineStepDelegate: @@ -60,7 +59,7 @@ class TaskModelSavingDelegate(EngineStepDelegate): self.current_task_model: Optional[TaskModel] = None self.task_models: dict[str, TaskModel] = {} - self.json_data_models: dict[str, JsonDataModel] = {} + self.json_data_dicts: dict[str, JsonDataDict] = {} self.serializer = serializer def should_update_task_model(self) -> bool: @@ -72,14 +71,14 @@ class TaskModelSavingDelegate(EngineStepDelegate): def will_complete_task(self, spiff_task: SpiffTask) -> None: if self.should_update_task_model(): - _bpmn_process, task_model, new_task_models, new_json_data_models = ( + _bpmn_process, task_model, new_task_models, new_json_data_dicts = ( TaskService.find_or_create_task_model_from_spiff_task( spiff_task, self.process_instance, self.serializer ) ) self.current_task_model = task_model self.task_models.update(new_task_models) - self.json_data_models.update(new_json_data_models) + self.json_data_dicts.update(new_json_data_dicts) self.current_task_model.start_in_seconds = time.time() if self.secondary_engine_step_delegate: self.secondary_engine_step_delegate.will_complete_task(spiff_task) @@ -87,18 +86,20 @@ class TaskModelSavingDelegate(EngineStepDelegate): def did_complete_task(self, spiff_task: SpiffTask) -> None: if self.current_task_model and self.should_update_task_model(): self.current_task_model.end_in_seconds = time.time() - json_data = TaskService.update_task_model( + json_data_dict = TaskService.update_task_model( self.current_task_model, spiff_task, self.serializer ) - if json_data is not None: - self.json_data_models[json_data.hash] = json_data + if json_data_dict is not None: + self.json_data_dicts[json_data_dict['hash']] = json_data_dict self.task_models[self.current_task_model.guid] = self.current_task_model if self.secondary_engine_step_delegate: self.secondary_engine_step_delegate.did_complete_task(spiff_task) def save(self, _commit: bool = True) -> None: db.session.bulk_save_objects(self.task_models.values()) - db.session.bulk_save_objects(self.json_data_models.values()) + + TaskService.insert_or_update_json_data_records(self.json_data_dicts) + if self.secondary_engine_step_delegate: self.secondary_engine_step_delegate.save(commit=False) db.session.commit() @@ -113,19 +114,19 @@ class TaskModelSavingDelegate(EngineStepDelegate): | TaskState.MAYBE | TaskState.LIKELY ): - _bpmn_process, task_model, new_task_models, new_json_data_models = ( + _bpmn_process, task_model, new_task_models, new_json_data_dicts = ( TaskService.find_or_create_task_model_from_spiff_task( waiting_spiff_task, self.process_instance, self.serializer ) ) self.task_models.update(new_task_models) - self.json_data_models.update(new_json_data_models) - json_data = TaskService.update_task_model( + self.json_data_dicts.update(new_json_data_dicts) + json_data_dict = TaskService.update_task_model( task_model, waiting_spiff_task, self.serializer ) self.task_models[task_model.guid] = task_model - if json_data is not None: - self.json_data_models[json_data.hash] = json_data + if json_data_dict is not None: + self.json_data_dicts[json_data_dict['hash']] = json_data_dict class StepDetailLoggingDelegate(EngineStepDelegate): From 15784c0ea364a6330f8a802a3a238a99904f0063 Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 13 Mar 2023 17:35:03 -0400 Subject: [PATCH 36/48] attempt to support on conflict inserts for postgres as well w/ burnettk --- .../services/task_service.py | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py index 83c34748..c4e84630 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py @@ -1,5 +1,6 @@ import json from hashlib import sha256 +from flask import current_app from typing import TypedDict from typing import Optional from typing import Tuple @@ -15,7 +16,8 @@ from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401 from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.task import TaskModel # noqa: F401 -from sqlalchemy.dialects.mysql import insert +from sqlalchemy.dialects.mysql import insert as mysql_insert +from sqlalchemy.dialects.postgresql import insert as postgres_insert class JsonDataDict(TypedDict): @@ -28,18 +30,32 @@ class TaskService: def insert_or_update_json_data_records(cls, json_data_hash_to_json_data_dict_mapping: dict[str, JsonDataDict]) -> None: list_of_dicts = [*json_data_hash_to_json_data_dict_mapping.values()] if len(list_of_dicts) > 0: - # db.session.execute(JsonDataModel.__table__.insert(), list_of_dicts) - - # insert_stmt = insert(my_table).values( - # id='some_existing_id', - # data='inserted value') - insert_stmt = insert(JsonDataModel).values(list_of_dicts) - - on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update( - data=insert_stmt.inserted.data, - status='U' - ) - + # >>> from sqlalchemy.dialects.postgresql import insert + # >>> insert_stmt = insert(my_table).values( + # ... id='some_existing_id', + # ... data='inserted value') + # >>> do_nothing_stmt = insert_stmt.on_conflict_do_nothing( + # ... index_elements=['id'] + # ... ) + # >>> print(do_nothing_stmt) + # INSERT INTO my_table (id, data) VALUES (%(id)s, %(data)s) + # ON CONFLICT (id) DO NOTHING + # >>> do_update_stmt = insert_stmt.on_conflict_do_update( + # ... constraint='pk_my_table', + # ... set_=dict(data='updated value') + # ... ) + on_duplicate_key_stmt = None + if current_app.config['SPIFFWORKFLOW_BACKEND_DATABASE_TYPE'] == "mysql": + insert_stmt = mysql_insert(JsonDataModel).values(list_of_dicts) + on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update( + data=insert_stmt.inserted.data, + status='U' + ) + else: + insert_stmt = postgres_insert(JsonDataModel).values(list_of_dicts) + on_duplicate_key_stmt = insert_stmt.on_conflict_do_nothing( + index_elements=['hash'] + ) db.session.execute(on_duplicate_key_stmt) @classmethod From d35c2a9f2d5e1210175c32f5d1669b685b2c2562 Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 13 Mar 2023 17:36:58 -0400 Subject: [PATCH 37/48] pyl w/ burnettk --- .../services/process_instance_processor.py | 2 +- .../services/task_service.py | 31 +++++++++++-------- .../services/workflow_execution_service.py | 7 +++-- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 37bf2942..0b2d73b3 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -1915,7 +1915,7 @@ class ProcessInstanceProcessor: if json_data_dict is not None: json_data = ( db.session.query(JsonDataModel.id) - .filter_by(hash=json_data_dict['hash']) + .filter_by(hash=json_data_dict["hash"]) .first() ) if json_data is None: diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py index c4e84630..c73e0380 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py @@ -1,14 +1,16 @@ import json from hashlib import sha256 -from flask import current_app -from typing import TypedDict from typing import Optional from typing import Tuple +from typing import TypedDict +from flask import current_app from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflow # type: ignore from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer from SpiffWorkflow.task import Task as SpiffTask # type: ignore from SpiffWorkflow.task import TaskStateNames +from sqlalchemy.dialects.mysql import insert as mysql_insert +from sqlalchemy.dialects.postgresql import insert as postgres_insert from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel from spiffworkflow_backend.models.db import db @@ -16,9 +18,6 @@ from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401 from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.task import TaskModel # noqa: F401 -from sqlalchemy.dialects.mysql import insert as mysql_insert -from sqlalchemy.dialects.postgresql import insert as postgres_insert - class JsonDataDict(TypedDict): hash: str @@ -27,7 +26,9 @@ class JsonDataDict(TypedDict): class TaskService: @classmethod - def insert_or_update_json_data_records(cls, json_data_hash_to_json_data_dict_mapping: dict[str, JsonDataDict]) -> None: + def insert_or_update_json_data_records( + cls, json_data_hash_to_json_data_dict_mapping: dict[str, JsonDataDict] + ) -> None: list_of_dicts = [*json_data_hash_to_json_data_dict_mapping.values()] if len(list_of_dicts) > 0: # >>> from sqlalchemy.dialects.postgresql import insert @@ -45,16 +46,15 @@ class TaskService: # ... set_=dict(data='updated value') # ... ) on_duplicate_key_stmt = None - if current_app.config['SPIFFWORKFLOW_BACKEND_DATABASE_TYPE'] == "mysql": + if current_app.config["SPIFFWORKFLOW_BACKEND_DATABASE_TYPE"] == "mysql": insert_stmt = mysql_insert(JsonDataModel).values(list_of_dicts) on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update( - data=insert_stmt.inserted.data, - status='U' + data=insert_stmt.inserted.data, status="U" ) else: insert_stmt = postgres_insert(JsonDataModel).values(list_of_dicts) on_duplicate_key_stmt = insert_stmt.on_conflict_do_nothing( - index_elements=['hash'] + index_elements=["hash"] ) db.session.execute(on_duplicate_key_stmt) @@ -86,7 +86,9 @@ class TaskService: spiff_task_data = new_properties_json.pop("data") task_model.properties_json = new_properties_json task_model.state = TaskStateNames[new_properties_json["state"]] - json_data_dict = cls._update_task_data_on_task_model(task_model, spiff_task_data) + json_data_dict = cls._update_task_data_on_task_model( + task_model, spiff_task_data + ) return json_data_dict @classmethod @@ -214,7 +216,10 @@ class TaskService: bpmn_process_data_json.encode("utf8") ).hexdigest() if bpmn_process.json_data_hash != bpmn_process_data_hash: - new_json_data_dicts[bpmn_process_data_hash] = {"hash": bpmn_process_data_hash, "data": bpmn_process_data_dict} + new_json_data_dicts[bpmn_process_data_hash] = { + "hash": bpmn_process_data_hash, + "data": bpmn_process_data_dict, + } bpmn_process.json_data_hash = bpmn_process_data_hash if bpmn_process_parent is None: @@ -251,6 +256,6 @@ class TaskService: ) new_task_models[task_model.guid] = task_model if json_data_dict is not None: - new_json_data_dicts[json_data_dict['hash']] = json_data_dict + new_json_data_dicts[json_data_dict["hash"]] = json_data_dict return (bpmn_process, new_task_models, new_json_data_dicts) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index d7d3a83a..864885e5 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -19,7 +19,8 @@ from spiffworkflow_backend.models.message_instance_correlation import ( from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel from spiffworkflow_backend.models.task import TaskModel # noqa: F401 -from spiffworkflow_backend.services.task_service import JsonDataDict, TaskService +from spiffworkflow_backend.services.task_service import JsonDataDict +from spiffworkflow_backend.services.task_service import TaskService class EngineStepDelegate: @@ -90,7 +91,7 @@ class TaskModelSavingDelegate(EngineStepDelegate): self.current_task_model, spiff_task, self.serializer ) if json_data_dict is not None: - self.json_data_dicts[json_data_dict['hash']] = json_data_dict + self.json_data_dicts[json_data_dict["hash"]] = json_data_dict self.task_models[self.current_task_model.guid] = self.current_task_model if self.secondary_engine_step_delegate: self.secondary_engine_step_delegate.did_complete_task(spiff_task) @@ -126,7 +127,7 @@ class TaskModelSavingDelegate(EngineStepDelegate): ) self.task_models[task_model.guid] = task_model if json_data_dict is not None: - self.json_data_dicts[json_data_dict['hash']] = json_data_dict + self.json_data_dicts[json_data_dict["hash"]] = json_data_dict class StepDetailLoggingDelegate(EngineStepDelegate): From 90f0b5ff859fc9833a7ae8d217f8300e61795352 Mon Sep 17 00:00:00 2001 From: burnettk Date: Mon, 13 Mar 2023 17:53:29 -0400 Subject: [PATCH 38/48] remove comment --- .../spiffworkflow_backend/services/task_service.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py index c73e0380..dbd0a912 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/task_service.py @@ -31,20 +31,6 @@ class TaskService: ) -> None: list_of_dicts = [*json_data_hash_to_json_data_dict_mapping.values()] if len(list_of_dicts) > 0: - # >>> from sqlalchemy.dialects.postgresql import insert - # >>> insert_stmt = insert(my_table).values( - # ... id='some_existing_id', - # ... data='inserted value') - # >>> do_nothing_stmt = insert_stmt.on_conflict_do_nothing( - # ... index_elements=['id'] - # ... ) - # >>> print(do_nothing_stmt) - # INSERT INTO my_table (id, data) VALUES (%(id)s, %(data)s) - # ON CONFLICT (id) DO NOTHING - # >>> do_update_stmt = insert_stmt.on_conflict_do_update( - # ... constraint='pk_my_table', - # ... set_=dict(data='updated value') - # ... ) on_duplicate_key_stmt = None if current_app.config["SPIFFWORKFLOW_BACKEND_DATABASE_TYPE"] == "mysql": insert_stmt = mysql_insert(JsonDataModel).values(list_of_dicts) From 8bbacc2259acf9c98764293f0e324486af743447 Mon Sep 17 00:00:00 2001 From: burnettk Date: Mon, 13 Mar 2023 22:25:30 -0400 Subject: [PATCH 39/48] log connector --- .../src/spiffworkflow_backend/services/service_task_service.py | 1 + 1 file changed, 1 insertion(+) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/service_task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/service_task_service.py index 6862a5eb..56377f57 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/service_task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/service_task_service.py @@ -91,6 +91,7 @@ class ServiceTaskDelegate: def call_connector(name: str, bpmn_params: Any, task_data: Any) -> str: """Calls a connector via the configured proxy.""" call_url = f"{connector_proxy_url()}/v1/do/{name}" + current_app.logger.error(f"Calling connector proxy using connector: {name}") with sentry_sdk.start_span(op="connector_by_name", description=name): with sentry_sdk.start_span(op="call-connector", description=call_url): params = { From 620832e374d2b24d2bd5082820f9f8a921f9ec37 Mon Sep 17 00:00:00 2001 From: burnettk Date: Mon, 13 Mar 2023 22:37:49 -0400 Subject: [PATCH 40/48] oops, meant to use info --- .../src/spiffworkflow_backend/services/service_task_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/service_task_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/service_task_service.py index 56377f57..77a5fb21 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/service_task_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/service_task_service.py @@ -91,7 +91,7 @@ class ServiceTaskDelegate: def call_connector(name: str, bpmn_params: Any, task_data: Any) -> str: """Calls a connector via the configured proxy.""" call_url = f"{connector_proxy_url()}/v1/do/{name}" - current_app.logger.error(f"Calling connector proxy using connector: {name}") + current_app.logger.info(f"Calling connector proxy using connector: {name}") with sentry_sdk.start_span(op="connector_by_name", description=name): with sentry_sdk.start_span(op="call-connector", description=call_url): params = { From 7cd645846f2b325802d21afda6cc813c7a50080d Mon Sep 17 00:00:00 2001 From: jasquat Date: Tue, 14 Mar 2023 10:51:12 -0400 Subject: [PATCH 41/48] do not write spiff step details to see how that changes performance --- .../services/process_instance_processor.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 0b2d73b3..b2ce4cfd 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -1661,17 +1661,19 @@ class ProcessInstanceProcessor: ) -> None: """Do_engine_steps.""" - def spiff_step_details_mapping_builder( - task: SpiffTask, start: float, end: float - ) -> dict: - self._script_engine.environment.revise_state_with_task_data(task) - return self.spiff_step_details_mapping(task, start, end) - - step_delegate = StepDetailLoggingDelegate( - self.increment_spiff_step, spiff_step_details_mapping_builder - ) + # NOTE: Commenting out to test how this changes performance: + # def spiff_step_details_mapping_builder( + # task: SpiffTask, start: float, end: float + # ) -> dict: + # self._script_engine.environment.revise_state_with_task_data(task) + # return self.spiff_step_details_mapping(task, start, end) + # + # step_delegate = StepDetailLoggingDelegate( + # self.increment_spiff_step, spiff_step_details_mapping_builder + # ) task_model_delegate = TaskModelSavingDelegate( - secondary_engine_step_delegate=step_delegate, + # secondary_engine_step_delegate=step_delegate, + secondary_engine_step_delegate=None, serializer=self._serializer, process_instance=self.process_instance_model, ) From 764eb35d1b9c81f190ebf1931b634527ef9c5f46 Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Tue, 14 Mar 2023 13:12:01 -0400 Subject: [PATCH 42/48] Move process instance locking to new queue table (#177) --- .../migrations/versions/e2972eaf8469_.py | 58 +++++++++ .../src/spiffworkflow_backend/__init__.py | 31 ++++- .../spiffworkflow_backend/config/default.py | 14 +++ .../load_database_models.py | 3 + .../models/process_instance.py | 3 - .../models/process_instance_queue.py | 30 +++++ .../routes/process_instances_controller.py | 10 ++ .../services/background_processing_service.py | 6 + .../services/process_instance_lock_service.py | 67 +++++++++++ .../services/process_instance_processor.py | 85 ++++---------- .../process_instance_queue_service.py | 110 ++++++++++++++++++ .../services/process_instance_service.py | 22 +++- .../services/workflow_execution_service.py | 13 ++- .../helpers/base_test.py | 6 + .../unit/test_process_instance_processor.py | 16 ++- 15 files changed, 389 insertions(+), 85 deletions(-) create mode 100644 spiffworkflow-backend/migrations/versions/e2972eaf8469_.py create mode 100644 spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_queue.py create mode 100644 spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_lock_service.py create mode 100644 spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_queue_service.py diff --git a/spiffworkflow-backend/migrations/versions/e2972eaf8469_.py b/spiffworkflow-backend/migrations/versions/e2972eaf8469_.py new file mode 100644 index 00000000..f1796bfb --- /dev/null +++ b/spiffworkflow-backend/migrations/versions/e2972eaf8469_.py @@ -0,0 +1,58 @@ +"""empty message + +Revision ID: e2972eaf8469 +Revises: 389800c352ee +Create Date: 2023-03-13 22:00:21.579493 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = 'e2972eaf8469' +down_revision = '389800c352ee' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('process_instance_queue', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('process_instance_id', sa.Integer(), nullable=False), + sa.Column('run_at_in_seconds', sa.Integer(), nullable=True), + sa.Column('priority', sa.Integer(), nullable=True), + sa.Column('locked_by', sa.String(length=80), nullable=True), + sa.Column('locked_at_in_seconds', sa.Integer(), nullable=True), + sa.Column('status', sa.String(length=50), nullable=True), + sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True), + sa.Column('created_at_in_seconds', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_process_instance_queue_locked_at_in_seconds'), 'process_instance_queue', ['locked_at_in_seconds'], unique=False) + op.create_index(op.f('ix_process_instance_queue_locked_by'), 'process_instance_queue', ['locked_by'], unique=False) + op.create_index(op.f('ix_process_instance_queue_process_instance_id'), 'process_instance_queue', ['process_instance_id'], unique=True) + op.create_index(op.f('ix_process_instance_queue_status'), 'process_instance_queue', ['status'], unique=False) + op.alter_column('message_instance', 'user_id', + existing_type=mysql.INTEGER(), + nullable=True) + op.drop_column('process_instance', 'locked_by') + op.drop_column('process_instance', 'locked_at_in_seconds') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('process_instance', sa.Column('locked_at_in_seconds', mysql.INTEGER(), autoincrement=False, nullable=True)) + op.add_column('process_instance', sa.Column('locked_by', mysql.VARCHAR(length=80), nullable=True)) + op.alter_column('message_instance', 'user_id', + existing_type=mysql.INTEGER(), + nullable=False) + op.drop_index(op.f('ix_process_instance_queue_status'), table_name='process_instance_queue') + op.drop_index(op.f('ix_process_instance_queue_process_instance_id'), table_name='process_instance_queue') + op.drop_index(op.f('ix_process_instance_queue_locked_by'), table_name='process_instance_queue') + op.drop_index(op.f('ix_process_instance_queue_locked_at_in_seconds'), table_name='process_instance_queue') + op.drop_table('process_instance_queue') + # ### end Alembic commands ### diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/__init__.py b/spiffworkflow-backend/src/spiffworkflow_backend/__init__.py index 3266ae76..d7041ecb 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/__init__.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/__init__.py @@ -68,6 +68,15 @@ def start_scheduler( ) -> None: """Start_scheduler.""" scheduler = scheduler_class() + + # TODO: polling intervals for different jobs + polling_interval_in_seconds = app.config[ + "SPIFFWORKFLOW_BACKEND_BACKGROUND_SCHEDULER_POLLING_INTERVAL_IN_SECONDS" + ] + # TODO: add job to release locks to simplify other queries + # TODO: add job to delete completed entires + # TODO: add job to run old/low priority instances so they do not get drowned out + scheduler.add_job( BackgroundProcessingService(app).process_message_instances_with_app_context, "interval", @@ -76,7 +85,7 @@ def start_scheduler( scheduler.add_job( BackgroundProcessingService(app).process_waiting_process_instances, "interval", - seconds=10, + seconds=polling_interval_in_seconds, ) scheduler.add_job( BackgroundProcessingService(app).process_user_input_required_process_instances, @@ -86,6 +95,20 @@ def start_scheduler( scheduler.start() +def should_start_scheduler(app: flask.app.Flask) -> bool: + if not app.config["SPIFFWORKFLOW_BACKEND_RUN_BACKGROUND_SCHEDULER"]: + return False + + # do not start the scheduler twice in flask debug mode but support code reloading + if ( + app.config["ENV_IDENTIFIER"] != "local_development" + or os.environ.get("WERKZEUG_RUN_MAIN") != "true" + ): + return False + + return True + + class NoOpCipher: def encrypt(self, value: str) -> bytes: return str.encode(value) @@ -134,11 +157,7 @@ def create_app() -> flask.app.Flask: app.json = MyJSONEncoder(app) - # do not start the scheduler twice in flask debug mode - if ( - app.config["SPIFFWORKFLOW_BACKEND_RUN_BACKGROUND_SCHEDULER"] - and os.environ.get("WERKZEUG_RUN_MAIN") != "true" - ): + if should_start_scheduler(app): start_scheduler(app) configure_sentry(app) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py b/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py index 04136d36..61a89f97 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/default.py @@ -21,6 +21,12 @@ SPIFFWORKFLOW_BACKEND_RUN_BACKGROUND_SCHEDULER = ( environ.get("SPIFFWORKFLOW_BACKEND_RUN_BACKGROUND_SCHEDULER", default="false") == "true" ) +SPIFFWORKFLOW_BACKEND_BACKGROUND_SCHEDULER_POLLING_INTERVAL_IN_SECONDS = int( + environ.get( + "SPIFFWORKFLOW_BACKEND_BACKGROUND_SCHEDULER_POLLING_INTERVAL_IN_SECONDS", + default="10", + ) +) SPIFFWORKFLOW_BACKEND_URL_FOR_FRONTEND = environ.get( "SPIFFWORKFLOW_BACKEND_URL_FOR_FRONTEND", default="http://localhost:7001" ) @@ -147,6 +153,14 @@ SPIFFWORKFLOW_BACKEND_DEFAULT_USER_GROUP = environ.get( "SPIFFWORKFLOW_BACKEND_DEFAULT_USER_GROUP", default="everybody" ) +SPIFFWORKFLOW_BACKEND_ENGINE_STEP_DEFAULT_STRATEGY_BACKGROUND = environ.get( + "SPIFFWORKFLOW_BACKEND_ENGINE_STEP_DEFAULT_STRATEGY_BACKGROUND", default="greedy" +) + +SPIFFWORKFLOW_BACKEND_ENGINE_STEP_DEFAULT_STRATEGY_WEB = environ.get( + "SPIFFWORKFLOW_BACKEND_ENGINE_STEP_DEFAULT_STRATEGY_WEB", default="greedy" +) + # this is only used in CI. use SPIFFWORKFLOW_BACKEND_DATABASE_URI instead for real configuration SPIFFWORKFLOW_BACKEND_DATABASE_PASSWORD = environ.get( "SPIFFWORKFLOW_BACKEND_DATABASE_PASSWORD", default=None diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py b/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py index 376083cf..4b547158 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py @@ -66,5 +66,8 @@ from spiffworkflow_backend.models.json_data import JsonDataModel # noqa: F401 from spiffworkflow_backend.models.bpmn_process_definition_relationship import ( BpmnProcessDefinitionRelationshipModel, ) # noqa: F401 +from spiffworkflow_backend.models.process_instance_queue import ( + ProcessInstanceQueueModel, +) # noqa: F401 add_listeners() diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py index cbbceaba..f155494a 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py @@ -105,9 +105,6 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel): bpmn_version_control_identifier: str = db.Column(db.String(255)) spiff_step: int = db.Column(db.Integer) - locked_by: str | None = db.Column(db.String(80)) - locked_at_in_seconds: int | None = db.Column(db.Integer) - bpmn_xml_file_contents: str | None = None process_model_with_diagram_identifier: str | None = None diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_queue.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_queue.py new file mode 100644 index 00000000..ff81cf86 --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_queue.py @@ -0,0 +1,30 @@ +"""Process_instance_queue.""" +from dataclasses import dataclass +from typing import Union + +from sqlalchemy import ForeignKey + +from spiffworkflow_backend.models.db import db +from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel +from spiffworkflow_backend.models.process_instance import ProcessInstanceModel + + +@dataclass +class ProcessInstanceQueueModel(SpiffworkflowBaseDBModel): + """ProcessInstanceQueueModel.""" + + __tablename__ = "process_instance_queue" + + id: int = db.Column(db.Integer, primary_key=True) + process_instance_id: int = db.Column( + ForeignKey(ProcessInstanceModel.id), index=True, unique=True, nullable=False # type: ignore + ) + run_at_in_seconds: int = db.Column(db.Integer) + priority: int = db.Column(db.Integer) + locked_by: Union[str, None] = db.Column(db.String(80), index=True, nullable=True) + locked_at_in_seconds: Union[int, None] = db.Column( + db.Integer, index=True, nullable=True + ) + status: str = db.Column(db.String(50), index=True) + updated_at_in_seconds: int = db.Column(db.Integer) + created_at_in_seconds: int = db.Column(db.Integer) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py index f6c9ff66..252b9264 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_instances_controller.py @@ -30,6 +30,9 @@ from spiffworkflow_backend.models.process_instance import ProcessInstanceModelSc from spiffworkflow_backend.models.process_instance_metadata import ( ProcessInstanceMetadataModel, ) +from spiffworkflow_backend.models.process_instance_queue import ( + ProcessInstanceQueueModel, +) from spiffworkflow_backend.models.process_instance_report import ( ProcessInstanceReportModel, ) @@ -55,6 +58,9 @@ from spiffworkflow_backend.services.message_service import MessageService from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, ) +from spiffworkflow_backend.services.process_instance_queue_service import ( + ProcessInstanceQueueService, +) from spiffworkflow_backend.services.process_instance_report_service import ( ProcessInstanceReportFilter, ) @@ -92,6 +98,7 @@ def process_instance_create( process_model_identifier, g.user ) ) + ProcessInstanceQueueService.enqueue(process_instance) return Response( json.dumps(ProcessInstanceModelSchema().dump(process_instance)), status=201, @@ -413,6 +420,9 @@ def process_instance_delete( db.session.query(SpiffStepDetailsModel).filter_by( process_instance_id=process_instance.id ).delete() + db.session.query(ProcessInstanceQueueModel).filter_by( + process_instance_id=process_instance.id + ).delete() db.session.delete(process_instance) db.session.commit() return Response(json.dumps({"ok": True}), status=200, mimetype="application/json") diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/background_processing_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/background_processing_service.py index dc7e1e7e..3ce0e8f2 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/background_processing_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/background_processing_service.py @@ -3,6 +3,9 @@ import flask from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus from spiffworkflow_backend.services.message_service import MessageService +from spiffworkflow_backend.services.process_instance_lock_service import ( + ProcessInstanceLockService, +) from spiffworkflow_backend.services.process_instance_service import ( ProcessInstanceService, ) @@ -18,11 +21,13 @@ class BackgroundProcessingService: def process_waiting_process_instances(self) -> None: """Since this runs in a scheduler, we need to specify the app context as well.""" with self.app.app_context(): + ProcessInstanceLockService.set_thread_local_locking_context("bg:waiting") ProcessInstanceService.do_waiting() def process_user_input_required_process_instances(self) -> None: """Since this runs in a scheduler, we need to specify the app context as well.""" with self.app.app_context(): + ProcessInstanceLockService.set_thread_local_locking_context("bg:userinput") ProcessInstanceService.do_waiting( ProcessInstanceStatus.user_input_required.value ) @@ -30,4 +35,5 @@ class BackgroundProcessingService: def process_message_instances_with_app_context(self) -> None: """Since this runs in a scheduler, we need to specify the app context as well.""" with self.app.app_context(): + ProcessInstanceLockService.set_thread_local_locking_context("bg:messages") MessageService.correlate_all_message_instances() diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_lock_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_lock_service.py new file mode 100644 index 00000000..5c3cd935 --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_lock_service.py @@ -0,0 +1,67 @@ +import threading +from typing import Any +from typing import List +from typing import Optional + +from flask import current_app + +from spiffworkflow_backend.models.process_instance_queue import ( + ProcessInstanceQueueModel, +) + + +class ProcessInstanceLockService: + """TODO: comment.""" + + @classmethod + def set_thread_local_locking_context(cls, domain: str) -> None: + current_app.config["THREAD_LOCAL_DATA"].lock_service_context = { + "domain": domain, + "uuid": current_app.config["PROCESS_UUID"], + "thread_id": threading.get_ident(), + "locks": {}, + } + + @classmethod + def get_thread_local_locking_context(cls) -> dict[str, Any]: + tld = current_app.config["THREAD_LOCAL_DATA"] + if not hasattr(tld, "lock_service_context"): + cls.set_thread_local_locking_context("web") + return tld.lock_service_context # type: ignore + + @classmethod + def locked_by(cls) -> str: + ctx = cls.get_thread_local_locking_context() + return f"{ctx['domain']}:{ctx['uuid']}:{ctx['thread_id']}" + + @classmethod + def lock( + cls, process_instance_id: int, queue_entry: ProcessInstanceQueueModel + ) -> None: + ctx = cls.get_thread_local_locking_context() + ctx["locks"][process_instance_id] = queue_entry + + @classmethod + def lock_many(cls, queue_entries: List[ProcessInstanceQueueModel]) -> List[int]: + ctx = cls.get_thread_local_locking_context() + new_locks = {entry.process_instance_id: entry for entry in queue_entries} + new_lock_ids = list(new_locks.keys()) + ctx["locks"].update(new_locks) + return new_lock_ids + + @classmethod + def unlock(cls, process_instance_id: int) -> ProcessInstanceQueueModel: + ctx = cls.get_thread_local_locking_context() + return ctx["locks"].pop(process_instance_id) # type: ignore + + @classmethod + def try_unlock( + cls, process_instance_id: int + ) -> Optional[ProcessInstanceQueueModel]: + ctx = cls.get_thread_local_locking_context() + return ctx["locks"].pop(process_instance_id, None) # type: ignore + + @classmethod + def has_lock(cls, process_instance_id: int) -> bool: + ctx = cls.get_thread_local_locking_context() + return process_instance_id in ctx["locks"] diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index b2ce4cfd..f78a3fd4 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -51,7 +51,6 @@ from SpiffWorkflow.spiff.serializer.config import SPIFF_SPEC_CONFIG # type: ign from SpiffWorkflow.task import Task as SpiffTask # type: ignore from SpiffWorkflow.task import TaskState from SpiffWorkflow.util.deep_merge import DeepMerge # type: ignore -from sqlalchemy import text from spiffworkflow_backend.exceptions.api_error import ApiError from spiffworkflow_backend.models.bpmn_process import BpmnProcessModel @@ -89,6 +88,12 @@ from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.scripts.script import Script from spiffworkflow_backend.services.custom_parser import MyCustomParser from spiffworkflow_backend.services.file_system_service import FileSystemService +from spiffworkflow_backend.services.process_instance_lock_service import ( + ProcessInstanceLockService, +) +from spiffworkflow_backend.services.process_instance_queue_service import ( + ProcessInstanceQueueService, +) from spiffworkflow_backend.services.process_model_service import ProcessModelService from spiffworkflow_backend.services.service_task_service import ServiceTaskDelegate from spiffworkflow_backend.services.spec_file_service import SpecFileService @@ -143,14 +148,6 @@ class MissingProcessInfoError(Exception): """MissingProcessInfoError.""" -class ProcessInstanceIsAlreadyLockedError(Exception): - pass - - -class ProcessInstanceLockedBySomethingElseError(Exception): - pass - - class SpiffStepDetailIsMissingError(Exception): pass @@ -1253,6 +1250,8 @@ class ProcessInstanceProcessor: self.bpmn_process_instance.catch(event_definition) except Exception as e: print(e) + + # TODO: do_engine_steps without a lock self.do_engine_steps(save=True) def add_step(self, step: Union[dict, None] = None) -> None: @@ -1543,55 +1542,13 @@ class ProcessInstanceProcessor: # current_app.logger.debug(f"the_status: {the_status} for instance {self.process_instance_model.id}") return the_status - # inspiration from https://github.com/collectiveidea/delayed_job_active_record/blob/master/lib/delayed/backend/active_record.rb - # could consider borrowing their "cleanup all my locks when the app quits" idea as well and - # implement via https://docs.python.org/3/library/atexit.html + # TODO: replace with implicit/more granular locking in workflow execution service def lock_process_instance(self, lock_prefix: str) -> None: - current_app.config["THREAD_LOCAL_DATA"].locked_by_prefix = lock_prefix - locked_by = f"{lock_prefix}_{current_app.config['PROCESS_UUID']}" - current_time_in_seconds = round(time.time()) - lock_expiry_in_seconds = ( - current_time_in_seconds - - current_app.config[ - "SPIFFWORKFLOW_BACKEND_ALLOW_CONFISCATING_LOCK_AFTER_SECONDS" - ] - ) - - query_text = text( - "UPDATE process_instance SET locked_at_in_seconds =" - " :current_time_in_seconds, locked_by = :locked_by where id = :id AND" - " (locked_by IS NULL OR locked_at_in_seconds < :lock_expiry_in_seconds);" - ).execution_options(autocommit=True) - result = db.engine.execute( - query_text, - id=self.process_instance_model.id, - current_time_in_seconds=current_time_in_seconds, - locked_by=locked_by, - lock_expiry_in_seconds=lock_expiry_in_seconds, - ) - # it seems like autocommit is working above (we see the statement in debug logs) but sqlalchemy doesn't - # seem to update properly so tell it to commit as well. - # if we omit this line then querying the record from a unit test doesn't ever show the record as locked. - db.session.commit() - if result.rowcount < 1: - raise ProcessInstanceIsAlreadyLockedError( - f"Cannot lock process instance {self.process_instance_model.id}. " - "It has already been locked." - ) + ProcessInstanceQueueService.dequeue(self.process_instance_model) + # TODO: replace with implicit/more granular locking in workflow execution service def unlock_process_instance(self, lock_prefix: str) -> None: - current_app.config["THREAD_LOCAL_DATA"].locked_by_prefix = None - locked_by = f"{lock_prefix}_{current_app.config['PROCESS_UUID']}" - if self.process_instance_model.locked_by != locked_by: - raise ProcessInstanceLockedBySomethingElseError( - f"Cannot unlock process instance {self.process_instance_model.id}." - f"It locked by {self.process_instance_model.locked_by}" - ) - - self.process_instance_model.locked_by = None - self.process_instance_model.locked_at_in_seconds = None - db.session.add(self.process_instance_model) - db.session.commit() + ProcessInstanceQueueService.enqueue(self.process_instance_model) def process_bpmn_messages(self) -> None: """Process_bpmn_messages.""" @@ -1657,7 +1614,7 @@ class ProcessInstanceProcessor: self, exit_at: None = None, save: bool = False, - execution_strategy_name: str = "greedy", + execution_strategy_name: Optional[str] = None, ) -> None: """Do_engine_steps.""" @@ -1677,6 +1634,12 @@ class ProcessInstanceProcessor: serializer=self._serializer, process_instance=self.process_instance_model, ) + + if execution_strategy_name is None: + execution_strategy_name = current_app.config[ + "SPIFFWORKFLOW_BACKEND_ENGINE_STEP_DEFAULT_STRATEGY_WEB" + ] + execution_strategy = execution_strategy_named( execution_strategy_name, task_model_delegate ) @@ -1692,12 +1655,9 @@ class ProcessInstanceProcessor: # log the spiff step details so we know what is processing the process # instance when a human task has a timer event. def log_spiff_step_details(self, step_details: Any) -> None: - tld = current_app.config["THREAD_LOCAL_DATA"] - if hasattr(tld, "locked_by_prefix") and len(step_details) > 0: - locked_by_prefix = tld.locked_by_prefix - message = ( - f"ADDING SPIFF BULK STEP DETAILS: {locked_by_prefix}: {step_details}" - ) + if ProcessInstanceLockService.has_lock(self.process_instance_model.id): + locked_by = ProcessInstanceLockService.locked_by() + message = f"ADDING SPIFF BULK STEP DETAILS: {locked_by}: {step_details}" current_app.logger.debug(message) def cancel_notify(self) -> None: @@ -1712,6 +1672,7 @@ class ProcessInstanceProcessor: bpmn_process_instance.signal("cancel") # generate a cancel signal. bpmn_process_instance.catch(CancelEventDefinition()) # Due to this being static, can't save granular step details in this case + # TODO: do_engine_steps without a lock bpmn_process_instance.do_engine_steps() except WorkflowTaskException as we: raise ApiError.from_workflow_exception("task_error", str(we), we) from we diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_queue_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_queue_service.py new file mode 100644 index 00000000..d9f900b2 --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_queue_service.py @@ -0,0 +1,110 @@ +import time +from typing import List + +from flask import current_app + +from spiffworkflow_backend.models.db import db +from spiffworkflow_backend.models.process_instance import ProcessInstanceModel +from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus +from spiffworkflow_backend.models.process_instance_queue import ( + ProcessInstanceQueueModel, +) +from spiffworkflow_backend.services.process_instance_lock_service import ( + ProcessInstanceLockService, +) + + +class ProcessInstanceIsAlreadyLockedError(Exception): + pass + + +class ProcessInstanceQueueService: + """TODO: comment.""" + + @staticmethod + def enqueue(process_instance: ProcessInstanceModel) -> None: + queue_item = ProcessInstanceLockService.try_unlock(process_instance.id) + + if queue_item is None: + queue_item = ProcessInstanceQueueModel( + process_instance_id=process_instance.id + ) + + # TODO: configurable params (priority/run_at) + queue_item.run_at_in_seconds = round(time.time()) + queue_item.priority = 2 + queue_item.status = process_instance.status + queue_item.locked_by = None + queue_item.locked_at_in_seconds = None + + db.session.add(queue_item) + db.session.commit() + + @staticmethod + def dequeue(process_instance: ProcessInstanceModel) -> None: + if ProcessInstanceLockService.has_lock(process_instance.id): + return + + locked_by = ProcessInstanceLockService.locked_by() + + db.session.query(ProcessInstanceQueueModel).filter( + ProcessInstanceQueueModel.process_instance_id == process_instance.id, + ProcessInstanceQueueModel.locked_by.is_(None), # type: ignore + ).update( + { + "locked_by": locked_by, + } + ) + + db.session.commit() + + queue_entry = ( + db.session.query(ProcessInstanceQueueModel) + .filter( + ProcessInstanceQueueModel.process_instance_id == process_instance.id, + ProcessInstanceQueueModel.locked_by == locked_by, + ) + .first() + ) + + if queue_entry is None: + raise ProcessInstanceIsAlreadyLockedError( + f"Cannot lock process instance {process_instance.id}. " + "It has already been locked or has not been enqueued." + ) + + ProcessInstanceLockService.lock(process_instance.id, queue_entry) + + @staticmethod + def dequeue_many( + status_value: str = ProcessInstanceStatus.waiting.value, + ) -> List[int]: + locked_by = ProcessInstanceLockService.locked_by() + + # TODO: configurable params (priority/run_at/limit) + db.session.query(ProcessInstanceQueueModel).filter( + ProcessInstanceQueueModel.status == status_value, + ProcessInstanceQueueModel.locked_by.is_(None), # type: ignore + ).update( + { + "locked_by": locked_by, + } + ) + + db.session.commit() + + queue_entries = ( + db.session.query(ProcessInstanceQueueModel) + .filter( + ProcessInstanceQueueModel.status == status_value, + ProcessInstanceQueueModel.locked_by == locked_by, + ) + .all() + ) + + locked_ids = ProcessInstanceLockService.lock_many(queue_entries) + + if len(locked_ids) > 0: + current_app.logger.info(f"{locked_by} dequeued_many: {locked_ids}") + + return locked_ids diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py index b3959ea8..dfeb2bde 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_service.py @@ -29,10 +29,13 @@ from spiffworkflow_backend.services.authorization_service import AuthorizationSe from spiffworkflow_backend.services.git_service import GitCommandError from spiffworkflow_backend.services.git_service import GitService from spiffworkflow_backend.services.process_instance_processor import ( + ProcessInstanceProcessor, +) +from spiffworkflow_backend.services.process_instance_queue_service import ( ProcessInstanceIsAlreadyLockedError, ) -from spiffworkflow_backend.services.process_instance_processor import ( - ProcessInstanceProcessor, +from spiffworkflow_backend.services.process_instance_queue_service import ( + ProcessInstanceQueueService, ) from spiffworkflow_backend.services.process_model_service import ProcessModelService @@ -81,9 +84,15 @@ class ProcessInstanceService: @staticmethod def do_waiting(status_value: str = ProcessInstanceStatus.waiting.value) -> None: """Do_waiting.""" + locked_process_instance_ids = ProcessInstanceQueueService.dequeue_many( + status_value + ) + if len(locked_process_instance_ids) == 0: + return + records = ( db.session.query(ProcessInstanceModel) - .filter(ProcessInstanceModel.status == status_value) + .filter(ProcessInstanceModel.id.in_(locked_process_instance_ids)) # type: ignore .all() ) process_instance_lock_prefix = "Background" @@ -97,7 +106,12 @@ class ProcessInstanceService: processor = ProcessInstanceProcessor(process_instance) processor.lock_process_instance(process_instance_lock_prefix) locked = True - processor.do_engine_steps(save=True) + execution_strategy_name = current_app.config[ + "SPIFFWORKFLOW_BACKEND_ENGINE_STEP_DEFAULT_STRATEGY_BACKGROUND" + ] + processor.do_engine_steps( + save=True, execution_strategy_name=execution_strategy_name + ) except ProcessInstanceIsAlreadyLockedError: continue except Exception as e: diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index 864885e5..1ab22ee4 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -4,6 +4,7 @@ from typing import Callable from typing import List from typing import Optional +from flask import current_app from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer # type: ignore from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore from SpiffWorkflow.exceptions import SpiffWorkflowException # type: ignore @@ -19,6 +20,9 @@ from spiffworkflow_backend.models.message_instance_correlation import ( from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel from spiffworkflow_backend.models.task import TaskModel # noqa: F401 +from spiffworkflow_backend.services.process_instance_lock_service import ( + ProcessInstanceLockService, +) from spiffworkflow_backend.services.task_service import JsonDataDict from spiffworkflow_backend.services.task_service import TaskService @@ -202,7 +206,7 @@ class ExecutionStrategy: class GreedyExecutionStrategy(ExecutionStrategy): - """The common execution strategy. This will greedily run all engine step without stopping.""" + """The common execution strategy. This will greedily run all engine steps without stopping.""" def do_engine_steps( self, bpmn_process_instance: BpmnWorkflow, exit_at: None = None @@ -286,9 +290,16 @@ class WorkflowExecutionService: def do_engine_steps(self, exit_at: None = None, save: bool = False) -> None: """Do_engine_steps.""" + if not ProcessInstanceLockService.has_lock(self.process_instance_model.id): + # TODO: can't be an exception yet - believe there are flows that are not locked. + current_app.logger.error( + "The current thread has not obtained a lock for this process instance.", + ) + try: self.bpmn_process_instance.refresh_waiting_tasks() + # TODO: implicit re-entrant locks here `with_dequeued` self.execution_strategy.do_engine_steps(self.bpmn_process_instance, exit_at) if self.bpmn_process_instance.is_completed(): diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py index 3b1c3344..704d7379 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py @@ -25,6 +25,9 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.file_system_service import FileSystemService +from spiffworkflow_backend.services.process_instance_queue_service import ( + ProcessInstanceQueueService, +) from spiffworkflow_backend.services.process_model_service import ProcessModelService from spiffworkflow_backend.services.user_service import UserService @@ -308,6 +311,9 @@ class BaseTest: ) db.session.add(process_instance) db.session.commit() + + ProcessInstanceQueueService.enqueue(process_instance) + return process_instance @classmethod diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py index e1618f61..3452dcf1 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_instance_processor.py @@ -18,15 +18,12 @@ from spiffworkflow_backend.services.authorization_service import AuthorizationSe from spiffworkflow_backend.services.authorization_service import ( UserDoesNotHaveAccessToTaskError, ) -from spiffworkflow_backend.services.process_instance_processor import ( - ProcessInstanceIsAlreadyLockedError, -) -from spiffworkflow_backend.services.process_instance_processor import ( - ProcessInstanceLockedBySomethingElseError, -) from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, ) +from spiffworkflow_backend.services.process_instance_queue_service import ( + ProcessInstanceIsAlreadyLockedError, +) from spiffworkflow_backend.services.process_instance_service import ( ProcessInstanceService, ) @@ -436,7 +433,8 @@ class TestProcessInstanceProcessor(BaseTest): assert len(process_instance.active_human_tasks) == 1 assert initial_human_task_id == process_instance.active_human_tasks[0].id - def test_it_can_lock_and_unlock_a_process_instance( + # TODO: port this test to queue_service test + def xxx_test_it_can_lock_and_unlock_a_process_instance( self, app: Flask, client: FlaskClient, @@ -465,8 +463,8 @@ class TestProcessInstanceProcessor(BaseTest): with pytest.raises(ProcessInstanceIsAlreadyLockedError): processor.lock_process_instance("TEST") - with pytest.raises(ProcessInstanceLockedBySomethingElseError): - processor.unlock_process_instance("TEST2") + # with pytest.raises(ProcessInstanceLockedBySomethingElseError): + # processor.unlock_process_instance("TEST2") processor.unlock_process_instance("TEST") From d7e7ee823b50c3b6f798ab65162ec0d6dce9a960 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 14 Mar 2023 13:24:12 -0400 Subject: [PATCH 43/48] Remove nav from task show page --- .../services/process_instance_processor.py | 4 ---- spiffworkflow-frontend/src/routes/TaskShow.tsx | 5 ++++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index b2ce4cfd..e095ff15 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -97,9 +97,6 @@ from spiffworkflow_backend.services.user_service import UserService from spiffworkflow_backend.services.workflow_execution_service import ( execution_strategy_named, ) -from spiffworkflow_backend.services.workflow_execution_service import ( - StepDetailLoggingDelegate, -) from spiffworkflow_backend.services.workflow_execution_service import ( TaskModelSavingDelegate, ) @@ -1660,7 +1657,6 @@ class ProcessInstanceProcessor: execution_strategy_name: str = "greedy", ) -> None: """Do_engine_steps.""" - # NOTE: Commenting out to test how this changes performance: # def spiff_step_details_mapping_builder( # task: SpiffTask, start: float, end: float diff --git a/spiffworkflow-frontend/src/routes/TaskShow.tsx b/spiffworkflow-frontend/src/routes/TaskShow.tsx index fbf0bd81..058ee0b5 100644 --- a/spiffworkflow-frontend/src/routes/TaskShow.tsx +++ b/spiffworkflow-frontend/src/routes/TaskShow.tsx @@ -23,7 +23,7 @@ import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; export default function TaskShow() { const [task, setTask] = useState(null); - const [userTasks, setUserTasks] = useState(null); + const [userTasks] = useState(null); const params = useParams(); const navigate = useNavigate(); const [disabled, setDisabled] = useState(false); @@ -33,6 +33,8 @@ export default function TaskShow() { useEffect(() => { const processResult = (result: ProcessInstanceTask) => { setTask(result); + setDisabled(false); + /* Disable call to load previous tasks -- do not display menu. const url = `/v1.0/process-instances/for-me/${modifyProcessIdentifierForPathParam( result.process_model_identifier )}/${params.process_instance_id}/task-info`; @@ -52,6 +54,7 @@ export default function TaskShow() { addError(error); }, }); + */ }; HttpService.makeCallToBackend({ path: `/tasks/${params.process_instance_id}/${params.task_id}`, From 11339f3130bdf1ab8ec3084642953a2c852f36c0 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 14 Mar 2023 14:02:15 -0400 Subject: [PATCH 44/48] remove any filterable columns when doing a "clear" or "reset" on the filter form. Also clear out the process initiator. --- .../src/components/ProcessInstanceListTable.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx index 4b0498b7..9f8d258c 100644 --- a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx +++ b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx @@ -702,6 +702,14 @@ export default function ProcessInstanceListTable({ setEndFromTime(''); setEndToDate(''); setEndToTime(''); + setProcessInitiatorSelection(null); + setProcessInitiatorText(''); + + if (reportMetadata) { + reportMetadata.columns = reportMetadata.columns.filter( + (column) => !column.filterable + ); + } }; const processInstanceReportDidChange = (selection: any, mode?: string) => { From 40b23c31c7840f2450a583b86390a8c701054469 Mon Sep 17 00:00:00 2001 From: burnettk Date: Tue, 14 Mar 2023 15:08:10 -0400 Subject: [PATCH 45/48] add list with just one user for bootstrapping --- spiffworkflow-backend/keycloak/test_user_lists/admin | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 spiffworkflow-backend/keycloak/test_user_lists/admin diff --git a/spiffworkflow-backend/keycloak/test_user_lists/admin b/spiffworkflow-backend/keycloak/test_user_lists/admin new file mode 100644 index 00000000..aa676cd9 --- /dev/null +++ b/spiffworkflow-backend/keycloak/test_user_lists/admin @@ -0,0 +1,2 @@ +email,spiffworkflow-employeeid +admin@spiffworkflow.org From a38aa139a61268110721998cd9b411ce49ff25e1 Mon Sep 17 00:00:00 2001 From: burnettk Date: Tue, 14 Mar 2023 17:54:20 -0400 Subject: [PATCH 46/48] put back spiff step details for today --- .../services/process_instance_processor.py | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 54432f67..dd2b405a 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -102,6 +102,9 @@ from spiffworkflow_backend.services.user_service import UserService from spiffworkflow_backend.services.workflow_execution_service import ( execution_strategy_named, ) +from spiffworkflow_backend.services.workflow_execution_service import ( + StepDetailLoggingDelegate, +) from spiffworkflow_backend.services.workflow_execution_service import ( TaskModelSavingDelegate, ) @@ -1613,20 +1616,19 @@ class ProcessInstanceProcessor: save: bool = False, execution_strategy_name: Optional[str] = None, ) -> None: - """Do_engine_steps.""" - # NOTE: Commenting out to test how this changes performance: - # def spiff_step_details_mapping_builder( - # task: SpiffTask, start: float, end: float - # ) -> dict: - # self._script_engine.environment.revise_state_with_task_data(task) - # return self.spiff_step_details_mapping(task, start, end) - # - # step_delegate = StepDetailLoggingDelegate( - # self.increment_spiff_step, spiff_step_details_mapping_builder - # ) + # NOTE: To avoid saving spiff step details, just comment out this function and the step_delegate and + # set the TaskModelSavingDelegate's secondary_engine_step_delegate to None. + def spiff_step_details_mapping_builder( + task: SpiffTask, start: float, end: float + ) -> dict: + self._script_engine.environment.revise_state_with_task_data(task) + return self.spiff_step_details_mapping(task, start, end) + + step_delegate = StepDetailLoggingDelegate( + self.increment_spiff_step, spiff_step_details_mapping_builder + ) task_model_delegate = TaskModelSavingDelegate( - # secondary_engine_step_delegate=step_delegate, - secondary_engine_step_delegate=None, + secondary_engine_step_delegate=step_delegate, serializer=self._serializer, process_instance=self.process_instance_model, ) From 1f2845962cb16cac4e577464b8df1a567417699e Mon Sep 17 00:00:00 2001 From: burnettk Date: Tue, 14 Mar 2023 18:10:49 -0400 Subject: [PATCH 47/48] exclude connexion logging even in debug mode --- .../services/logging_service.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/logging_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/logging_service.py index 401d071e..77adeaf3 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/logging_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/logging_service.py @@ -161,6 +161,9 @@ def setup_logger(app: Flask) -> None: spiff_logger_filehandler.setLevel(spiff_log_level) spiff_logger_filehandler.setFormatter(log_formatter) + # these loggers have been deemed too verbose to be useful + garbage_loggers_to_exclude = ["connexion"] + # make all loggers act the same for name in logging.root.manager.loggerDict: # use a regex so spiffworkflow_backend isn't filtered out @@ -172,10 +175,15 @@ def setup_logger(app: Flask) -> None: the_logger.propagate = False the_logger.addHandler(spiff_logger_filehandler) else: - if len(the_logger.handlers) < 1: - # it's very verbose, so only add handlers for the obscure loggers when log level is DEBUG - if upper_log_level_string == "DEBUG": - the_logger.addHandler(logging.StreamHandler(sys.stdout)) + # it's very verbose, so only add handlers for the obscure loggers when log level is DEBUG + if upper_log_level_string == "DEBUG": + if len(the_logger.handlers) < 1: + exclude_logger_name_from_logging = False + for garbage_logger in garbage_loggers_to_exclude: + if name.startswith(garbage_logger): + exclude_logger_name_from_logging = True + if not exclude_logger_name_from_logging: + the_logger.addHandler(logging.StreamHandler(sys.stdout)) for the_handler in the_logger.handlers: the_handler.setFormatter(log_formatter) the_handler.setLevel(log_level) From 569995aa4649c6419ff8dee627846e337a68d7cc Mon Sep 17 00:00:00 2001 From: jbirddog <100367399+jbirddog@users.noreply.github.com> Date: Tue, 14 Mar 2023 20:22:57 -0400 Subject: [PATCH 48/48] Safe asserts (#180) --- .../services/assertion_service.py | 18 ++++++++++++++++++ .../services/workflow_execution_service.py | 15 +++++++++------ 2 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 spiffworkflow-backend/src/spiffworkflow_backend/services/assertion_service.py diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/assertion_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/assertion_service.py new file mode 100644 index 00000000..b9f7c61b --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/assertion_service.py @@ -0,0 +1,18 @@ +"""Assertion_service.""" +import contextlib +from typing import Generator + +import sentry_sdk +from flask import current_app + + +@contextlib.contextmanager +def safe_assertion(condition: bool) -> Generator[bool, None, None]: + try: + yield True + except AssertionError as e: + if not condition: + sentry_sdk.capture_exception(e) + current_app.logger.exception(e) + if current_app.config["ENV_IDENTIFIER"] == "local_development": + raise e diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py index 1ab22ee4..386f2054 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/workflow_execution_service.py @@ -4,7 +4,6 @@ from typing import Callable from typing import List from typing import Optional -from flask import current_app from SpiffWorkflow.bpmn.serializer.workflow import BpmnWorkflowSerializer # type: ignore from SpiffWorkflow.bpmn.workflow import BpmnWorkflow # type: ignore from SpiffWorkflow.exceptions import SpiffWorkflowException # type: ignore @@ -20,6 +19,7 @@ from spiffworkflow_backend.models.message_instance_correlation import ( from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel from spiffworkflow_backend.models.task import TaskModel # noqa: F401 +from spiffworkflow_backend.services.assertion_service import safe_assertion from spiffworkflow_backend.services.process_instance_lock_service import ( ProcessInstanceLockService, ) @@ -290,11 +290,14 @@ class WorkflowExecutionService: def do_engine_steps(self, exit_at: None = None, save: bool = False) -> None: """Do_engine_steps.""" - if not ProcessInstanceLockService.has_lock(self.process_instance_model.id): - # TODO: can't be an exception yet - believe there are flows that are not locked. - current_app.logger.error( - "The current thread has not obtained a lock for this process instance.", - ) + with safe_assertion( + ProcessInstanceLockService.has_lock(self.process_instance_model.id) + ) as tripped: + if tripped: + raise AssertionError( + "The current thread has not obtained a lock for this process" + f" instance ({self.process_instance_model.id})." + ) try: self.bpmn_process_instance.refresh_waiting_tasks()