retrieve all records when listing authority, and filter the user ones
[sface.git] / sface / screens / userscreen.py
index 7745d59..db0d3e7 100644 (file)
@@ -11,15 +11,74 @@ from sface.sfiprocess import SfiProcess
 from sface.screens.sfascreen import SfaScreen
 
 NAME_COLUMN = 0
-ROLE_COLUMN = 1
-MEMBERSHIP_STATUS_COLUMN = 2
-SERVER_MEMBERSHIP_STATUS_COLUMN = 3
+#ROLE_COLUMN = 1
+MEMBERSHIP_STATUS_COLUMN = 1
+SERVER_MEMBERSHIP_STATUS_COLUMN = 2
+
+# maximum length of a name to display before clipping
+NAME_MAX_LEN = 48
 
 user_status = { "in": "Already Selected",
                 "out": "Not Selected",
                 "add": "To be Added",
                 "remove": "To be Removed"}
 
+color_status = { "in": QColor.fromRgb(0, 250, 250),
+                 "add": QColor.fromRgb(0, 250, 0),
+                 "remove": QColor.fromRgb(250, 0, 0) }
+
+
+class UserNameDelegate(QStyledItemDelegate):
+    def __init__(self, parent):
+        QStyledItemDelegate.__init__(self, parent)
+
+    def displayText(self, value, locale):
+        data = str(QStyledItemDelegate.displayText(self, value, locale))
+        if (len(data)>NAME_MAX_LEN):
+            data = data[:(NAME_MAX_LEN-3)] + "..."
+        return QString(data)
+
+    def paint(self, painter, option, index):
+        model = index.model()
+        data = str(self.displayText(index.data(), QLocale()))
+        status_index = model.index(index.row(), MEMBERSHIP_STATUS_COLUMN, index.parent())
+        status_data = status_index.data().toString()
+
+        fm = QFontMetrics(option.font)
+        rect = QRect(option.rect)
+
+        rect.setHeight(rect.height() - 2)
+        rect.setWidth(fm.width(QString(data)) + 6)
+        rect.setX(rect.x() + 5)
+        rect.setY(rect.y() - 1)
+
+        textRect = QRect(option.rect)
+        textRect.setWidth(fm.width(QString(data)) + 6)
+        textRect.setX(rect.x())
+
+        x, y, h, w = rect.x(), rect.y(), rect.height(), rect.width()
+
+        path = QPainterPath()
+        path.addRoundedRect(x - 1, y + 1, w, h, 4, 4)
+
+        painter.save()
+        painter.setRenderHint(QPainter.Antialiasing)
+
+        if option.state & QStyle.State_Selected:
+            painter.fillRect(option.rect, option.palette.color(QPalette.Active, QPalette.Highlight))
+
+        color = None
+        for x in user_status.keys():
+            if (user_status[x] == status_data) and (x in color_status):
+                color = color_status[x]
+
+        if color != None:
+            painter.fillPath(path, color)
+        painter.setPen(QColor.fromRgb(0, 0, 0))
+        painter.drawText(textRect, Qt.AlignVCenter, QString(data))
+
+        painter.restore()
+
 class UserView(QTableView):
     def __init__(self, parent=None):
         QTableView.__init__(self, parent)
@@ -31,7 +90,18 @@ class UserView(QTableView):
         self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
         self.setToolTip("Double click on a row to change its status.")
 
+        self.setItemDelegateForColumn(0, UserNameDelegate(self))
+
+    def keyPressEvent(self, event):
+        if (event.key() == Qt.Key_Space):
+            self.toggleSelection()
+        else:
+            QTableView.keyPressEvent(self, event)
+
     def mouseDoubleClickEvent(self, event):
+        self.toggleSelection()
+
+    def toggleSelection(self):
         index = self.currentIndex()
         model = index.model()
         status_index = model.index(index.row(), MEMBERSHIP_STATUS_COLUMN, index.parent())
@@ -57,8 +127,104 @@ class UserView(QTableView):
         model = current.model()
         node_index = model.index(current.row(), 0, current.parent())
         node_data = node_index.data().toString()
