5 from PyQt4.QtCore import *
6 from PyQt4.QtGui import *
8 from sface.config import config
9 from sface.sfiprocess import SfiProcess
10 from sface.screens.sfascreen import SfaScreen
11 from sface.sfidata import SfiData
15 MEMBERSHIP_STATUS_COLUMN = 1
16 SERVER_MEMBERSHIP_STATUS_COLUMN = 2
18 # maximum length of a name to display before clipping
21 user_status = { "in": "Already Selected",
22 "out": "Not Selected",
24 "remove": "To be Removed"}
26 color_status = { "in": QColor.fromRgb(0, 250, 250),
27 "add": QColor.fromRgb(0, 250, 0),
28 "remove": QColor.fromRgb(250, 0, 0) }
31 class UserNameDelegate(QStyledItemDelegate):
32 def __init__(self, parent):
33 QStyledItemDelegate.__init__(self, parent)
35 def displayText(self, value, locale):
36 data = str(QStyledItemDelegate.displayText(self, value, locale))
37 if (len(data)>NAME_MAX_LEN):
38 data = data[:(NAME_MAX_LEN-3)] + "..."
41 def paint(self, painter, option, index):
43 data = str(self.displayText(index.data(), QLocale()))
44 status_index = model.index(index.row(), MEMBERSHIP_STATUS_COLUMN, index.parent())
45 status_data = status_index.data().toString()
47 fm = QFontMetrics(option.font)
48 rect = QRect(option.rect)
50 rect.setHeight(rect.height() - 2)
51 rect.setWidth(fm.width(QString(data)) + 6)
52 rect.setX(rect.x() + 5)
53 rect.setY(rect.y() - 1)
55 textRect = QRect(option.rect)
56 textRect.setWidth(fm.width(QString(data)) + 6)
57 textRect.setX(rect.x())
59 x, y, h, w = rect.x(), rect.y(), rect.height(), rect.width()
62 path.addRoundedRect(x - 1, y + 1, w, h, 4, 4)
65 painter.setRenderHint(QPainter.Antialiasing)
67 if option.state & QStyle.State_Selected:
68 painter.fillRect(option.rect, option.palette.color(QPalette.Active, QPalette.Highlight))
70 for x in user_status.keys():
71 if (user_status[x] == status_data) and (x in color_status):
72 painter.fillPath(path, color_status[x])
74 painter.setPen(QColor.fromRgb(0, 0, 0))
75 painter.drawText(textRect, Qt.AlignVCenter, QString(data))
79 class UserView(QTableView):
80 def __init__(self, parent=None):
81 QTableView.__init__(self, parent)
83 self.setSelectionBehavior(QAbstractItemView.SelectRows)
84 self.setSelectionMode(QAbstractItemView.SingleSelection)
85 self.setAlternatingRowColors(True)
86 self.setAttribute(Qt.WA_MacShowFocusRect, 0)
87 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
88 self.setToolTip("Double click on a row to change its status.")
90 self.setItemDelegateForColumn(0, UserNameDelegate(self))
92 def keyPressEvent(self, event):
93 if (event.key() == Qt.Key_Space):
94 self.toggleSelection()
96 QTableView.keyPressEvent(self, event)
98 def mouseDoubleClickEvent(self, event):
99 self.toggleSelection()
101 def toggleSelection(self):
102 index = self.currentIndex()
103 model = index.model()
104 status_index = model.index(index.row(), MEMBERSHIP_STATUS_COLUMN, index.parent())
105 status_data = status_index.data().toString()
106 server_status_data = model.index(index.row(), SERVER_MEMBERSHIP_STATUS_COLUMN, index.parent()).data().toString()
107 node_index = model.index(index.row(), NAME_COLUMN, index.parent())
108 node_data = node_index.data().toString()
111 if status_data == user_status['in']:
112 model.setData(status_index, QString(user_status['remove']))
113 elif status_data == user_status['out']:
114 model.setData(status_index, QString(user_status['add']))
115 elif status_data in (user_status['add'], user_status['remove']):
116 if server_status_data == user_status["in"]:
117 model.setData(status_index, QString(user_status['in']))
119 model.setData(status_index, QString(user_status['out']))
121 model.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), node_index, node_index)
123 def currentChanged(self, current, previous):
124 model = current.model()
125 node_index = model.index(current.row(), 0, current.parent())
126 node_data = node_index.data().toString()
128 def hideUnusableColumns(self):
129 self.hideColumn(SERVER_MEMBERSHIP_STATUS_COLUMN)
131 class UserModel(QStandardItemModel):
132 def __init__(self, rows=0, columns=4, parent=None):
133 QStandardItemModel.__init__(self, rows, columns, parent)
135 def updateModel(self, sliceRec):
142 #for pi in sliceRec.get("PI", []):
144 # if not name in added_persons:
145 # slice_persons.append({"name": name, "role": "PI", "member": user_status["in"]})
146 # added_persons.append(name)
148 for researcher in sliceRec.get("researcher", []):
149 name = str(researcher)
150 if not name in added_persons:
151 slice_persons.append({"name": name, "role": "researcher", "member": user_status["in"]})
152 added_persons.append(name)
154 userNames = SfiData().getAuthorityHrns(type="user")
155 for name in userNames:
156 if not name in added_persons:
157 slice_persons.append({"name": name, "role": "", "member": user_status["out"]})
158 added_persons.append(name)
160 rootItem = self.invisibleRootItem()
162 for person in slice_persons:
163 rootItem.appendRow([self.readOnlyItem(person["name"]),
164 self.readOnlyItem(person["member"]),
165 self.readOnlyItem(person["member"])])
167 headers = QStringList() << "User Name" << "Status" << "ServerStatus"
168 self.setHorizontalHeaderLabels(headers)
170 def readOnlyItem(self, x):
171 item = QStandardItem(QString(x))
172 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
175 def updateRecord(self, slicerec):
178 item = self.invisibleRootItem()
179 children = item.rowCount()
180 for row in range(0, children):
181 childName = str(item.child(row, NAME_COLUMN).data(Qt.DisplayRole).toString())
182 childStatus = str(item.child(row, MEMBERSHIP_STATUS_COLUMN).data(Qt.DisplayRole).toString())
184 if (childStatus == user_status['add']):
185 researcher = slicerec.get("researcher", [])
186 researcher.append(childName)
187 slicerec["researcher"] = researcher
189 elif (childStatus == user_status['remove']):
190 if childName in slicerec.get("PI"):
191 slicerec.get("PI").remove(childName)
192 if childName in slicerec.get("researcher"):
193 slicerec.get("researcher").remove(childName)
198 def getResearchers(self):
200 item = self.invisibleRootItem()
201 children = item.rowCount()
202 for row in range(0, children):
203 childName = str(item.child(row, NAME_COLUMN).data(Qt.DisplayRole).toString())
204 childStatus = str(item.child(row, MEMBERSHIP_STATUS_COLUMN).data(Qt.DisplayRole).toString())
206 if (childStatus == user_status['add']) or (childStatus == user_status['in']):
207 researchers.append(childName)
211 class UsersWidget(QWidget):
212 def __init__(self, parent):
213 QWidget.__init__(self, parent)
215 self.process = SfiProcess(self)
217 self.slicename = QLabel("", self)
218 self.updateSliceName()
219 self.slicename.setScaledContents(False)
220 searchlabel = QLabel ("Search: ", self)
221 searchlabel.setScaledContents(False)
222 searchbox = QLineEdit(self)
223 searchbox.setAttribute(Qt.WA_MacShowFocusRect, 0)
225 toplayout = QHBoxLayout()
226 toplayout.addWidget(self.slicename, 0, Qt.AlignLeft)
227 toplayout.addStretch()
228 toplayout.addWidget(searchlabel, 0, Qt.AlignRight)
229 toplayout.addWidget(searchbox, 0, Qt.AlignRight)
231 self.userView = UserView()
233 refresh = QPushButton("Refresh", self)
234 refresh.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
235 submit = QPushButton("Submit", self)
236 submit.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
238 bottomlayout = QHBoxLayout()
239 bottomlayout.addWidget(refresh, 0, Qt.AlignLeft)
240 bottomlayout.addStretch()
241 bottomlayout.addWidget(submit, 0, Qt.AlignRight)
243 layout = QVBoxLayout()
244 layout.addLayout(toplayout)
245 layout.addWidget(self.userView)
246 layout.addLayout(bottomlayout)
247 self.setLayout(layout)
248 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
250 self.userModel = UserModel(parent=self)
252 self.connect(refresh, SIGNAL('clicked()'), self.refresh)
253 self.connect(submit, SIGNAL('clicked()'), self.submit)
257 def submitFinished(self):
258 self.disconnect(self.process, SIGNAL('finished()'), self.submitFinished)
260 faultString = self.process.getFaultString()
262 self.setStatus("<font color='green'>Slice user data submitted.</font>")
263 QTimer.singleShot(1000, self.refresh)
265 self.setStatus("<font color='red'>Slice user submit failed: %s</font>" % (faultString))
267 def getSliceRecordFinished(self):
268 self.disconnect(self.process, SIGNAL('finished()'), self.getSliceRecordFinished)
270 faultString = self.process.getFaultString()
272 self.setStatus("<font color='green'>Slice record refreshed.</font>")
273 self.refreshAuthority()
275 self.setStatus("<font color='red'>Slice rec refresh error: %s</font>" % (faultString))
277 def getAuthorityRecordFinished(self):
278 self.disconnect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished)
280 faultString = self.process.getFaultString()
282 self.setStatus("<font color='green'>User data refreshed.</font>")
284 #self.parent().signalAll("usersUpdated")
286 self.setStatus("<font color='red'>Authority rec refresh error: %s</font>" % (faultString))
288 def setStatus(self, msg, timeout=None):
289 self.parent().setStatus(msg, timeout)
291 def checkRunningProcess(self):
292 if self.process.isRunning():
293 self.setStatus("<font color='red'>There is already a process running. Please wait.</font>")
298 if self.checkRunningProcess():
301 rec = SfiData().getSliceRecord()
302 change = self.userModel.updateRecord(rec)
305 self.setStatus("<font color=red>No change in slice data. Not submitting!</font>", timeout=3000)
308 rec_file = config.getSliceRecordFile()
309 file(rec_file, "w").write(rec.save_to_string())
311 self.connect(self.process, SIGNAL('finished()'), self.submitFinished)
313 self.process.updateRecord(rec_file)
314 self.setStatus("Sending slice record. This will take some time...")
317 if not config.getSlice():
318 self.setStatus("<font color='red'>Slice not set yet!</font>")
321 if self.process.isRunning():
322 self.setStatus("<font color='red'>There is already a process running. Please wait.</font>")
325 self.connect(self.process, SIGNAL('finished()'), self.getSliceRecordFinished)
327 self.process.getSliceRecord()
328 self.setStatus("Refreshing slice record. This will take some time...")
330 def refreshAuthority(self):
331 self.connect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished)
333 self.process.listRecords(config.getAuthority(), None)
334 self.setStatus("Refreshing user records. This will take some time...")
336 def updateView(self):
337 sliceRec = SfiData().getSliceRecord()
340 # wait until we've resolved the slicerecord before displaying
341 # anything to the user.
342 self.userModel.clear()
344 self.userModel.updateModel(sliceRec)
346 self.userView.setModel(self.userModel)
347 self.userView.hideColumn(SERVER_MEMBERSHIP_STATUS_COLUMN)
348 self.userView.resizeColumnToContents(0)
350 def updateSliceName(self):
351 self.slicename.setText("Slice : %s" % (config.getSlice() or "None"))
356 class UserScreen(SfaScreen):
357 def __init__(self, parent):
358 SfaScreen.__init__(self, parent)
360 slice = UsersWidget(self)
361 self.init(slice, "Users", "OneLab SFA crawler")
363 def configurationChanged(self):
364 self.widget.updateSliceName()
365 self.widget.updateView()