def testSaveLoad(self):
        test_conf = os.path.join(test_path, 'test.conf')
        dm = DumbManager()
        dm._location = test_conf

        s = WorkflowScene(dm)
        ws = QtCore.QSettings(test_conf, QtCore.QSettings.IniFormat)
        ds1 = DumbStep()
        ds1._identifier = '1'
        ds1._name = 'a'
        ds2 = DumbStep()
        ds2._identifier = '2'
        ds2._name = 'b'
        item1 = MetaStep(ds1)
        item2 = MetaStep(ds2)
        s.addItem(item1)
        s.addItem(item2)
        s.saveState(ws)
        del ws

        self.assertTrue(os.path.exists(test_conf))
        file_content = open(test_conf).read()
        self.assertIn('Point(0 0)', file_content)
        self.assertIn('name=a', file_content)
        self.assertIn('name=b', file_content)
        self.assertNotIn('selected=false', file_content)

        os.remove(test_conf)
    def testItemAPI(self):

        item = MetaStep(DumbStep())
        s = WorkflowScene(DumbManager())
        s.addItem(item)
        self.assertEqual(len(s._items), 1)
        s.setItemPos(item, QtCore.QPoint(344, 404))
        self.assertEqual(s._items[item]._pos.x(), 344)
        self.assertEqual(s._items[item]._pos.y(), 404)
        s.setItemSelected(item, False)
        self.assertFalse(s._items[item]._selected)
        s.removeItem(item)
        self.assertEqual(len(s._items), 0)
 def setUp(self):
     self._s = WorkflowScene(DumbManager())
     self._nodes = []
     self._nodes.append(MetaStep(DumbStep()))
     self._nodes.append(MetaStep(DumbStep()))
     self._nodes.append(MetaStep(DumbStep()))
     self._nodes.append(MetaStep(DumbStep()))
     self._nodes.append(MetaStep(DumbStep()))
     self._nodes.append(MetaStep(DumbStep()))
     self._nodes.append(MetaStep(DumbStep()))
    def __init__(self):
        '''
        Constructor
        '''
        self.name = 'WorkflowManager'
#        self.widget = None
#        self.widgetIndex = -1
        self._location = ''
        self._workspace_location = None
        self._conf_filename = None
        self._previousLocation = None
        self._saveStateIndex = 0
        self._currentStateIndex = 0

        self._title = None

        self._scene = WorkflowScene(self)