-        self.emit(SIGNAL('hostnameClicked(QString)'), node_data)
 
+    def hideUnusableColumns(self):
+        self.hideColumn(SERVER_MEMBERSHIP_STATUS_COLUMN)
+
+class UserModel(QStandardItemModel):
+    def __init__(self, rows=0, columns=4, parent=None):
+         QStandardItemModel.__init__(self, rows, columns, parent)
+
+    def updateModel(self, sliceRec):
+        self.clear()
+
+        added_persons = []
+        slice_persons = []
+
+        if sliceRec:
+            #for pi in sliceRec.get_field("PI", default=[]):
+            #    name = str(pi)
+            #    if not name in added_persons:
+            #         slice_persons.append({"name": name, "role": "PI", "member": user_status["in"]})
+            #         added_persons.append(name)
+
+            for researcher in sliceRec.get_field("researcher", default=[]):
+                name = str(researcher)
+                if not name in added_persons:
+                     slice_persons.append({"name": name, "role": "researcher", "member": user_status["in"]})
+                     added_persons.append(name)
+
+        i=0
+        while (os.path.exists(config.getAuthorityListFile(i))):
+            rec = self.readUserRecord(i)
+            if rec:
+                type = str(rec.get_type())
+                if (type == "user"):
+                    name = str(rec.get_name())
+                    if not name in added_persons:
+                        slice_persons.append({"name": name, "role": "", "member": user_status["out"]})
+                        added_persons.append(name)
+            i=i+1
+
+        rootItem = self.invisibleRootItem()
+
+        for person in slice_persons:
+            rootItem.appendRow([self.readOnlyItem(person["name"]),
+                                self.readOnlyItem(person["member"]),
+                                self.readOnlyItem(person["member"])])
+
+        headers = QStringList() << "User Name" << "Status" << "ServerStatus"
+        self.setHorizontalHeaderLabels(headers)
+
+    def readOnlyItem(self, x):
+        item = QStandardItem(QString(x))
+        item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
+        return item
+
+    def updateRecord(self, slicerec):
+        change = False
+
+        item = self.invisibleRootItem()
+        children = item.rowCount()
+        for row in range(0, children):
+            childName = str(item.child(row, NAME_COLUMN).data(Qt.DisplayRole).toString())
+            childStatus = str(item.child(row, MEMBERSHIP_STATUS_COLUMN).data(Qt.DisplayRole).toString())
+
+            if (childStatus == user_status['add']):
+                researcher = slicerec.get_field("researcher", [])
+                researcher.append(childName)
+                slicerec["researcher"] = researcher
+                change = True
+            elif (childStatus == user_status['remove']):
+                if childName in slicerec.get_field("PI"):
+                     slicerec.get_field("PI").remove(childName)
+                if childName in slicerec.get_field("researcher"):
+                     slicerec.get_field("researcher").remove(childName)
+                change = True
+
+        return change
+
+    def getResearchers(self):
+        researchers = []
+        item = self.invisibleRootItem()
+        children = item.rowCount()
+        for row in range(0, children):
+            childName = str(item.child(row, NAME_COLUMN).data(Qt.DisplayRole).toString())
+            childStatus = str(item.child(row, MEMBERSHIP_STATUS_COLUMN).data(Qt.DisplayRole).toString())
+
+            if (childStatus == user_status['add']) or (childStatus == user_status['in']):
+                researchers.append(childName)
+
+        return researchers
+
+    def readUserRecord(self, i):
+        rec_file = config.getAuthorityListFile(i)
+        if os.path.exists(rec_file):
+            xml = open(rec_file).read()
+            rec = UserRecord()
+            rec.load_from_string(xml)
+            return rec
+        return None
 
 class UsersWidget(QWidget):
     def __init__(self, parent):
@@ -81,8 +247,6 @@ class UsersWidget(QWidget):
         toplayout.addWidget(searchbox, 0, Qt.AlignRight)
 
         self.userView = UserView()
