Example #1
0
class WProbes(QWidget, Logger.ClassLogger):
    """
    Widget for probes
    """
    def __init__(self, parent):
        """
        Constructs WProbes widget

        @param parent: 
        @type parent:
        """
        QWidget.__init__(self, parent)
        self.parent = parent
        self.name = self.tr("Probes")
        self.itemCurrentRunning = None
        self.itemCurrentInstalled = None
        self.itemCurrentDefault = None
        self.probes = {}

        self.nbPrbs = 0
        # self.prbsInstalled = None

        self.createWidgets()
        self.createConnections()
        self.createActions()
        self.createToolbar()
        self.deactivate()

    def createWidgets (self):
        """
        QtWidgets creation

        QTreeWidget (Name, Running on address, Type, Sched at, Version, Description)
         _______________
        |               |
        |---------------|
        |               |
        |               |
        |_______________|
        """
        layout = QHBoxLayout()

        self.deployBox = QGroupBox("Default probes")
        self.probesAvailable = QTreeWidget(self)
        self.probesAvailable.setIndentation(10)
        self.labelsAvail = [ self.tr("Installed") ]
        self.probesAvailable.setHeaderLabels(self.labelsAvail)

        self.runningBox = QGroupBox("Running")
        self.probesRegistered = QTreeWidget(self)
        self.probesRegistered.setIndentation(10)
        self.runningDockToolbar = QToolBar(self)
        self.runningDockToolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.availDockToolbar = QToolBar(self)
        self.availDockToolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        self.labels = [ self.tr("Name"), self.tr("Running on address"), self.tr("Started at"), 
                        self.tr("Type"), self.tr("Auto Startup"), self.tr("Description") ]        
        self.probesRegistered.setHeaderLabels(self.labels)
        self.probesRegistered.setColumnWidth(0, 180)
        self.probesRegistered.setColumnWidth(1, 120)
        self.probesRegistered.setColumnWidth(2, 150)
        self.probesRegistered.setColumnWidth(3, 70)
        self.probesRegistered.setContextMenuPolicy(Qt.CustomContextMenu)

        self.probesDefault = QTreeWidget(self)
        self.probesDefault.setIndentation(10)
        self.labelsDefault = [ self.tr("Enabled"), self.tr("Name"), self.tr("Type"), self.tr("Description") ]
        self.probesDefault.setHeaderLabels(self.labelsDefault)
        self.probesDefault.setContextMenuPolicy(Qt.CustomContextMenu)
        self.probesDefault.setColumnWidth(1, 180)


        layoutRunning = QVBoxLayout()
        layoutRunning.addWidget(self.runningDockToolbar)
        layoutRunning.addWidget(self.probesRegistered)
        self.runningBox.setLayout(layoutRunning)

        self.probeNameEdit = QLineEdit('')
        self.probeNameEdit.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed )

        self.probeDescEdit = QLineEdit('')
        self.probeDescEdit.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed )

        self.probeTypeEdit = QLineEdit('')
        self.probeTypeEdit.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Fixed )
        self.probeTypeEdit.setEnabled(False)
        
        self.probeDescrInstalledEdit = QTextEdit('')
        self.probeDescrInstalledEdit.setEnabled(False)
        
        self.checkAutoStartOption = QCheckBox()
        self.checkStartNowOption = QCheckBox()

        paramLayout = QGridLayout()
        paramLayout.addWidget(QLabel("Type:"), 0, 0, Qt.AlignRight)
        paramLayout.addWidget(self.probeTypeEdit, 0, 1)
        paramLayout.addWidget(QLabel("Name:"), 1, 0, Qt.AlignRight)
        paramLayout.addWidget(self.probeNameEdit, 1, 1)
        paramLayout.addWidget(QLabel("Description:"), 2, 0, Qt.AlignRight)
        paramLayout.addWidget(self.probeDescEdit, 2, 1)
        paramLayout.addWidget(QLabel("Startup on boot:"), 3, 0, Qt.AlignRight)
        paramLayout.addWidget(self.checkAutoStartOption, 3, 1)
        paramLayout.addWidget(QLabel("Start now:"), 4, 0, Qt.AlignRight)
        paramLayout.addWidget(self.checkStartNowOption, 4, 1)
      
        layoutLeft = QVBoxLayout()

        layoutAvail = QHBoxLayout()
        layoutAvail.addWidget(self.probesAvailable)
        layoutAvail.addWidget(self.probeDescrInstalledEdit)
        
        layoutLeft.addLayout(layoutAvail)
        layoutLeft.addWidget(self.runningBox)

        layoutDeploy = QVBoxLayout()
        layoutDeploy.addWidget(self.availDockToolbar)
        layoutDeploy.addLayout(paramLayout)
        layoutDeploy.addWidget(self.probesDefault)
        self.deployBox.setLayout(layoutDeploy)

        layoutRight = QVBoxLayout()
        layoutRight.addWidget(self.deployBox)   

        layout.addLayout(layoutLeft)
        layout.addLayout(layoutRight)
        self.setLayout(layout)

    def createConnections (self):
        """
        Create Qt Connections
        """
        self.probesRegistered.customContextMenuRequested.connect(self.onPopupMenu)
        self.probesRegistered.currentItemChanged.connect(self.currentItemChanged)
        self.probesRegistered.itemClicked.connect(self.itemClicked)

        self.probesAvailable.currentItemChanged.connect(self.currentItemChanged)

        self.probesDefault.currentItemChanged.connect(self.currentItemChanged)
        self.probesDefault.customContextMenuRequested.connect(self.onPopupMenuDefault)

    def createActions (self):
        """
        Actions defined:
         * stop probe
         * start probe
         * delete one probe
         * clear fields
         * refresh running probes
         * refresh default probes
        """
        self.stopAction = QtHelper.createAction(self, "&Stop", self.stopProbe, tip = 'Stop probe', 
                                        icon = QIcon(":/act-stop.png"))
        self.startAction = QtHelper.createAction(self, "&Add / Start", self.startProbe, tip = 'Add default probe', 
                                        icon = QIcon(":/probe-add.png"))
        self.delProbeAction = QtHelper.createAction(self, "&Delete", self.delProbe, tip = 'Delete default probe', 
                                        icon = QIcon(":/probe-del.png"))
        self.cancelAction = QtHelper.createAction(self, "&Clear", self.resetProbe, tip = 'Clear fields', 
                                        icon = QIcon(":/clear.png") )
        self.refreshRunningAction = QtHelper.createAction(self, "&Refresh", 
                                        self.refreshRunningProbe, tip = 'Refresh running probes', 
                                        icon = QIcon(":/act-refresh.png") )
        self.refreshDefaultAction = QtHelper.createAction(self, "&Refresh", 
                                        self.refreshDefaultProbe, tip = 'Refresh default probes', 
                                        icon = QIcon(":/act-refresh.png") )

    def createToolbar(self):
        """
        Toolbar creation
            
        ||-------||
        || Empty ||
        ||-------||
        """
        self.runningDockToolbar.setObjectName("Registered Probe toolbar")
        self.runningDockToolbar.addAction(self.refreshRunningAction)
        self.runningDockToolbar.addSeparator()
        self.runningDockToolbar.addAction(self.stopAction)
        self.runningDockToolbar.addSeparator()
        self.runningDockToolbar.setIconSize(QSize(16, 16))

        self.availDockToolbar.setObjectName("Installed Probe toolbar")
        self.availDockToolbar.addAction(self.refreshDefaultAction)
        self.availDockToolbar.addSeparator()
        self.availDockToolbar.addAction(self.startAction)
        self.availDockToolbar.addAction(self.delProbeAction)
        self.availDockToolbar.addSeparator()
        self.availDockToolbar.addAction(self.cancelAction)
        self.availDockToolbar.addSeparator()
        self.availDockToolbar.setIconSize(QSize(16, 16))

    def itemClicked(self, currentItem):
        """
        On item clicked

        @param currentItem: 
        @type currentItem:  
        """
        if currentItem is not None:
            if isinstance( currentItem, ProbeItem ):
                self.itemCurrentRunning = currentItem
                self.stopAction.setEnabled(True)

    def currentItemChanged(self, currentItem, previousItem):
        """
        On current item changed

        @param currentItem: 
        @type currentItem:  

        @param previousItem: 
        @type previousItem: 
        """
        if currentItem is not None:
            if isinstance( currentItem, ProbeInstalledItem ):
                self.itemCurrentInstalled = currentItem
                if 'description' in currentItem.dataProbe:
                    self.probeDescrInstalledEdit.setText( currentItem.dataProbe['description'] )
                    self.probeTypeEdit.setText( currentItem.dataProbe['type'] )
                else:
                    self.probeDescrInstalledEdit.setText( '' )
            elif isinstance( currentItem, ProbeItem ):
                self.itemCurrentRunning = currentItem
                self.stopAction.setEnabled(True)
            elif isinstance( currentItem, ProbeDefaultItem ):
                self.itemCurrentDefault = currentItem
                self.delProbeAction.setEnabled(True)
            else:
                self.stopAction.setEnabled(False)
                self.delProbeAction.setEnabled(False)
                self.probeDescrInstalledEdit.setText( '' )

    def onPopupMenu(self, pos):
        """
        Display menu on right click

        @param pos: 
        @type pos:
        """
        self.menu = QMenu()
        item = self.probesRegistered.itemAt(pos)
        if item:
            self.itemCurrentRunning = item
            self.menu.addAction( "Stop...", self.stopProbe)
            self.menu.popup(self.probesRegistered.mapToGlobal(pos))

    def onPopupMenuDefault(self, pos):
        """
        Display menu on right click

        @param pos: 
        @type pos:
        """
        self.menu = QMenu()
        item = self.probesDefault.itemAt(pos)
        if item:
            self.itemCurrentDefault = item
            self.menu.addAction( "Delete...", self.delProbe)
            self.menu.popup(self.probesDefault.mapToGlobal(pos))


    def refreshDefaultProbe(self):
        """
        Refresh the default list of probes
        """
        RCI.instance().defaultProbes()
        
    def refreshRunningProbe(self):
        """
        Refresh the running list of probes
        """
        RCI.instance().runningProbes()
        
    def delProbe(self):
        """
        Delete probe
        """
        if self.itemCurrentDefault is not None:
            reply = QMessageBox.question(self, "Stop probe", "Are you sure ?",
                QMessageBox.Yes | QMessageBox.No )
            if reply == QMessageBox.Yes:
                probeName = self.itemCurrentDefault.dataProbe['name']
                self.delProbeAction.setEnabled(False)
                
                # rest call
                RCI.instance().removeProbe(probeName=probeName)
                
    def stopProbe(self):
        """
        Stop the selected probe
        """
        if self.itemCurrentRunning is not None:
            reply = QMessageBox.question(self, "Stop probe", "Are you sure ?",
                QMessageBox.Yes | QMessageBox.No )
            if reply == QMessageBox.Yes:
                probeName = self.itemCurrentRunning.dataProbe['id']
                self.itemCurrentRunning = None
                self.stopAction.setEnabled(False)
                
                # rest call
                RCI.instance().disconnectProbe(probeName=probeName)
                
    def startProbe(self):
        """
        Start a new probe
        """
        # some checks before
        if self.probeTypeEdit.text() == '':
            QMessageBox.information(self, "Add Default Probe" , "Please select the probe type.")
            return
        if self.probeNameEdit.text() == '':
            QMessageBox.information(self, "Add Default Probe" , "Probe name is mandatory.")
            return
        if not self.checkAutoStartOption.isChecked() and not self.checkStartNowOption.isChecked():
            QMessageBox.information(self, "Add Default Probe" , "Select startup option.")
            return
            
        # call web services
        probeType = str( self.probeTypeEdit.text() )
        probeName = str( self.probeNameEdit.text() )
        probeDescription =  str( self.probeDescEdit.text() )
        probeAutoStart = self.checkAutoStartOption.isChecked()

        if not self.checkStartNowOption.isChecked():
            RCI.instance().addProbe(probeName=probeName, probeType=probeType, 
                                    probeDescription=probeDescription)
        else:
            RCI.instance().connectProbe(probeName=probeName, probeType=probeType, 
                                        probeDescription=probeDescription, 
                                        probeBoot=probeAutoStart)
                                        
    def resetProbe(self):
        """
        Clear probe field
        """
        self.probeDescrInstalledEdit.setText( '' )
        self.probeDescEdit.setText( '' )
        self.probeNameEdit.setText( '' )
        self.probeTypeEdit.setText( '' )
        
        # clear selection
        itms = self.probesAvailable.selectedItems()
        for i in itms:
            if i.isSelected():
                i.setSelected(False)
        self.itemCurrentInstalled = None

    def active (self):
        """
        Enables QTreeWidget
        """
        self.probesRegistered.setEnabled(True)
        self.probesAvailable.setEnabled(True)
        self.deployBox.setEnabled(True)
        self.runningBox.setEnabled(True)

        self.refreshRunningAction.setEnabled(True)

    def deactivate (self):
        """
        Clears QTreeWidget and disables it
        """
        self.checkAutoStartOption.setCheckState(Qt.Unchecked) 
        self.checkStartNowOption.setCheckState(Qt.Unchecked) 

        self.probesAvailable.clear()
        self.probeDescrInstalledEdit.setText('')
        
        self.probesRegistered.clear()
        self.probesDefault.clear()
        self.probes = {}
        
        self.probesRegistered.setEnabled(False)
        self.probesAvailable.setEnabled(False)
        self.deployBox.setEnabled(False)
        self.runningBox.setEnabled(False)

        # actions
        self.stopAction.setEnabled(False)
        self.delProbeAction.setEnabled(False)
        
        self.itemCurrentRunning = None
        self.itemCurrentInstalled = None

        self.probeDescEdit.setText( '' )
        self.probeTypeEdit.setText( '' )
        self.probeNameEdit.setText( '' )

        self.resetNbProbes()

        self.refreshRunningAction.setEnabled(False)

    def getRunningProbes(self):
        """
        Get running probes 
        """
        if sys.version_info > (3,): # python3 support
            return list(self.probes.keys())
        else:
            return self.probes.keys()

    def getProbeTypeByName(self, name):
        """
        Get probe type by name

        @param name: 
        @type name:
        """
        if name in self.probes:
            return self.probes[name].getProbeType()
        else:
            return ''

    def loadDefault (self, data):
        """
        Loads default probes

        @param data: 
        @type data: dict
        """
        self.probesDefault.clear()

        for defProbe in data:
            defProbeItem = ProbeDefaultItem( probe = defProbe, parent= self.probesDefault)

    def loadData (self, data, dataInstalled=None):
        """
        Loads probes

        @param data: 
        @type data: dict

        @param dataInstalled: 
        @type dataInstalled:
        """
        if isinstance(data, dict):
            data = [ data ]

        self.probesRegistered.clear()

        for probe in data:
            probeItem = ProbeItem( probe = probe, parent= self.probesRegistered)
            self.probes[probe['id']] = probeItem

        # load tests stats
        if dataInstalled is not None:
            if len(dataInstalled) == 0:
                self.deployBox.setEnabled(False)
                self.probesAvailable.setEnabled(False)

    def resetNbProbes(self, data=None):
        """
        Reset the number of probes
        """
        pass

    def refreshData (self, data, action):
        """
        Refresh probes

        @param data: 
        @type data: dict

        @param action: expected values in 'del' and 'add'
        @type action: string
        """
        if action == 'del':
            self.probesRegistered.clear()
            self.probes = {}
            self.resetNbProbes(data=data)
            self.loadData( data = data )
        elif action == 'add':
            self.resetNbProbes(data=data)
            self.loadData( data = data )        
        else:
            self.error( 'action unknown: %s' % str(action) )
    
    def refreshDataDefault(self, data, action):
        """
        Refresh probes

        @param data: 
        @type data: dict

        @param action: expected values in 'del' and 'add'
        @type action: string
        """
        if action == 'del':
            self.probesDefault.clear()
            self.loadDefault( data = data )
        elif action == 'add':
            self.probesDefault.clear()
            self.loadDefault( data = data )     
        else:
            self.error( 'action unknown: %s' % str(action) )
