Example #1
0
def workflow_undo_redo(request, pk, action, format=None):
    workflow = _lookup_workflow_for_write(pk, request)

    if action == 'undo':
        WorkflowUndo(workflow)
    elif action == 'redo':
        WorkflowRedo(workflow)
    else:
        return JsonResponse({'message': '"action" must be "undo" or "redo"'},
                            status=status.HTTP_400_BAD_REQUEST)

    return Response(status=status.HTTP_204_NO_CONTENT)
Example #2
0
def workflow_undo_redo(request, pk, action, format=None):
    try:
        workflow = Workflow.objects.get(pk=pk)
    except Workflow.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if not workflow.user_authorized_write(request.user):
        return HttpResponseForbidden()

    if action=='undo':
        WorkflowUndo(workflow)
    elif action=='redo':
        WorkflowRedo(workflow)

    return Response(status=status.HTTP_204_NO_CONTENT)
Example #3
0
    def test_undo_redo(self):
        ModuleVersion.create_or_replace_from_spec({
            'id_name':
            'pastecsv',
            'name':
            'pastecsv',
            'category':
            'Clean',
            'parameters': [
                {
                    'id_name': 'csv',
                    'type': 'string'
                },
            ]
        })

        workflow = Workflow.create_and_init()
        tab = workflow.tabs.first()

        all_modules = tab.live_wf_modules  # beginning state: nothing

        v0 = workflow.last_delta_id

        # Test undoing nothing at all. Should NOP
        self.run_with_async_db(WorkflowUndo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v0)
        self.assertEqual(all_modules.count(), 0)
        self.assertEqual(workflow.last_delta_id, v0)

        # Add a module
        cmd1 = self.run_with_async_db(
            AddModuleCommand.create(workflow=workflow,
                                    tab=tab,
                                    module_id_name='pastecsv',
                                    position=0,
                                    param_values={}))
        v1 = cmd1.id
        workflow.refresh_from_db()
        self.assertEqual(all_modules.count(), 1)
        self.assertGreater(v1, v0)
        self.assertEqual(workflow.last_delta_id, v1)
        self.assertWfModuleVersions(tab, [v1])

        # Undo, ensure we are back at start
        self.run_with_async_db(WorkflowUndo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(all_modules.count(), 0)
        self.assertEqual(workflow.last_delta_id, v0)
        self.assertWfModuleVersions(tab, [])

        # Redo, ensure we are back at v1
        self.run_with_async_db(WorkflowRedo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(all_modules.count(), 1)
        self.assertEqual(workflow.last_delta_id, v1)
        self.assertWfModuleVersions(tab, [v1])

        # Change a parameter
        cmd2 = self.run_with_async_db(
            ChangeParametersCommand.create(
                workflow=workflow,
                wf_module=tab.live_wf_modules.first(),
                new_values={'csv': 'some value'}))
        v2 = cmd2.id
        workflow.refresh_from_db()
        self.assertEqual(tab.live_wf_modules.first().params['csv'],
                         'some value')
        self.assertEqual(workflow.last_delta_id, v2)
        self.assertGreater(v2, v1)
        self.assertWfModuleVersions(tab, [v2])

        # Undo parameter change
        self.run_with_async_db(WorkflowUndo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v1)
        self.assertEqual(tab.live_wf_modules.first().params['csv'], '')
        self.assertWfModuleVersions(tab, [v1])

        # Redo
        self.run_with_async_db(WorkflowRedo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v2)
        self.assertEqual(tab.live_wf_modules.first().params['csv'],
                         'some value')
        self.assertWfModuleVersions(tab, [v2])

        # Redo again should do nothing
        self.run_with_async_db(WorkflowRedo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v2)
        self.assertEqual(tab.live_wf_modules.first().params['csv'],
                         'some value')
        self.assertWfModuleVersions(tab, [v2])

        # Add one more command so the stack is 3 deep
        cmd3 = self.run_with_async_db(
            ChangeWorkflowTitleCommand.create(workflow=workflow,
                                              new_value='New Title'))
        v3 = cmd3.id
        self.assertGreater(v3, v2)
        self.assertWfModuleVersions(tab, [v2])

        # Undo twice
        self.run_with_async_db(WorkflowUndo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta, cmd2)
        self.assertWfModuleVersions(tab, [v2])
        self.run_with_async_db(WorkflowUndo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta, cmd1)
        self.assertWfModuleVersions(tab, [v1])

        # Redo twice
        self.run_with_async_db(WorkflowRedo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta, cmd2)
        self.assertWfModuleVersions(tab, [v2])
        self.run_with_async_db(WorkflowRedo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta, cmd3)
        self.assertWfModuleVersions(tab, [v2])

        # Undo again to get to a place where we have two commands to redo
        self.run_with_async_db(WorkflowUndo(workflow))
        self.run_with_async_db(WorkflowUndo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta, cmd1)

        # Now add a new command. It should remove cmd2, cmd3 from the redo
        # stack and delete them from the db
        wfm = all_modules.first()
        cmd4 = self.run_with_async_db(
            ChangeWfModuleNotesCommand.create(workflow=workflow,
                                              wf_module=wfm,
                                              new_value='Note of no note'))
        v4 = cmd4.id
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v4)
        self.assertEqual(set(Delta.objects.values_list('id', flat=True)),
                         {v0, v1, v4})  # v2, v3 deleted

        # Undo back to start, then add a command, ensure it deletes dangling
        # commands (tests an edge case in Delta.save)
        self.run_with_async_db(WorkflowUndo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v1)
        cmd5 = self.run_with_async_db(
            ChangeWfModuleNotesCommand.create(workflow=workflow,
                                              wf_module=cmd1.wf_module,
                                              new_value='Note of some note'))
        v5 = cmd5.id
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v5)
        self.assertEqual(set(Delta.objects.values_list('id', flat=True)),
                         {v0, v1, v5})  # v1, v4 deleted
        self.assertWfModuleVersions(tab, [v1])
    def test_undo_redo(self):
        mz = create_module_zipfile(
            "loadsomething",
            spec_kwargs={"parameters": [{"id_name": "csv", "type": "string"}]},
        )
        self.kernel.migrate_params.side_effect = lambda m, p: p

        workflow = Workflow.create_and_init()
        tab = workflow.tabs.first()

        all_modules = tab.live_wf_modules  # beginning state: nothing

        v0 = workflow.last_delta_id

        # Test undoing nothing at all. Should NOP
        self.run_with_async_db(WorkflowUndo(workflow.id))
        workflow.refresh_from_db()
        self.assertEqual(all_modules.count(), 0)
        self.assertEqual(workflow.last_delta_id, v0)

        # Add a module
        cmd1 = self.run_with_async_db(
            commands.do(
                AddModuleCommand,
                workflow_id=workflow.id,
                tab=tab,
                slug="step-1",
                module_id_name="loadsomething",
                position=0,
                param_values={},
            )
        )
        v1 = cmd1.id
        workflow.refresh_from_db()
        self.assertEqual(all_modules.count(), 1)
        self.assertGreater(v1, v0)
        self.assertEqual(workflow.last_delta_id, v1)
        self.assertWfModuleVersions(tab, [v1])

        # Undo, ensure we are back at start
        self.run_with_async_db(WorkflowUndo(workflow.id))
        workflow.refresh_from_db()
        self.assertEqual(all_modules.count(), 0)
        self.assertEqual(workflow.last_delta_id, v0)
        self.assertWfModuleVersions(tab, [])

        # Redo, ensure we are back at v1
        self.run_with_async_db(WorkflowRedo(workflow.id))
        workflow.refresh_from_db()
        self.assertEqual(all_modules.count(), 1)
        self.assertEqual(workflow.last_delta_id, v1)
        self.assertWfModuleVersions(tab, [v1])

        # Change a parameter
        with self.assertLogs(level=logging.INFO):
            cmd2 = self.run_with_async_db(
                commands.do(
                    ChangeParametersCommand,
                    workflow_id=workflow.id,
                    wf_module=tab.live_wf_modules.first(),
                    new_values={"csv": "some value"},
                )
            )
        v2 = cmd2.id
        workflow.refresh_from_db()
        self.assertEqual(tab.live_wf_modules.first().params["csv"], "some value")
        self.assertEqual(workflow.last_delta_id, v2)
        self.assertGreater(v2, v1)
        self.assertWfModuleVersions(tab, [v2])

        # Undo parameter change
        with self.assertLogs(level=logging.INFO):
            self.run_with_async_db(WorkflowUndo(workflow.id))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v1)
        self.assertEqual(tab.live_wf_modules.first().params["csv"], "")
        self.assertWfModuleVersions(tab, [v1])

        # Redo
        with self.assertLogs(level=logging.INFO):
            self.run_with_async_db(WorkflowRedo(workflow.id))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v2)
        self.assertEqual(tab.live_wf_modules.first().params["csv"], "some value")
        self.assertWfModuleVersions(tab, [v2])

        # Redo again should do nothing
        self.run_with_async_db(WorkflowRedo(workflow.id))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v2)
        self.assertEqual(tab.live_wf_modules.first().params["csv"], "some value")
        self.assertWfModuleVersions(tab, [v2])

        # Add one more command so the stack is 3 deep
        cmd3 = self.run_with_async_db(
            commands.do(
                ChangeWorkflowTitleCommand,
                workflow_id=workflow.id,
                new_value="New Title",
            )
        )
        v3 = cmd3.id
        self.assertGreater(v3, v2)
        self.assertWfModuleVersions(tab, [v2])

        # Undo twice
        self.run_with_async_db(WorkflowUndo(workflow.id))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta, cmd2)
        self.assertWfModuleVersions(tab, [v2])
        with self.assertLogs(level=logging.INFO):
            self.run_with_async_db(WorkflowUndo(workflow.id))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta, cmd1)
        self.assertWfModuleVersions(tab, [v1])

        # Redo twice
        with self.assertLogs(level=logging.INFO):
            self.run_with_async_db(WorkflowRedo(workflow.id))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta, cmd2)
        self.assertWfModuleVersions(tab, [v2])
        self.run_with_async_db(WorkflowRedo(workflow.id))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta, cmd3)
        self.assertWfModuleVersions(tab, [v2])

        # Undo again to get to a place where we have two commands to redo
        self.run_with_async_db(WorkflowUndo(workflow.id))
        with self.assertLogs(level=logging.INFO):
            self.run_with_async_db(WorkflowUndo(workflow.id))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta, cmd1)

        # Now add a new command. It should remove cmd2, cmd3 from the redo
        # stack and delete them from the db
        step = all_modules.first()
        cmd4 = self.run_with_async_db(
            commands.do(
                ChangeWfModuleNotesCommand,
                workflow_id=workflow.id,
                wf_module=step,
                new_value="Note of no note",
            )
        )
        v4 = cmd4.id
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v4)
        self.assertEqual(
            set(Delta.objects.values_list("id", flat=True)), {v0, v1, v4}
        )  # v2, v3 deleted

        # Undo back to start, then add a command, ensure it deletes dangling
        # commands (tests an edge case in Delta.save)
        self.run_with_async_db(WorkflowUndo(workflow.id))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v1)
        cmd5 = self.run_with_async_db(
            commands.do(
                ChangeWfModuleNotesCommand,
                workflow_id=workflow.id,
                wf_module=cmd1.wf_module,
                new_value="Note of some note",
            )
        )
        v5 = cmd5.id
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v5)
        self.assertEqual(
            set(Delta.objects.values_list("id", flat=True)), {v0, v1, v5}
        )  # v1, v4 deleted
        self.assertWfModuleVersions(tab, [v1])