-        #self.userView.setSelectionBehavior(QAbstractItemView.SelectRows)
-        #self.userView.setSelectionMode(QAbstractItemView.SingleSelection)
 
         refresh = QPushButton("Refresh", self)
         refresh.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
@@ -101,7 +265,7 @@ class UsersWidget(QWidget):
         self.setLayout(layout)
         self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
 
-        self.userModel = QStandardItemModel(0, 4, self)
+        self.userModel = UserModel(parent=self)
 
         self.connect(refresh, SIGNAL('clicked()'), self.refresh)
         self.connect(submit, SIGNAL('clicked()'), self.submit)
@@ -109,17 +273,35 @@ class UsersWidget(QWidget):
         self.updateView()
 
     def submitFinished(self):
-        self.setStatus("<font color='green'>Slice data submitted.</font>")
-        QTimer.singleShot(1000, self.refresh)
+        self.disconnect(self.process, SIGNAL('finished()'), self.submitFinished)
+
+        faultString = self.process.getFaultString()
+        if not faultString:
+            self.setStatus("<font color='green'>Slice user data submitted.</font>")
+            QTimer.singleShot(1000, self.refresh)
+        else:
+            self.setStatus("<font color='red'>Slice user submit failed: %s</font>" % (faultString))
 
     def getSliceRecordFinished(self):
-        self.setStatus("<font color='green'>Authority data refreshed.</font>", timeout=5000)
-        self.refreshAuthority()
+        self.disconnect(self.process, SIGNAL('finished()'), self.getSliceRecordFinished)
+
+        faultString = self.process.getFaultString()
+        if not faultString:
+            self.setStatus("<font color='green'>Slice record refreshed.</font>")
+            self.refreshAuthority()
+        else:
+            self.setStatus("<font color='red'>Slice rec refresh error: %s</font>" % (faultString))
 
     def getAuthorityRecordFinished(self):
-        self.setStatus("<font color='green'>Slice data refreshed.</font>", timeout=5000)
-        self.updateView()
-        #self.parent().signalAll("usersUpdated")
+        self.disconnect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished)
+
+        faultString = self.process.getFaultString()
+        if not faultString:
+            self.setStatus("<font color='green'>User data refreshed.</font>")
+            self.updateView()
+            #self.parent().signalAll("usersUpdated")
+        else:
+            self.setStatus("<font color='red'>Authority rec refresh error: %s</font>" % (faultString))
 
     def readSliceRecord(self):
         rec_file = config.getSliceRecordFile()
@@ -139,15 +321,6 @@ class UsersWidget(QWidget):
             return rec
         return None
 
-    def readUserRecord(self, i):
-        rec_file = config.getAuthorityListFile(i)
-        if os.path.exists(rec_file):
-            xml = open(rec_file).read()
-            rec = UserRecord()
-            rec.load_from_string(xml)
-            return rec
-        return None
-
     def setStatus(self, msg, timeout=None):
         self.parent().setStatus(msg, timeout)
 
@@ -162,7 +335,7 @@ class UsersWidget(QWidget):
             return
 
         rec = self.readSliceRecord()
-        change = self.process_table(rec)
+        change = self.userModel.updateRecord(rec)
 
         if not change:
             self.setStatus("<font color=red>No change in slice data. Not submitting!</font>", timeout=3000)
@@ -171,7 +344,6 @@ class UsersWidget(QWidget):
         rec_file = config.getSliceRecordFile()
         file(rec_file, "w").write(rec.save_to_string())
 
-        self.disconnect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished)
         self.connect(self.process, SIGNAL('finished()'), self.submitFinished)
 
         self.process.updateRecord(rec_file)
@@ -186,73 +358,26 @@ class UsersWidget(QWidget):
             self.setStatus("<font color='red'>There is already a process running. Please wait.</font>")
             return
 
-        self.disconnect(self.process, SIGNAL('finished()'), self.submitFinished)
         self.connect(self.process, SIGNAL('finished()'), self.getSliceRecordFinished)
 
         self.process.getSliceRecord()
         self.setStatus("Refreshing slice record. This will take some time...")
 
     def refreshAuthority(self):