Example #2
0
class WTestManager(QWidget, Logger.ClassLogger):
    """
    Widget to display all tests in the scheduler
    """
    def __init__(self, parent):
        """
        Constructs WTestManager widget

        @param parent: 
        @type parent:
        """
        QWidget.__init__(self, parent)
        self.parent = parent
        self.name = self.tr("Task Manager")
        self.ascendingOrder = False
        self.ascendingOrderHistory = False
        self.ascendingOrderWaiting = False
        
        self.itemCurrent = None
        self.projects = []
        self.createWidgets()
        self.createActions()
        self.createConnections()
        self.createActions()
        self.createToolbar()
        self.deactivate()

    def createWidgets (self):
        """
        QtWidgets creation
        
        QTreeWidget (Id, Name, Init at, Sched at, Start at, Stop at, User, Duration)
         _______________
        |    QToolBar   |
        |---------------|
         _______________
        |               |
        |---------------|
        |               |
        |               |
        |_______________|
        """

        #self.mainTab = QTabWidget()


        layout = QVBoxLayout()

        topLayout = QHBoxLayout()

        # waiting tree widget
        self.tabWaiting = QTabWidget()

        #self.waitingBox = QGroupBox("Waiting")
        self.waitingBox = QFrame(self)
        self.testWaiting = QTreeWidget(self)
        self.testWaiting.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.waitingToolbar = QToolBar(self)
        self.labelsWaiting = [ self.tr("No.") , self.tr("Group"), self.tr("Schedulation Type"),
                              self.tr("Project"), self.tr("Name") , self.tr("Next run"), 
                              self.tr("Repeat"), self.tr("Probes"), self.tr("Notifications") , 
                              self.tr("Tests result"), self.tr("Author")  ]
        self.testWaiting.setHeaderLabels(self.labelsWaiting)
        self.testWaiting.setIndentation(10)
        self.testWaiting.setContextMenuPolicy(Qt.CustomContextMenu)
        layoutWaiting = QVBoxLayout()
        layoutWaiting.addWidget(self.waitingToolbar)
        layoutWaiting.addWidget(self.testWaiting)
        self.waitingBox.setLayout(layoutWaiting)

        self.tabWaiting.addTab( self.waitingBox, "Scheduled" )

        self.enqueuedBox = QFrame(self)
        layoutEnqueued = QVBoxLayout()

        self.testEnqueued = QTreeWidget(self)
        self.labelsEnqueued = [ self.tr("Group of tests")  ] 
        self.testEnqueued.setHeaderLabels(self.labelsEnqueued)
        self.testEnqueued.setIndentation(10)

        layoutEnqueued.addWidget(self.testEnqueued)
        self.enqueuedBox.setLayout(layoutEnqueued)
        self.tabWaiting.addTab( self.enqueuedBox, "Waiting" )


        # current tree widget
        self.currentBox = QGroupBox("Running")
        self.testManager = QTreeWidget(self)
        self.testManager.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.dockToolbar = QToolBar(self)
        self.labels  = [ self.tr("No."), self.tr("Project"), self.tr("Name"), self.tr("Started at"),
                         self.tr("Author"), self.tr("Recursive") ]
        self.testManager.setHeaderLabels(self.labels)
        self.testManager.setIndentation(10)
        self.testManager.setContextMenuPolicy(Qt.CustomContextMenu)
        layoutCurrent = QVBoxLayout()
        layoutCurrent.addWidget(self.dockToolbar)
        layoutCurrent.addWidget(self.testManager)
        self.currentBox.setLayout(layoutCurrent)
        
        v_splitter = QSplitter(self)      
        v_splitter.addWidget( self.tabWaiting )
        v_splitter.addWidget( self.currentBox ) 
        topLayout.addWidget(v_splitter)

        # history tree widget
        self.historyBox = QGroupBox("History")
        self.testHistory = QTreeWidget(self)
        self.historyToolbar = QToolBar(self)
        self.labels2 = [ self.tr("Id"),  self.tr("Schedulation Type"),  self.tr("Project") , self.tr("Name"), 
                         self.tr("Sched at"), self.tr("Run start"), self.tr("Run end"), self.tr("Author"), 
                         self.tr("Duration (in sec.)"), self.tr("Run Result") ]
        self.testHistory.setHeaderLabels(self.labels2)
        
        self.statsBox = QGroupBox("Summary")
        self.nbRunningLabel = QLabel("0")
        self.nbWaitingLabel = QLabel("0")
        self.nbHistoryLabel = QLabel("0")
        self.nbCompleteHistoryLabel = QLabel("0")
        self.nbErrorHistoryLabel = QLabel("0")
        self.nbKilledHistoryLabel = QLabel("0")
        self.nbCancelledHistoryLabel = QLabel("0")
        layout2 = QFormLayout()
        layout2.addRow(QLabel("Running"), self.nbRunningLabel )
        layout2.addRow(QLabel("Waiting"), self.nbWaitingLabel )
        layout2.addRow( QLabel(""), QLabel("") )
        layout2.addRow(QLabel("History"), self.nbHistoryLabel )
        layout2.addRow(QLabel(" - COMPLETE"), self.nbCompleteHistoryLabel )
        layout2.addRow(QLabel(" - ERROR"), self.nbErrorHistoryLabel )
        layout2.addRow(QLabel(" - KILLED"), self.nbKilledHistoryLabel )
        layout2.addRow(QLabel(" - CANCELLED"), self.nbCancelledHistoryLabel )
        self.statsBox.setLayout(layout2)

        layoutHistory = QVBoxLayout()
        layoutHistory.addWidget(self.historyToolbar)
        layoutHistory.addWidget(self.testHistory)
        self.historyBox.setLayout(layoutHistory)
        
        subLayout = QHBoxLayout()
        subLayout.addWidget(self.historyBox)
        subLayout.addWidget(self.statsBox)

        frame_left = QFrame(self)
        frame_left.setFrameShape(QFrame.NoFrame)
        frame_left.setLayout(topLayout)

        frame_right = QFrame(self)
        frame_right.setFrameShape(QFrame.NoFrame)
        frame_right.setLayout(subLayout)

        topLayout.setContentsMargins(0,0,0,0)
        subLayout.setContentsMargins(0,0,0,0)

        splitter1 = QSplitter(Qt.Vertical)
        splitter1.addWidget(frame_left)
        splitter1.addWidget(frame_right)

        layout.addWidget(splitter1)
        self.setLayout(layout)

    def createConnections(self):
        """
        Create Qt Connections
        """
        self.testWaiting.customContextMenuRequested.connect(self.onPopupMenuWaiting)
        self.testWaiting.currentItemChanged.connect(self.onCurrentWaitingItemChanged)

        self.testManager.customContextMenuRequested.connect(self.onPopupMenu)
        self.testManager.currentItemChanged.connect(self.onCurrentItemChanged)

        self.testWaiting.itemDoubleClicked.connect(self.onItemDoubleClicked)

    def createActions (self):
        """
        Actions defined:
         * sort running task
         * sort waiting task
         * sort history task
         * kill one task
         * kill all tasks
         * cancel one task
         * cancel all task
         * clear the history
         * edit a waiting task
         * refresh waiting tasks
         * refresh running tasks
         * partial refresh of the history
         * complete refresh of the history
         * disable a waiting task
         * enable a waiting task
        """
        self.sortAction = QtHelper.createAction(self, "Ascending Order", self.toggleSort, toggled = True, 
                                icon = QIcon(":/ascending.png") )
        self.sortHistoryAction = QtHelper.createAction(self, "Ascending Order", self.toggleSortHistory, toggled = True, 
                                icon = QIcon(":/ascending.png") )
        self.sortWaitingAction = QtHelper.createAction(self, "Ascending Order", self.toggleSortWaiting, toggled = True, 
                                icon = QIcon(":/ascending.png") )
        self.killAction = QtHelper.createAction(self, "&Kill", self.killTask, tip = 'Kill selected test', 
                                icon = QIcon(":/process-kill.png") )
        self.killAllAction = QtHelper.createAction(self, "&Kill All", self.killAllTasks, tip = 'Kill all running tests', 
                                icon = QIcon(":/process-killall.png") )
        self.cancelAction = QtHelper.createAction(self, "&Cancel", self.cancelTask, tip = 'Cancel selected test', 
                                icon = QIcon(":/process-cancel.png") )
        self.cancelAllAction = QtHelper.createAction(self, "&Cancel All", self.cancelAllTasks, tip = 'Cancel all waiting tests', 
                                icon = QIcon(":/processes-cancelall.png") )
        
        self.clearHistoryAction = QtHelper.createAction(self, "&Clear history", self.clearHistory, tip = 'Clear history', 
                                icon = QIcon(":/trash.png"))
        self.editWaitingAction = QtHelper.createAction(self, "&Edit", self.editWaiting, tip = 'Edit the selected task', 
                                icon = QIcon(":/reschedule.png"))

        self.refreshWaitingAction = QtHelper.createAction(self, "Waiting tasks", self.refreshWaitingList, 
                                icon = QIcon(":/act-refresh.png"), tip="Refresh waiting tasks" )
        self.refreshRunningAction = QtHelper.createAction(self, "Running tasks", self.refreshRunningList, 
                                icon = QIcon(":/act-refresh.png"), tip="Refresh running tasks" )
        self.partialRefreshHistoryAction = QtHelper.createAction(self, "Partial history", self.partialRefreshHistoryList, 
                                icon = QIcon(":/act-half-refresh.png"), tip="Refresh partial history" )
        self.refreshHistoryAction = QtHelper.createAction(self, "Full history", self.refreshHistoryList, 
                                icon = QIcon(":/act-refresh.png"), tip="Refresh full history" )

        self.disableAction = QtHelper.createAction(self, "&Disable", self.disableTask, tip = 'Disable task', 
                                icon = QIcon(":/process-pause-icon.png") )
        self.enableAction = QtHelper.createAction(self, "&Enable", self.enableTask, tip = 'Enable task', 
                                icon = None)

    def createToolbar(self):
        """
        Toolbar creation
            
        ||----------||
        || Kill all ||
        ||----------||
        """
        self.dockToolbar.setObjectName("Test Manager toolbar")
        self.dockToolbar.addAction(self.sortAction)
        self.dockToolbar.addAction(self.refreshRunningAction)
        self.dockToolbar.addSeparator()
        self.dockToolbar.addAction(self.killAction)
        self.dockToolbar.addAction(self.killAllAction)
        self.dockToolbar.addSeparator()
        self.dockToolbar.setIconSize(QSize(16, 16))
        #
        self.historyToolbar.setObjectName("Test Manager History toolbar")
        self.historyToolbar.addAction(self.sortHistoryAction)
        self.historyToolbar.addAction(self.partialRefreshHistoryAction)
        self.historyToolbar.addAction(self.refreshHistoryAction)
        self.historyToolbar.addSeparator()
        self.historyToolbar.addAction(self.clearHistoryAction)
        self.historyToolbar.setIconSize(QSize(16, 16))
        #
        self.waitingToolbar.setObjectName("Test Manager Waiting toolbar")
        self.waitingToolbar.addAction(self.sortWaitingAction)
        self.waitingToolbar.addAction(self.refreshWaitingAction)
        self.waitingToolbar.addSeparator()
        self.waitingToolbar.addAction(self.editWaitingAction)
        self.waitingToolbar.addAction(self.disableAction)
        self.waitingToolbar.addAction(self.cancelAction)
        self.waitingToolbar.addAction(self.cancelAllAction)
        self.waitingToolbar.addSeparator()
        self.waitingToolbar.setIconSize(QSize(16, 16))

    def onItemDoubleClicked(self, item):
        """
        On item double clicked in waiting task
        """
        if item.taskEventType in [ UCI.SCHED_QUEUE_AT, UCI.SCHED_QUEUE ]:
            pass
        else:
            self.editWaiting()
        
    def getProjectName(self, prjId):
        """
        Return the project name
        """
        pname = "UNKNOWN"
        
        if prjId == 0:
            return "UNDEFINED"

        for p in self.projects:
            if int(p['project_id']) == int(prjId):
                pname = p['name']
                break

        return pname

    def refreshWaitingList(self):
        """
        Refresh the waiting task list
        """
        RCI.instance().waitingTasks()
        
    def refreshRunningList(self):
        """
        Refresh the running task list
        """
        RCI.instance().runningTasks()
        
    def partialRefreshHistoryList(self):
        """
        Partial refresh of the history task list
        """
        RCI.instance().historyTasks()
        
    def refreshHistoryList(self):
        """
        Refresh the history task list
        """
        RCI.instance().historyTasksAll()
        
    def onCurrentItemChanged(self, witem1, witem2):
        """
        On current item changed 

        @param witem1: 
        @type witem1:

        @param witem2: 
        @type witem2:
        """
        # kill task available just for admin and tester
        if UCI.RIGHTS_ADMIN in RCI.instance().userRights  or UCI.RIGHTS_TESTER in RCI.instance().userRights :
            if witem1 is not None:
                if witem1.taskState == STATE_RUNNING :
                    self.itemCurrent = witem1
                    self.killAction.setEnabled(True)
                else:
                    self.killAction.setEnabled(False)

    def onCurrentWaitingItemChanged(self, witem1, witem2):
        """
        On current waiting task item changed

        @param witem1: 
        @type witem1:

        @param witem2: 
        @type witem2:
        """
        # kill task available just for admin and tester
        if UCI.RIGHTS_ADMIN in RCI.instance().userRights or UCI.RIGHTS_TESTER in RCI.instance().userRights :
            if witem1 is not None:
                self.itemCurrent = witem1
                self.cancelAction.setEnabled(True)
                self.editWaitingAction.setEnabled(True)
                self.disableAction.setEnabled(True)
            else:
                self.cancelAction.setEnabled(False)
                self.editWaitingAction.setEnabled(False)
                self.disableAction.setEnabled(False)

    def disableTask(self):
        """
        Disable a waiting task
        """
        # if self.itemCurrent is not None:
        for currentItem in self.testWaiting.selectedItems():
            RCI.instance().rescheduleTask(taskId=currentItem.taskId, taskEnabled=False, 
                                            scheduleType=currentItem.taskEventType,
                                            scheduleAt=currentItem.taskEventArgs, 
                                            scheduleRepeat=currentItem.taskEventNb, 
                                            probesEnabled=currentItem.taskWithoutProbes, 
                                            notificationsEnabled=currentItem.taskWithoutNotif, 
                                            debugEnabled=False, logsEnabled=currentItem.taskNoKeepTr, 
                                            fromTime=currentItem.taskEventFrom, toTime=currentItem.taskEventTo )
                                            
    def enableTask(self):
        """
        Enable a waiting task
        """
        # if self.itemCurrent is not None:
        for currentItem in self.testWaiting.selectedItems():
            RCI.instance().rescheduleTask(taskId=currentItem.taskId, taskEnabled=True, 
                                            scheduleType=currentItem.taskEventType,
                                            scheduleAt=currentItem.taskEventArgs, 
                                            scheduleRepeat=currentItem.taskEventNb, 
                                            probesEnabled=currentItem.taskWithoutProbes, 
                                            notificationsEnabled=currentItem.taskWithoutNotif, 
                                            debugEnabled=False, logsEnabled=currentItem.taskNoKeepTr, 
                                            fromTime=currentItem.taskEventFrom, toTime=currentItem.taskEventTo )
    
    def editWaiting(self):
        """
        Edit a waiting task
        """
        if self.itemCurrent is not None:
            if self.itemCurrent.taskEventType in [ UCI.SCHED_QUEUE_AT, UCI.SCHED_QUEUE ]:
                pass
            else:
                dSched = ScheduleDialog.SchedDialog( self )
                dSched.fillFields( schedType=self.itemCurrent.taskEventType, schedArgs=self.itemCurrent.taskEventArgs,
                                    taskName=self.itemCurrent.taskEventName, taskId=self.itemCurrent.taskId, 
                                    schedNb=self.itemCurrent.taskEventNb, withoutProbes=self.itemCurrent.taskWithoutProbes,
                                    enabled=self.itemCurrent.taskEventEnabled,
                                    withoutNotifs=self.itemCurrent.taskWithoutNotif, noKeepTr=self.itemCurrent.taskNoKeepTr,
                                    schedFrom=self.itemCurrent.taskEventFrom, schedTo=self.itemCurrent.taskEventTo )
                if dSched.exec_() == QDialog.Accepted:
                    runAt, runType, runNb, withoutProbes, runEnabled, noTr, withoutNotifs, runFrom, runTo = dSched.getSchedtime()

                    RCI.instance().rescheduleTask(taskId=self.itemCurrent.taskId, taskEnabled=runEnabled, scheduleType=runType,
                                                    scheduleAt=runAt, scheduleRepeat=runNb, 
                                                    probesEnabled=withoutProbes, notificationsEnabled=withoutNotifs, 
                                                    debugEnabled=False, logsEnabled=noTr, 
                                                    fromTime=runFrom, toTime=runTo )
    def clearHistory(self):
        """
        Call the server to clear the history
        """
        reply = QMessageBox.question(self, "Clear tasks history", "Are you sure ?",
                        QMessageBox.Yes | QMessageBox.No )
        if reply == QMessageBox.Yes:
            RCI.instance().clearHistory()
            
    def testKilled(self):
        """
        Test kiled
        """
        self.itemCurrent = None
        self.killAction.setEnabled(False)

    def testCancelled(self):
        """
        Test cancelled
        """
        self.itemCurrent = None
        self.cancelAction.setEnabled(False)

    def active (self):
        """
        Enables QTreeWidget 
        """
        self.waitingBox.setEnabled(True)
        self.currentBox.setEnabled(True)
        self.historyBox.setEnabled(True)
        self.statsBox.setEnabled(True)
        #
        self.testManager.setEnabled(True)
        self.testHistory.setEnabled(True)
        self.testWaiting.setEnabled(True)
        #
        self.sortAction.setEnabled(True)
        self.sortHistoryAction.setEnabled(True)
        self.sortWaitingAction.setEnabled(True)
        
        self.refreshWaitingAction.setEnabled(True)
        self.refreshRunningAction.setEnabled(True)
        self.refreshHistoryAction.setEnabled(True)
        self.partialRefreshHistoryAction.setEnabled(True)

        self.killAction.setEnabled(False)
        self.cancelAction.setEnabled(False)
        self.editWaitingAction.setEnabled(False)
        if UCI.RIGHTS_ADMIN in RCI.instance().userRights:
            self.killAllAction.setEnabled(True)
            self.cancelAllAction.setEnabled(True)
            self.clearHistoryAction.setEnabled(True)

        self.disableAction.setEnabled(False)
        self.enableAction.setEnabled(True)

    def deactivate (self):
        """
        Clears QTreeWidget and disables it
        """
        self.waitingBox.setEnabled(False)
        self.currentBox.setEnabled(False)
        self.historyBox.setEnabled(False)
        self.statsBox.setEnabled(False)
        
        self.testWaiting.clear()
        self.testWaiting.setEnabled(False)
        self.testManager.clear()
        self.testManager.setEnabled(False)
        self.testHistory.clear()
        self.testHistory.setEnabled(False)
        self.testEnqueued.clear()
        self.testHistory.setEnabled(False)

        self.nbRunningLabel.setText("0")
        self.nbWaitingLabel.setText("0")
        self.nbHistoryLabel.setText("0")
        self.nbCompleteHistoryLabel.setText("0")
        self.nbErrorHistoryLabel.setText("0")
        self.nbKilledHistoryLabel.setText("0")
        self.nbCancelledHistoryLabel.setText("0")
        self.itemCurrent = None
        
        self.setDefaultActionsValues()
    
    def setDefaultActionsValues (self):
        """
        Set default values for qt actions
        """
        self.sortAction.setEnabled(False)
        self.sortHistoryAction.setEnabled(False)
        self.sortWaitingAction.setEnabled(False)

        self.killAction.setEnabled(False)
        self.killAllAction.setEnabled(False)

        self.cancelAction.setEnabled(False)
        self.cancelAllAction.setEnabled(False)
        self.editWaitingAction.setEnabled(False)
        self.clearHistoryAction.setEnabled(False)

        self.refreshWaitingAction.setEnabled(False)
        self.refreshRunningAction.setEnabled(False)
        self.refreshHistoryAction.setEnabled(False)
        self.partialRefreshHistoryAction.setEnabled(False)

        self.disableAction.setEnabled(False)
        self.enableAction.setEnabled(False)

    def loadProjects(self, data):
        """
        Load all projects
        """
        self.projects = data

    def loadRunning (self, data):
        """
        Loads running tasks

        @param data: 
        @type data: dict
        """
        self.nbRunningLabel.setText( str( len(data) ) )
        for task in data:
            if self.getProjectName(task['project-id']) == "UNKNOWN":
                continue
            taskItem = TaskRunningItem(  task = task, parent= self.testManager )
        
        # resize cols
        for i in xrange(len(self.labels2) - 1):
            self.testManager.resizeColumnToContents(i)
        
        # change sort order
        if self.ascendingOrder:
            self.testWaiting.sortItems(2, Qt.AscendingOrder)     # sort by sched time
        self.testManager.sortItems(2, Qt.DescendingOrder)

    def updateRunning(self, data):
        """
        Update running tasks

        @param data: 
        @type data: 
        """
        self.testManager.clear()
        self.loadRunning( data=data )

    def loadWaiting (self, data):
        """
        Loads waiting tasks

        @param data: 
        @type data: dict
        """
        self.nbWaitingLabel.setText( str(len(data)) )
        for task in data:
            eventid, eventtype, eventargs, eventtime, eventname, author, realruntime, duration, result, eventnb, \
            eventnbcur, eventenabled, withoutprobes, withoutnotif, nokeeptr, userid, projectid, eventfrom, eventto, groupid  = task
            if self.getProjectName(projectid) == "UNKNOWN":
                continue
            taskItem = TaskWaitingItem(  task = task, parent= self.testWaiting )
        
        # resize cols
        for i in xrange(len(self.labels2) - 1):
            self.testWaiting.resizeColumnToContents(i)
        
        # change sort order
        if self.ascendingOrderHistory:
            self.testWaiting.sortItems(3, Qt.AscendingOrder)     
        self.testWaiting.sortItems(3, Qt.DescendingOrder)

    def updateWaiting(self, data):
        """
        Update waiting tasks

        @param data: 
        @type data: 
        """
        self.testWaiting.clear()
        self.loadWaiting( data=data )

    def loadEnqueued (self, data):
        """
        Loads enqueued tasks

        @param data: 
        @type data: dict
        """
        for (groupId, groupData) in data.items():
            groupName, groupsTests = groupData

            pluriel = ''
            if len(groupsTests) > 1:
                pluriel = ''
            groupTask = TaskEnqueuedItem( parent=self.testEnqueued, text="%s - %s - (%s test%s)" % (groupId, groupName, len(groupsTests), pluriel) )
            groupTask.setExpanded(True)
            for gTest in groupsTests:
                tDict = eval(gTest)
                tst = "%s:%s.%s" % ( self.getProjectName(tDict['prj-id']), tDict['test-path'], tDict['test-extension'])
                
                # set the icon according to the type of test
                if tDict['test-extension'] == 'tux':
                    testIcon = QIcon(":/tux.png")
                if tDict['test-extension'] == 'tax':
                    testIcon = QIcon(":/tax.png")
                if tDict['test-extension'] == 'tsx':
                    testIcon = QIcon(":/tsx.png")
                if tDict['test-extension'] == 'tpx':
                    testIcon = QIcon(":/tpx.png")
                if tDict['test-extension'] == 'tgx':
                    testIcon = QIcon(":/tgx.png")
                subTask = TaskEnqueuedItem(parent=groupTask, text=tst, icon=testIcon)

    def updateEnqueued(self, data):
        """
        Update enqueued tasks

        @param data: 
        @type data: 
        """
        self.testEnqueued.clear()
        self.loadEnqueued( data=data )

    def loadHistory (self, data):
        """
        Loads history tasks

        @param data: 
        @type data: dict
        """
        # Init counters
        nbComplete = int(self.nbCompleteHistoryLabel.text())
        nbError = int(self.nbErrorHistoryLabel.text())
        nbKilled = int(self.nbKilledHistoryLabel.text())
        nbCancelled = int(self.nbCancelledHistoryLabel.text())

        for task in data:
            dbid, eventtype, eventargs, eventtime, eventname, author, realruntime, duration, result, projectid  = task
            if self.getProjectName(projectid) == "UNKNOWN":
                continue
            taskItem = TaskHistoryItem(  task = task, parent= self.testHistory )
            if taskItem.taskEventResult == STATE_COMPLETE:
                nbComplete += 1
            if taskItem.taskEventResult == STATE_ERROR:
                nbError += 1
            if taskItem.taskEventResult == STATE_KILLED:
                nbKilled += 1
            if taskItem.taskEventResult == STATE_CANCELLED:
                nbCancelled += 1

        # resize cols
        for i in xrange(len(self.labels2) - 1):
            self.testHistory.resizeColumnToContents(i)
        
        # change sort order by sched at
        if self.ascendingOrderHistory:
            self.testHistory.sortItems(0, Qt.AscendingOrder)     
        self.testHistory.sortItems(0, Qt.DescendingOrder)
        
        # Update summary
        nbHistory = int(self.nbHistoryLabel.text())
        self.nbHistoryLabel.setText( str( len(data) + nbHistory ) )
        self.nbCompleteHistoryLabel.setText( str(nbComplete) )
        self.nbErrorHistoryLabel.setText( str(nbError) )
        self.nbKilledHistoryLabel.setText( str(nbKilled) )
        self.nbCancelledHistoryLabel.setText( str(nbCancelled) )

    def updateHistory(self, data):
        """
        Update history tasks

        @param data: 
        @type data: 
        """
        # reset counters
        self.nbHistoryLabel.setText("0")
        self.nbCompleteHistoryLabel.setText("0")
        self.nbErrorHistoryLabel.setText("0")
        self.nbKilledHistoryLabel.setText("0")
        self.nbCancelledHistoryLabel.setText("0")
        #
        self.testHistory.clear()
        self.loadHistory( data=data )
    
    def refreshRunningTask(self, data, action):
        """
        Refresh running task

        @param data: 
        @type data: 

        @param action: 
        @type action: 
        """
        if action == "add":
            self.loadRunning(data=data)
        elif action == "update":
            self.updateRunning(data=data)
        else:
            pass # error

    def refreshWaitingTask(self, data, action):
        """
        Refresh waiting task

        @param data: 
        @type data: 

        @param action: 
        @type action: 
        """
        if action == "update":
            self.updateWaiting(data=data)
        else:
            pass # error

    def refreshEnqueuedTask(self, data, action):
        """
        Refresh enqueued task

        @param data: 
        @type data: 

        @param action: 
        @type action: 
        """
        if action == "update":
            self.updateEnqueued(data=data)
        else:
            pass # error

    def refreshHistoryTask(self, data, action):
        """
        Refresh history task

        @param data: 
        @type data: 

        @param action: 
        @type action: 
        """
        if action == "add":
            self.loadHistory(data=data)
        elif action == "update":
            self.updateHistory(data=data)
        else:
            pass # error

    def onPopupMenu(self, pos):
        """
        Display menu on right click

        @param pos: 
        @type pos:
        """
        self.menu = QMenu()
        item = self.testManager.itemAt(pos)
        if item:
            self.itemCurrent = item
            # kill task available just for admin and tester
            if UCI.RIGHTS_ADMIN in RCI.instance().userRights or UCI.RIGHTS_TESTER in RCI.instance().userRights :
                if item.taskState == STATE_RUNNING :
                    self.menu.addAction( self.killAction )
                self.menu.popup(self.testManager.mapToGlobal(pos))

    def onPopupMenuWaiting(self, pos):
        """
        Display menu on right click

        @param pos: 
        @type pos:
        """
        self.menu = QMenu()
        item = self.testWaiting.itemAt(pos)
        if item:
            self.itemCurrent = item
            # kill task available just for admin and tester
            if UCI.RIGHTS_ADMIN in RCI.instance().userRights or UCI.RIGHTS_TESTER in RCI.instance().userRights :
                self.menu.addAction( self.editWaitingAction )
                self.menu.addAction( self.cancelAction )
                self.menu.addSeparator()
                self.menu.addAction( self.disableAction )
                self.menu.addAction( self.enableAction )
                self.menu.popup(self.testWaiting.mapToGlobal(pos))

    def cancelTask(self):
        """
        Cancel the selected task
        """
        items = self.testWaiting.selectedItems()
        taskIds = []
        for itm in items:
            taskIds.append( itm.taskId )
        
        reply = QMessageBox.question(self, "Cancel test(s)", "Are you sure ?",
                        QMessageBox.Yes | QMessageBox.No )
        if reply == QMessageBox.Yes:
            RCI.instance().cancelTasks(taskIds=taskIds)
            
    def cancelAllTasks(self):
        """
        Cancel all tasks
        """
        reply = QMessageBox.question(self, "Cancel all tests", "Are you sure ?",
                        QMessageBox.Yes | QMessageBox.No )
        if reply == QMessageBox.Yes:
            RCI.instance().cancelTasksAll()
            
    def killTask(self):
        """
        Kill the selected task
        """
        items = self.testManager.selectedItems()
        taskIds = []
        for itm in items:
            taskIds.append( itm.taskId )
            
        reply = QMessageBox.question(self, "Kill test", "Are you sure ?",
                        QMessageBox.Yes | QMessageBox.No )
        if reply == QMessageBox.Yes:
            RCI.instance().killTasks(taskIds=taskIds)
            
    def killAllTasks(self):
        """
        Kill all tasks
        """
        reply = QMessageBox.question(self, "Kill all tests", "Are you sure ?",
                        QMessageBox.Yes | QMessageBox.No )
        if reply == QMessageBox.Yes:
            RCI.instance().killTasksAll()
    
    def toggleSort(self):
        """
        Toggle sort of running task
        """
        if not self.ascendingOrder:
            self.testManager.sortItems(2, Qt.AscendingOrder)
            self.ascendingOrder = True
        else:
            self.testManager.sortItems(2, Qt.DescendingOrder)
            self.ascendingOrder = False

    def toggleSortHistory(self):
        """
        Toggle sort of the history
        """
        if not self.ascendingOrderHistory:
            self.testHistory.sortItems(0, Qt.AscendingOrder)
            self.ascendingOrderHistory = True
        else:
            self.testHistory.sortItems(0, Qt.DescendingOrder)
            self.ascendingOrderHistory = False

    def toggleSortWaiting(self):
        """
        Toggle sort of the waiting 
        """
        if not self.ascendingOrderWaiting:
            self.testWaiting.sortItems(3, Qt.AscendingOrder)
            self.ascendingOrderWaiting = True
        else:
            self.testWaiting.sortItems(3, Qt.DescendingOrder)
            self.ascendingOrderWaiting = False