5 from PyQt4.QtCore import *
6 from PyQt4.QtGui import *
8 from sfa.util.record import SfaRecord, SliceRecord, AuthorityRecord, UserRecord
9 from sface.config import config
10 from sface.sfiprocess import SfiProcess
11 from sface.screens.sfascreen import SfaScreen
12 from sface.sfidata import SfiData
16 MEMBERSHIP_STATUS_COLUMN = 1
17 SERVER_MEMBERSHIP_STATUS_COLUMN = 2
19 # maximum length of a name to display before clipping
22 user_status = { "in": "Already Selected",
23 "out": "Not Selected",
25 "remove": "To be Removed"}
27 color_status = { "in": QColor.fromRgb(0, 250, 250),
28 "add": QColor.fromRgb(0, 250, 0),
29 "remove": QColor.fromRgb(250, 0, 0) }
32 class UserNameDelegate(QStyledItemDelegate):
33 def __init__(self, parent):
34 QStyledItemDelegate.__init__(self, parent)
36 def displayText(self, value, locale):
37 data = str(QStyledItemDelegate.displayText(self, value, locale))
38 if (len(data)>NAME_MAX_LEN):
39 data = data[:(NAME_MAX_LEN-3)] + "..."
42 def paint(self, painter, option, index):
44 data = str(self.displayText(index.data(), QLocale()))
45 status_index = model.index(index.row(), MEMBERSHIP_STATUS_COLUMN, index.parent())
46 status_data = status_index.data().toString()
48 fm = QFontMetrics(option.font)
49 rect = QRect(option.rect)
51 rect.setHeight(rect.height() - 2)
52 rect.setWidth(fm.width(QString(data)) + 6)
53 rect.setX(rect.x() + 5)
54 rect.setY(rect.y() - 1)
56 textRect = QRect(option.rect)
57 textRect.setWidth(fm.width(QString(data)) + 6)
58 textRect.setX(rect.x())
60 x, y, h, w = rect.x(), rect.y(), rect.height(), rect.width()
63 path.addRoundedRect(x - 1, y + 1, w, h, 4, 4)
66 painter.setRenderHint(QPainter.Antialiasing)
68 if option.state & QStyle.State_Selected:
69 painter.fillRect(option.rect, option.palette.color(QPalette.Active, QPalette.Highlight))
71 for x in user_status.keys():
72 if (user_status[x] == status_data) and (x in color_status):
73 painter.fillPath(path, color_status[x])
75 painter.setPen(QColor.fromRgb(0, 0, 0))
76 painter.drawText(textRect, Qt.AlignVCenter, QString(data))
80 class UserView(QTableView):
81 def __init__(self, parent=None):
82 QTableView.__init__(self, parent)
84 self.setSelectionBehavior(QAbstractItemView.SelectRows)
85 self.setSelectionMode(QAbstractItemView.SingleSelection)
86 self.setAlternatingRowColors(True)
87 self.setAttribute(Qt.WA_MacShowFocusRect, 0)
88 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
89 self.setToolTip("Double click on a row to change its status.")
91 self.setItemDelegateForColumn(0, UserNameDelegate(self))
93 def keyPressEvent(self, event):
94 if (event.key() == Qt.Key_Space):
95 self.toggleSelection()
97 QTableView.keyPressEvent(self, event)
99 def mouseDoubleClickEvent(self, event):
100 self.toggleSelection()
102 def toggleSelection(self):
103 index = self.currentIndex()
104 model = index.model()
105 status_index = model.index(index.row(), MEMBERSHIP_STATUS_COLUMN, index.parent())
106 status_data = status_index.data().toString()
107 server_status_data = model.index(index.row(), SERVER_MEMBERSHIP_STATUS_COLUMN, index.parent()).data().toString()
108 node_index = model.index(index.row(), NAME_COLUMN, index.parent())
109 node_data = node_index.data().toString()
112 if status_data == user_status['in']:
113 model.setData(status_index, QString(user_status['remove']))
114 elif status_data == user_status['out']:
115 model.setData(status_index, QString(user_status['add']))
116 elif status_data in (user_status['add'], user_status['remove']):
117 if server_status_data == user_status["in"]:
118 model.setData(status_index, QString(user_status['in']))
120 model.setData(status_index, QString(user_status['out']))
122 model.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), node_index, node_index)
124 def currentChanged(self, current, previous):
125 model = current.model()
126 node_index = model.index(current.row(), 0, current.parent())
127 node_data = node_index.data().toString()
129 def hideUnusableColumns(self):
130 self.hideColumn(SERVER_MEMBERSHIP_STATUS_COLUMN)
132 class UserModel(QStandardItemModel):
133 def __init__(self, rows=0, columns=4, parent=None):
134 QStandardItemModel.__init__(self, rows, columns, parent)
136 def updateModel(self, sliceRec):
143 #for pi in sliceRec.get_field("PI", default=[]):
145 # if not name in added_persons:
146 # slice_persons.append({"name": name, "role": "PI", "member": user_status["in"]})
147 # added_persons.append(name)
149 for researcher in sliceRec.get_field("researcher", default=[]):
150 name = str(researcher)
151 if not name in added_persons:
152 slice_persons.append({"name": name, "role": "researcher", "member": user_status["in"]})
153 added_persons.append(name)
155 userNames = SfiData().getAuthorityHrns(type="user")
156 for name in userNames:
157 if not name in added_persons:
158 slice_persons.append({"name": name, "role": "", "member": user_status["out"]})
159 added_persons.append(name)
161 rootItem = self.invisibleRootItem()
163 for person in slice_persons:
164 rootItem.appendRow([self.readOnlyItem(person["name"]),
165 self.readOnlyItem(person["member"]),
166 self.readOnlyItem(person["member"])])
168 headers = QStringList() << "User Name" << "Status" << "ServerStatus"
169 self.setHorizontalHeaderLabels(headers)
171 def readOnlyItem(self, x):
172 item = QStandardItem(QString(x))
173 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
176 def updateRecord(self, slicerec):
179 item = self.invisibleRootItem()
180 children = item.rowCount()
181 for row in range(0, children):
182 childName = str(item.child(row, NAME_COLUMN).data(Qt.DisplayRole).toString())
183 childStatus = str(item.child(row, MEMBERSHIP_STATUS_COLUMN).data(Qt.DisplayRole).toString())
185 if (childStatus == user_status['add']):
186 researcher = slicerec.get_field("researcher", [])
187 researcher.append(childName)
188 slicerec["researcher"] = researcher
190 elif (childStatus == user_status['remove']):
191 if childName in slicerec.get_field("PI"):
192 slicerec.get_field("PI").remove(childName)
193 if childName in slicerec.get_field("researcher"):
194 slicerec.get_field("researcher").remove(childName)
199 def getResearchers(self):
201 item = self.invisibleRootItem()
202 children = item.rowCount()
203 for row in range(0, children):
204 childName = str(item.child(row, NAME_COLUMN).data(Qt.DisplayRole).toString())
205 childStatus = str(item.child(row, MEMBERSHIP_STATUS_COLUMN).data(Qt.DisplayRole).toString())
207 if (childStatus == user_status['add']) or (childStatus == user_status['in']):
208 researchers.append(childName)
212 class UsersWidget(QWidget):
213 def __init__(self, parent):
214 QWidget.__init__(self, parent)
216 self.process = SfiProcess(self)
218 self.slicename = QLabel("", self)
219 self.updateSliceName()
220 self.slicename.setScaledContents(False)
221 searchlabel = QLabel ("Search: ", self)
222 searchlabel.setScaledContents(False)
223 searchbox = QLineEdit(self)
224 searchbox.setAttribute(Qt.WA_MacShowFocusRect, 0)
226 toplayout = QHBoxLayout()
227 toplayout.addWidget(self.slicename, 0, Qt.AlignLeft)
228 toplayout.addStretch()
229 toplayout.addWidget(searchlabel, 0, Qt.AlignRight)
230 toplayout.addWidget(searchbox, 0, Qt.AlignRight)
232 self.userView = UserView()
234 refresh = QPushButton("Refresh", self)
235 refresh.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
236 submit = QPushButton("Submit", self)
237 submit.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
239 bottomlayout = QHBoxLayout()
240 bottomlayout.addWidget(refresh, 0, Qt.AlignLeft)
241 bottomlayout.addStretch()
242 bottomlayout.addWidget(submit, 0, Qt.AlignRight)
244 layout = QVBoxLayout()
245 layout.addLayout(toplayout)
246 layout.addWidget(self.userView)
247 layout.addLayout(bottomlayout)
248 self.setLayout(layout)
249 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
251 self.userModel = UserModel(parent=self)
253 self.connect(refresh, SIGNAL('clicked()'), self.refresh)
254 self.connect(submit, SIGNAL('clicked()'), self.submit)
258 def submitFinished(self):
259 self.disconnect(self.process, SIGNAL('finished()'), self.submitFinished)
261 faultString = self.process.getFaultString()
263 self.setStatus("<font color='green'>Slice user data submitted.</font>")
264 QTimer.singleShot(1000, self.refresh)
266 self.setStatus("<font color='red'>Slice user submit failed: %s</font>" % (faultString))
268 def getSliceRecordFinished(self):
269 self.disconnect(self.process, SIGNAL('finished()'), self.getSliceRecordFinished)
271 faultString = self.process.getFaultString()
273 self.setStatus("<font color='green'>Slice record refreshed.</font>")
274 self.refreshAuthority()
276 self.setStatus("<font color='red'>Slice rec refresh error: %s</font>" % (faultString))
278 def getAuthorityRecordFinished(self):
279 self.disconnect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished)
281 faultString = self.process.getFaultString()
283 self.setStatus("<font color='green'>User data refreshed.</font>")
285 #self.parent().signalAll("usersUpdated")
287 self.setStatus("<font color='red'>Authority rec refresh error: %s</font>" % (faultString))
289 def setStatus(self, msg, timeout=None):
290 self.parent().setStatus(msg, timeout)
292 def checkRunningProcess(self):
293 if self.process.isRunning():
294 self.setStatus("<font color='red'>There is already a process running. Please wait.</font>")
299 if self.checkRunningProcess():
302 rec = SfiData().getSliceRecord()
303 change = self.userModel.updateRecord(rec)
306 self.setStatus("<font color=red>No change in slice data. Not submitting!</font>", timeout=3000)
309 rec_file = config.getSliceRecordFile()
310 file(rec_file, "w").write(rec.save_to_string())
312 self.connect(self.process, SIGNAL('finished()'), self.submitFinished)
314 self.process.updateRecord(rec_file)
315 self.setStatus("Sending slice record. This will take some time...")
318 if not config.getSlice():
319 self.setStatus("<font color='red'>Slice not set yet!</font>")
322 if self.process.isRunning():
323 self.setStatus("<font color='red'>There is already a process running. Please wait.</font>")
326 self.connect(self.process, SIGNAL('finished()'), self.getSliceRecordFinished)
328 self.process.getSliceRecord()
329 self.setStatus("Refreshing slice record. This will take some time...")
331 def refreshAuthority(self):
332 self.connect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished)
334 self.process.listRecords(config.getAuthority(), None)
335 self.setStatus("Refreshing user records. This will take some time...")
337 def updateView(self):
338 sliceRec = SfiData().getSliceRecord()
341 # wait until we've resolved the slicerecord before displaying
342 # anything to the user.
343 self.userModel.clear()
345 self.userModel.updateModel(sliceRec)
347 self.userView.setModel(self.userModel)
348 self.userView.hideColumn(SERVER_MEMBERSHIP_STATUS_COLUMN)
349 self.userView.resizeColumnToContents(0)
351 def updateSliceName(self):
352 self.slicename.setText("Slice : %s" % (config.getSlice() or "None"))
357 class UserScreen(SfaScreen):
358 def __init__(self, parent):
359 SfaScreen.__init__(self, parent)
361 slice = UsersWidget(self)
362 self.init(slice, "Users", "OneLab SFA crawler")
364 def configurationChanged(self):
365 self.widget.updateSliceName()
366 self.widget.updateView()