-        self.disconnect(self.process, SIGNAL('finished()'), self.getSliceRecordFinished)
         self.connect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished)
 
-        self.process.listRecords(config.getAuthority(), "user", config.getAuthorityListFile())
+        self.process.listRecords(config.getAuthority(), None, config.getAuthorityListFile())
         self.setStatus("Refreshing user records. This will take some time...")
 
-    def addTableItem(self, table, row, col, val, data=None, readonly=True):
-        item = QTableWidgetItem(str(val))
-        if readonly:
-            item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
-        if data:
-            if not isinstance(data, str):
-               data = pickle.dumps(data)
-            item.setData(Qt.UserRole, QVariant(data))
-        table.setItem(row, col, item)
-
-    def updateModel(self):
-        self.userModel.clear()
-
+    def updateView(self):
         sliceRec = self.readSliceRecord()
-        if not sliceRec:
-            return None
 
-        added_persons = []
-        slice_persons = []
-
-        for pi in sliceRec.get_field("PI"):
-            name = str(pi)
-            if not name in added_persons:
-                 slice_persons.append({"name": name, "role": "PI", "member": user_status["in"]})
-                 added_persons.append(name)
-        for researcher in sliceRec.get_field("researcher"):
-            name = str(researcher)
-            if not name in added_persons:
-                 slice_persons.append({"name": name, "role": "researcher", "member": user_status["in"]})
-                 added_persons.append(name)
-
-        i=1
-        while (os.path.exists(config.getAuthorityListFile(i))):
-            rec = self.readUserRecord(i)
-            if rec:
-                name = str(rec.get_name())
-                if not name in added_persons:
-                    slice_persons.append({"name": name, "role": "", "member": user_status["out"]})
-                    added_persons.append(name)
-            i=i+1
-
-        rootItem = self.userModel.invisibleRootItem()
-
-        for person in slice_persons:
-            rootItem.appendRow([QStandardItem(QString(person["name"])),
-                               QStandardItem(QString(person["role"])),
-                               QStandardItem(QString(person["member"])),
-                               QStandardItem(QString(person["member"]))])
-
-        headers = QStringList() << "User Name" << "Role" << "Status" << "ServerStatus"
-        self.userModel.setHorizontalHeaderLabels(headers)
-
-    def updateView(self):
-        self.updateModel()
+        if not sliceRec:
+            # wait until we've resolved the slicerecord before displaying
+            # anything to the user.
+            self.userModel.clear()
+        else:
+            self.userModel.updateModel(sliceRec)
 
         self.userView.setModel(self.userModel)
         self.userView.hideColumn(SERVER_MEMBERSHIP_STATUS_COLUMN)
@@ -261,30 +386,7 @@ class UsersWidget(QWidget):
     def updateSliceName(self):
         self.slicename.setText("Slice : %s" % (config.getSlice() or "None"))
 
-    def nodeSelectionChanged(self, hostname):
-        self.parent().nodeSelectionChanged(hostname)
-
-    def process_table(self, slicerec):
-        change = False
-
-        item = self.userModel.invisibleRootItem()
-        children = item.rowCount()
-        for row in range(0, children):
-            childName = str(item.child(row, NAME_COLUMN).data(Qt.DisplayRole).toString())
-            childStatus = str(item.child(row, MEMBERSHIP_STATUS_COLUMN).data(Qt.DisplayRole).toString())
-
-            if (childStatus == user_status['add']):
-                slicerec.get_field("researcher").append(childName)
-                change = True
-            elif (childStatus == user_status['remove']):
-                if childName in slicerec.get_field("PI"):
-                     slicerec.get_field("PI").remove(childName)
-                if childName in slicerec.get_field("researcher"):
-                     slicerec.get_field("researcher").remove(childName)
-                change = True
 
-        print "XXX", slicerec.get_field("researcher")
-        return change
 
 
 class UserScreen(SfaScreen):