class WorkflowDependencyGraphTestCase(unittest.TestCase):


    def assertIn(self, a, b, *args, **kwargs):
        ''''Python < v2.7 compatibility.  Assert "a" in "b"'''
        try:
            f = super(WorkflowDependencyGraphTestCase, self).assertIn
        except AttributeError:
            self.assertTrue(a in b, *args, **kwargs)
        else:
            f(a, b, *args, **kwargs)

    def assertNotIn(self, a, b, *args, **kwargs):
        ''''Python < v2.7 compatibility.  Assert "a" NOT in "b"'''
        try:
            f = super(WorkflowDependencyGraphTestCase, self).assertNotIn
        except AttributeError:
            self.assertFalse(a in b, *args, **kwargs)
        else:
            f(a, b, *args, **kwargs)

    def assertLess(self, a, b, *args, **kwargs):
        ''''Python < v2.7 compatibility.  Assert "a" less "b"'''
        try:
            f = super(WorkflowDependencyGraphTestCase, self).assertLess
        except AttributeError:
            self.assertTrue(a < b, *args, **kwargs)
        else:
            f(a, b, *args, **kwargs)

    def setUp(self):
        self._s = WorkflowScene(DumbManager())
        self._nodes = []
        self._nodes.append(MetaStep(DumbStep()))
        self._nodes.append(MetaStep(DumbStep()))
        self._nodes.append(MetaStep(DumbStep()))
        self._nodes.append(MetaStep(DumbStep()))
        self._nodes.append(MetaStep(DumbStep()))
        self._nodes.append(MetaStep(DumbStep()))
        self._nodes.append(MetaStep(DumbStep()))

    def tearDown(self):
        self._s.clear()
        self._nodes = []

    def testCreate(self):
        g = WorkflowDependencyGraph(self._s)
        self.assertTrue(g != None)

    def testGraph1(self):
        g = WorkflowDependencyGraph(self._s)
        c1 = Connection(self._nodes[0], 0, self._nodes[1], 0)
        self._s.addItem(self._nodes[0])
        self._s.addItem(self._nodes[1])
        self._s.addItem(c1)

        self.assertTrue(g.canExecute())
        self.assertEqual(len(g._topologicalOrder), 2)
        self.assertEqual(g._topologicalOrder[0], self._nodes[0])
        self.assertEqual(g._topologicalOrder[1], self._nodes[1])

    def testGraph2(self):
        g = WorkflowDependencyGraph(self._s)
        c1 = Connection(self._nodes[0], 0, self._nodes[1], 0)
        c2 = Connection(self._nodes[1], 0, self._nodes[2], 0)
        self._s.addItem(self._nodes[0])
        self._s.addItem(self._nodes[1])
        self._s.addItem(self._nodes[2])
        self._s.addItem(c1)
        self._s.addItem(c2)

        self.assertTrue(g.canExecute())
        self.assertEqual(len(g._topologicalOrder), 3)
        self.assertEqual(g._topologicalOrder[0], self._nodes[0])
        self.assertEqual(g._topologicalOrder[1], self._nodes[1])
        self.assertEqual(g._topologicalOrder[2], self._nodes[2])

    def testGraph3(self):
        g = WorkflowDependencyGraph(self._s)
        c1 = Connection(self._nodes[0], 0, self._nodes[1], 0)
        c2 = Connection(self._nodes[1], 0, self._nodes[2], 0)
        c3 = Connection(self._nodes[2], 0, self._nodes[3], 0)
        self._s.addItem(self._nodes[0])
        self._s.addItem(self._nodes[1])
        self._s.addItem(self._nodes[2])
        self._s.addItem(self._nodes[3])
        self._s.addItem(c3)
        self._s.addItem(c1)
        self._s.addItem(c2)

        self.assertTrue(g.canExecute())
        self.assertEqual(len(g._topologicalOrder), 4)
        self.assertEqual(g._topologicalOrder[0], self._nodes[0])
        self.assertEqual(g._topologicalOrder[1], self._nodes[1])
        self.assertEqual(g._topologicalOrder[2], self._nodes[2])
        self.assertEqual(g._topologicalOrder[3], self._nodes[3])

    def testGraph4(self):
        g = WorkflowDependencyGraph(self._s)
        c1 = Connection(self._nodes[0], 0, self._nodes[1], 0)
        c2 = Connection(self._nodes[1], 0, self._nodes[2], 0)
        c3 = Connection(self._nodes[2], 0, self._nodes[3], 0)
        self._s.addItem(self._nodes[0])
        self._s.addItem(self._nodes[1])
        self._s.addItem(self._nodes[2])
        self._s.addItem(self._nodes[3])
        self._s.addItem(self._nodes[4])
        self._s.addItem(self._nodes[5])
        self._s.addItem(c3)
        self._s.addItem(c1)
        self._s.addItem(c2)

        nodes = g._findAllConnectedNodes()
        self.assertEqual(4, len(nodes))
        self.assertIn(self._nodes[0], nodes)
        self.assertIn(self._nodes[1], nodes)
        self.assertIn(self._nodes[2], nodes)
        self.assertIn(self._nodes[3], nodes)
        self.assertNotIn(self._nodes[4], nodes)
        self.assertNotIn(self._nodes[5], nodes)

    def testGraph5(self):
        g = WorkflowDependencyGraph(self._s)
        c1 = Connection(self._nodes[0], 0, self._nodes[2], 0)
        c2 = Connection(self._nodes[1], 0, self._nodes[2], 0)
        c3 = Connection(self._nodes[2], 0, self._nodes[3], 0)
        c4 = Connection(self._nodes[2], 0, self._nodes[4], 0)
        self._s.addItem(self._nodes[0])
        self._s.addItem(self._nodes[1])
        self._s.addItem(self._nodes[2])
        self._s.addItem(self._nodes[3])
        self._s.addItem(self._nodes[4])
        self._s.addItem(self._nodes[5])
        self._s.addItem(c1)
        self._s.addItem(c2)
        self._s.addItem(c3)
        self._s.addItem(c4)

        nodes = g._findAllConnectedNodes()
        self.assertEqual(5, len(nodes))
        self.assertIn(self._nodes[0], nodes)
        self.assertIn(self._nodes[1], nodes)
        self.assertIn(self._nodes[2], nodes)
        self.assertIn(self._nodes[3], nodes)
        self.assertIn(self._nodes[4], nodes)
        self.assertNotIn(self._nodes[5], nodes)

        graph = g._calculateDependencyGraph()
        self.assertFalse(g._nodeIsDestination(graph, self._nodes[0]))
        self.assertFalse(g._nodeIsDestination(graph, self._nodes[1]))
        self.assertTrue(g._nodeIsDestination(graph, self._nodes[2]))
        self.assertTrue(g._nodeIsDestination(graph, self._nodes[3]))
        self.assertTrue(g._nodeIsDestination(graph, self._nodes[4]))
        self.assertFalse(g._nodeIsDestination(graph, self._nodes[5]))

        starting_set = g._findStartingSet(graph, nodes)

        self.assertEqual(2, len(starting_set))
        self.assertIn(self._nodes[0], starting_set)
        self.assertIn(self._nodes[1], starting_set)
        self.assertNotIn(self._nodes[2], starting_set)
        self.assertNotIn(self._nodes[3], starting_set)
        self.assertNotIn(self._nodes[4], starting_set)
        self.assertNotIn(self._nodes[5], starting_set)

        order = g._determineTopologicalOrder(graph, starting_set)
        self.assertEqual(5, len(order))
        index0 = order.index(self._nodes[0])
        index1 = order.index(self._nodes[1])
        index2 = order.index(self._nodes[2])
        index3 = order.index(self._nodes[3])
        index4 = order.index(self._nodes[4])
        self.assertLess(index1, index2)
        self.assertLess(index0, index2)
        self.assertLess(index2, index3)
        self.assertLess(index2, index4)

    def testGraph6(self):
        g = WorkflowDependencyGraph(self._s)
        c1 = Connection(self._nodes[0], 0, self._nodes[2], 0)
        c2 = Connection(self._nodes[1], 0, self._nodes[2], 0)
        c3 = Connection(self._nodes[2], 0, self._nodes[3], 0)
        c4 = Connection(self._nodes[2], 0, self._nodes[4], 0)
        c5 = Connection(self._nodes[6], 0, self._nodes[0], 0)
        c6 = Connection(self._nodes[6], 0, self._nodes[1], 0)
        self._s.addItem(self._nodes[0])
        self._s.addItem(self._nodes[1])
        self._s.addItem(self._nodes[2])
        self._s.addItem(self._nodes[3])
        self._s.addItem(self._nodes[4])
        self._s.addItem(self._nodes[5])
        self._s.addItem(self._nodes[6])
        self._s.addItem(c1)
        self._s.addItem(c2)
        self._s.addItem(c3)
        self._s.addItem(c4)
        self._s.addItem(c5)
        self._s.addItem(c6)

        nodes = g._findAllConnectedNodes()
        self.assertEqual(6, len(nodes))
        graph = g._calculateDependencyGraph()
        starting_set = g._findStartingSet(graph, nodes)
        self.assertEqual(1, len(starting_set))
        order = g._determineTopologicalOrder(graph, starting_set)
        self.assertEqual(6, len(order))
        index0 = order.index(self._nodes[6])
        index1 = order.index(self._nodes[1])
        self.assertLess(index0, index1)

    def testGraph7(self):
        '''
        Testing independent graphs
        '''
        g = WorkflowDependencyGraph(self._s)
        c1 = Connection(self._nodes[0], 0, self._nodes[2], 0)
        c2 = Connection(self._nodes[1], 0, self._nodes[2], 0)
        c3 = Connection(self._nodes[2], 0, self._nodes[3], 0)
        c4 = Connection(self._nodes[2], 0, self._nodes[4], 0)
        c5 = Connection(self._nodes[6], 0, self._nodes[5], 0)
        self._s.addItem(self._nodes[0])
        self._s.addItem(self._nodes[1])
        self._s.addItem(self._nodes[2])
        self._s.addItem(self._nodes[3])
        self._s.addItem(self._nodes[4])
        self._s.addItem(self._nodes[5])
        self._s.addItem(self._nodes[6])
        self._s.addItem(c1)
        self._s.addItem(c2)
        self._s.addItem(c3)
        self._s.addItem(c4)
        self._s.addItem(c5)

        nodes = g._findAllConnectedNodes()
        self.assertEqual(7, len(nodes))
        graph = g._calculateDependencyGraph()
        starting_set = g._findStartingSet(graph, nodes)
        self.assertEqual(3, len(starting_set))
        order = g._determineTopologicalOrder(graph, starting_set)
        self.assertEqual(7, len(order))

    def testGraph8(self):
        '''
        Testing graph with loop
        '''
        g = WorkflowDependencyGraph(self._s)
        c1 = Connection(self._nodes[0], 0, self._nodes[1], 0)
        c2 = Connection(self._nodes[1], 0, self._nodes[2], 0)
        c3 = Connection(self._nodes[2], 0, self._nodes[3], 0)
        c4 = Connection(self._nodes[3], 0, self._nodes[4], 0)
        c5 = Connection(self._nodes[3], 0, self._nodes[5], 0)
        c6 = Connection(self._nodes[5], 0, self._nodes[6], 0)
        c7 = Connection(self._nodes[6], 0, self._nodes[2], 0)
        self._s.addItem(self._nodes[0])
        self._s.addItem(self._nodes[1])
        self._s.addItem(self._nodes[2])
        self._s.addItem(self._nodes[3])
        self._s.addItem(self._nodes[4])
        self._s.addItem(self._nodes[5])
        self._s.addItem(self._nodes[6])
        self._s.addItem(c1)
        self._s.addItem(c2)
        self._s.addItem(c3)
        self._s.addItem(c4)
        self._s.addItem(c5)
        self._s.addItem(c6)
        self._s.addItem(c7)

        nodes = g._findAllConnectedNodes()
        self.assertEqual(7, len(nodes))
        graph = g._calculateDependencyGraph()
        starting_set = g._findStartingSet(graph, nodes)
        self.assertEqual(1, len(starting_set))
        order = g._determineTopologicalOrder(graph, starting_set)
        self.assertEqual(0, len(order))

    def testExecute(self):
        g = WorkflowDependencyGraph(self._s)
        c1 = Connection(self._nodes[0], 0, self._nodes[2], 0)
        c2 = Connection(self._nodes[1], 0, self._nodes[2], 1)
        c3 = Connection(self._nodes[1], 1, self._nodes[2], 2)
        c4 = Connection(self._nodes[2], 0, self._nodes[3], 0)
        self._s.addItem(self._nodes[0])
        self._s.addItem(self._nodes[1])
        self._s.addItem(self._nodes[2])
        self._s.addItem(self._nodes[3])
        self._s.addItem(c1)
        self._s.addItem(c2)
        self._s.addItem(c3)
        self._s.addItem(c4)
        g.canExecute()

        for _ in range(len(g._topologicalOrder)):
            g.execute()
