From d0c925363fa4e48e8909d0c129aa2a6b0112e68d Mon Sep 17 00:00:00 2001
From: David Vogt <david.vogt@adfinis.com>
Date: Wed, 21 Apr 2021 16:23:27 +0200
Subject: [PATCH] feat(workflow): commandline tool to show workflow
 configuration

This is a first attempt at visualizing a workflow.
---
 .../management/commands/show_workflow.py      | 80 +++++++++++++++++++
 1 file changed, 80 insertions(+)
 create mode 100644 caluma/caluma_workflow/management/commands/show_workflow.py

diff --git a/caluma/caluma_workflow/management/commands/show_workflow.py b/caluma/caluma_workflow/management/commands/show_workflow.py
new file mode 100644
index 000000000..d4240cc23
--- /dev/null
+++ b/caluma/caluma_workflow/management/commands/show_workflow.py
@@ -0,0 +1,80 @@
+#!/usr/bin/python
+
+
+from logging import getLogger
+
+from django.core.management.base import BaseCommand
+from django.db import transaction
+
+from caluma.caluma_workflow.models import Task, Workflow
+
+log = getLogger(__name__)
+
+
+class Command(BaseCommand):
+    """Dump workflow structure."""
+
+    help = "Show workflow structure"
+
+    def add_arguments(self, parser):
+
+        parser.add_argument(
+            dest="workflow",
+            action="store",
+            help="Slug of the workflow to show",
+        )
+
+    @transaction.atomic
+    def handle(self, *args, **options):
+        self.workflow = Workflow.objects.get(pk=options["workflow"])
+        self.visit(self.workflow)
+
+    def _print(self, indent, data):
+        ind_str = "   " * indent
+        print(f"{ind_str}{data}")
+
+    def visit(self, obj, indent=0):
+        obj_type = str(type(obj).__name__)
+        method_name = f"visit_{obj_type}"
+        method = getattr(self, method_name)
+        return method(obj, indent)
+
+    def visit_Task(self, task, indent):
+        taskflows = task.task_flows.filter(workflow=self.workflow)
+        if taskflows.exists():
+            next = taskflows.get().flow.next
+        else:
+            next = None
+
+        self._print(
+            indent,
+            f"Task {task.slug} ({task.type})",
+        )
+        if task.type == Task.TYPE_COMPLETE_TASK_FORM:
+            # other tasks *may* have a form, but it's not relevant
+            self._print(indent + 1, f"Form: {task.form}")
+        self._print(indent + 1, f"Next: {next}" if next else "(last task in sequence)")
+
+    def visit_Workflow(self, workflow, indent):
+        self._print(indent, f"Workflow {workflow.slug}")
+        self._print(indent + 1, "Allowed forms:")
+        for form in workflow.allow_forms.all():
+            self.visit(form, indent + 2)
+
+        start_tasks = workflow.start_tasks.all()
+        self._print(indent + 1, "Start Tasks:")
+        start_tasks = workflow.start_tasks.all()
+        for task in start_tasks:
+            self.visit(task, indent + 2)
+        self._print(indent + 1, "Other Tasks:")
+
+        other_tasks = (
+            Task.objects.all()
+            .exclude(pk__in=start_tasks)
+            .filter(pk__in=workflow.task_flows.all().values("task"))
+        )
+        for task in other_tasks:
+            self.visit(task, indent + 2)
+
+    def visit_Form(self, form, indent):
+        self._print(indent, f"Form: {form.slug}")