Example #5
0
    def test_undo_redo(self):
        all_modules = self.workflow.wf_modules  # beginning state: nothing
        self.assertEqual(all_modules.count(), 0)
        self.assertEqual(self.workflow.last_delta_id, None)

        v0 = self.workflow.revision()

        # Test undoing nothing at all. Should NOP
        WorkflowUndo(self.workflow)
        self.assertEqual(self.workflow.revision(), v0)
        self.assertEqual(all_modules.count(), 0)
        self.assertEqual(self.workflow.last_delta_id, None)

        # Add a module
        cmd1 = AddModuleCommand.create(self.workflow, self.csv, 0)
        self.assertEqual(all_modules.count(), 1)
        self.assertNotEqual(self.workflow.last_delta_id, None)
        v1 = self.workflow.revision()
        self.assertGreater(v1, v0)
        self.assertWfModuleVersions([v1])

        # Undo, ensure we are back at start
        WorkflowUndo(self.workflow)
        self.assertEqual(self.workflow.revision(), v0)
        self.assertEqual(all_modules.count(), 0)
        self.assertEqual(self.workflow.last_delta_id, None)
        self.assertWfModuleVersions([])

        # Redo, ensure we are back at v1
        WorkflowRedo(self.workflow)
        self.assertEqual(all_modules.count(), 1)
        self.assertNotEqual(self.workflow.last_delta_id, None)
        self.assertEqual(self.workflow.revision(), v1)
        self.assertWfModuleVersions([v1])

        # Change a parameter
        pval = get_param_by_id_name('csv')
        cmd2 = ChangeParameterCommand.create(pval, 'some value')
        self.assertEqual(pval.value, 'some value')
        self.workflow.refresh_from_db()
        v2 = self.workflow.revision()
        self.assertGreater(v2, v1)
        self.assertWfModuleVersions([v2])

        # Undo parameter change
        WorkflowUndo(self.workflow)
        self.assertEqual(self.workflow.revision(), v1)
        pval.refresh_from_db()
        self.assertEqual(pval.value, '')
        self.assertWfModuleVersions([v1])

        # Redo
        WorkflowRedo(self.workflow)
        self.assertEqual(self.workflow.revision(), v2)
        pval.refresh_from_db()
        self.assertEqual(pval.value, 'some value')
        self.assertWfModuleVersions([v2])

        # Redo again should do nothing
        WorkflowRedo(self.workflow)
        self.assertEqual(self.workflow.revision(), v2)
        self.assertEqual(pval.value, 'some value')
        self.assertWfModuleVersions([v2])

        # Add one more command so the stack is 3 deep
        cmd3 = ChangeWorkflowTitleCommand.create(self.workflow, "New Title")
        # self.workflow.refresh_from_db()
        v3 = self.workflow.revision()
        self.assertGreater(v3, v2)
        self.assertWfModuleVersions([v2])

        # Undo twice
        WorkflowUndo(self.workflow)
        self.assertEqual(self.workflow.last_delta, cmd2)
        self.assertWfModuleVersions([v2])
        WorkflowUndo(self.workflow)
        self.assertEqual(self.workflow.last_delta, cmd1)
        self.assertWfModuleVersions([v1])

        # Redo twice
        WorkflowRedo(self.workflow)
        self.assertEqual(self.workflow.last_delta, cmd2)
        self.assertWfModuleVersions([v2])
        WorkflowRedo(self.workflow)
        self.assertEqual(self.workflow.last_delta, cmd3)
        self.assertWfModuleVersions([v2])

        # Undo again to get to a place where we have two commands to redo
        WorkflowUndo(self.workflow)
        WorkflowUndo(self.workflow)
        self.assertEqual(self.workflow.last_delta, cmd1)

        # Now add a new command. It should remove cmd2, cmd3 from the redo
        # stack and delete them from the db
        wfm = all_modules.first()
        cmd4 = ChangeWfModuleNotesCommand.create(wfm, "Note of no note")
        self.workflow.refresh_from_db()
        self.assertEqual(self.workflow.last_delta, cmd4)
        self.assertFalse(Delta.objects.filter(pk=cmd2.id).exists())
        self.assertFalse(Delta.objects.filter(pk=cmd3.id).exists())

        # Undo back to start, then add a command, ensure it deletes dangling
        # commands (tests an edge case in Delta.save)
        self.assertEqual(Delta.objects.count(), 2)
        WorkflowUndo(self.workflow)
        WorkflowUndo(self.workflow)
        self.assertIsNone(self.workflow.last_delta)
        cmd5 = ChangeWfModuleNotesCommand.create(wfm, "Note of some note")
        self.workflow.refresh_from_db()
        self.assertEqual(self.workflow.last_delta, cmd5)
        self.assertFalse(Delta.objects.filter(pk=cmd1.id).exists())
        self.assertFalse(Delta.objects.filter(pk=cmd4.id).exists())
        self.assertWfModuleVersions([v1])