class WorkflowManager(object):
    '''
    This class manages (models?) the workflow.
    '''

    def __init__(self):
        '''
        Constructor
        '''
        self.name = 'WorkflowManager'
#        self.widget = None
#        self.widgetIndex = -1
        self._location = ''
        self._workspace_location = None
        self._conf_filename = None
        self._previousLocation = None
        self._saveStateIndex = 0
        self._currentStateIndex = 0

        self._title = None

        self._scene = WorkflowScene(self)

    def title(self):
        self._title = info.APPLICATION_NAME
        if self._location:
            self._title = self._title + ' - ' + self._location
        if self._saveStateIndex != self._currentStateIndex:
            self._title = self._title + ' *'

        return self._title

    # Why set/get? all _prefix are public anyway, just use attributes...
    # if they need to be modified they can be turned into properties
    def setLocation(self, location):
        self._location = location

    def location(self):
        return self._location

    def setPreviousLocation(self, location):
        self._previousLocation = location

    def previousLocation(self):
        return self._previousLocation

    def scene(self):
        return self._scene

    def undoStackIndexChanged(self, index):
        self._currentStateIndex = index

    def identifierOccursCount(self, identifier):
        return self._scene.identifierOccursCount(identifier)

    def execute(self):
        self._scene.execute()

    def isModified(self):
        return self._saveStateIndex != self._currentStateIndex

    def new(self, location):
        '''
        Create a new workflow at the given location.  The location is a directory, it must exist
        it will not be created.  A file '.workflow.conf' is created in the directory at 'location' which holds
        information relating to the workflow.
        '''
        if location is None:
            raise WorkflowError('No location given to create new Workflow.')

        if not os.path.exists(location):
            raise WorkflowError('Location %s does not exist.' % location)

        self._location = location
        wf = _getWorkflowConfiguration(location)
        wf.setValue('version', info.VERSION_STRING)
#        self._title = info.APPLICATION_NAME + ' - ' + location
        self._scene.clear()

    def exists(self, location):
        '''
        Determines whether a workflow exists in the given location.
        Returns True if a valid workflow exists, False otherwise.
        '''
        if location is None:
            return False

        if not os.path.exists(location):
            return False

        wf = _getWorkflowConfiguration(location)
        if wf.contains('version'):
            return True

        return False

    def load(self, location):
        '''
        Open a workflow from the given location.
        :param location:
        '''
        if location is None:
            raise WorkflowError('No location given to open Workflow.')

        if not os.path.exists(location):
            raise WorkflowError('Location %s does not exist' % location)

        wf = _getWorkflowConfiguration(location)
        if not wf.contains('version'):
            raise WorkflowError('The given Workflow configuration file is not valid.')

        workflow_version = versionTuple(wf.value('version'))
        software_version = versionTuple(info.VERSION_STRING)
        if not workflow_version[0:2] == software_version[0:2]:
            # compare first two elements of version (major, minor)
            raise WorkflowError(
                'Major/Minor version number mismatch - '
                'application version: %s; workflow version: %s.' %
                    (info.VERSION_STRING, wf.value('version'))
            )
        if not workflow_version[2] <= software_version[2]:
            raise WorkflowError(
                'Patch version number of the workflow cannot be newer than '
                'application - '
                'application version: %s; workflow version: %s' %
                    (info.VERSION_STRING, wf.value('version'))
            )

        self._location = location
#        wf = _getWorkflowConfiguration()
        self._scene.loadState(wf)
        self._saveStateIndex = self._currentStateIndex = 0
#        self._title = info.APPLICATION_NAME + ' - ' + location

    def save(self):
        wf = _getWorkflowConfiguration(self._location)
        self._scene.saveState(wf)
        self._saveStateIndex = self._currentStateIndex
        af = _getWorkflowMetaAbsoluteFilename(self._location)
        f = open(af, 'w')
        f.write(serializeWorkflowAnnotation())
        self._scene.saveAnnotation(f)
        f.close()

#        self._title = info.APPLICATION_NAME + ' - ' + self._location

    def close(self):
        '''
        Close the current workflow
        '''
        self._location = ''
        self._saveStateIndex = self._currentStateIndex = 0
#        self._title = info.APPLICATION_NAME

    def isWorkflowOpen(self):
        return True  # not self._location == None

    def writeSettings(self, settings):
        settings.beginGroup(self.name)
        settings.setValue(_PREVIOUS_LOCATION_STRING, self._previousLocation)
        settings.endGroup()

    def readSettings(self, settings):
        settings.beginGroup(self.name)
        self._previousLocation = settings.value(_PREVIOUS_LOCATION_STRING, '')
        settings.endGroup()