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))
72 for x in user_status.keys():
73 if (user_status[x] == status_data) and (x in color_status):
74 color = color_status[x]
77 painter.fillPath(path, color)
78 painter.setPen(QColor.fromRgb(0, 0, 0))
79 painter.drawText(textRect, Qt.AlignVCenter, QString(data))
83 class UserView(QTableView):
84 def __init__(self, parent=None):
85 QTableView.__init__(self, parent)
87 self.setSelectionBehavior(QAbstractItemView.SelectRows)
88 self.setSelectionMode(QAbstractItemView.SingleSelection)
89 self.setAlternatingRowColors(True)
90 self.setAttribute(Qt.WA_MacShowFocusRect, 0)
91 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
92 self.setToolTip("Double click on a row to change its status.")
94 self.setItemDelegateForColumn(0, UserNameDelegate(self))
96 def keyPressEvent(self, event):
97 if (event.key() == Qt.Key_Space):
98 self.toggleSelection()
100 QTableView.keyPressEvent(self, event)
102 def mouseDoubleClickEvent(self, event):
103 self.toggleSelection()
105 def toggleSelection(self):
106 index = self.currentIndex()
107 model = index.model()
108 status_index = model.index(index.row(), MEMBERSHIP_STATUS_COLUMN, index.parent())
109 status_data = status_index.data().toString()
110 server_status_data = model.index(index.row(), SERVER_MEMBERSHIP_STATUS_COLUMN, index.parent()).data().toString()
111 node_index = model.index(index.row(), NAME_COLUMN, index.parent())
112 node_data = node_index.data().toString()
115 if status_data == user_status['in']:
116 model.setData(status_index, QString(user_status['remove']))
117 elif status_data == user_status['out']:
118 model.setData(status_index, QString(user_status['add']))
119 elif status_data in (user_status['add'], user_status['remove']):
120 if server_status_data == user_status["in"]:
121 model.setData(status_index, QString(user_status['in']))
123 model.setData(status_index, QString(user_status['out']))
125 model.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), node_index, node_index)
127 def currentChanged(self, current, previous):
128 model = current.model()
129 node_index = model.index(current.row(), 0, current.parent())
130 node_data = node_index.data().toString()
132 def hideUnusableColumns(self):
133 self.hideColumn(SERVER_MEMBERSHIP_STATUS_COLUMN)
135 class UserModel(QStandardItemModel):
136 def __init__(self, rows=0, columns=4, parent=None):
137 QStandardItemModel.__init__(self, rows, columns, parent)
139 def updateModel(self, sliceRec):
146 #for pi in sliceRec.get_field("PI", default=[]):
148 # if not name in added_persons:
149 # slice_persons.append({"name": name, "role": "PI", "member": user_status["in"]})
150 # added_persons.append(name)
152 for researcher in sliceRec.get_field("researcher", default=[]):
153 name = str(researcher)
154 if not name in added_persons:
155 slice_persons.append({"name": name, "role": "researcher", "member": user_status["in"]})
156 added_persons.append(name)
158 userNames = SfiData().getAuthorityHrns(type="user")
159 for name in userNames:
160 if not name in added_persons:
161 slice_persons.append({"name": name, "role": "", "member": user_status["out"]})
162 added_persons.append(name)
164 rootItem = self.invisibleRootItem()
166 for person in slice_persons:
167 rootItem.appendRow([self.readOnlyItem(person["name"]),
168 self.readOnlyItem(person["member"]),
169 self.readOnlyItem(person["member"])])
171 headers = QStringList() << "User Name" << "Status" << "ServerStatus"
172 self.setHorizontalHeaderLabels(headers)
174 def readOnlyItem(self, x):
175 item = QStandardItem(QString(x))
176 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
179 def updateRecord(self, slicerec):
182 item = self.invisibleRootItem()
183 children = item.rowCount()
184 for row in range(0, children):
185 childName = str(item.child(row, NAME_COLUMN).data(Qt.DisplayRole).toString())
186 childStatus = str(item.child(row, MEMBERSHIP_STATUS_COLUMN).data(Qt.DisplayRole).toString())
188 if (childStatus == user_status['add']):
189 researcher = slicerec.get_field("researcher", [])
190 researcher.append(childName)
191 slicerec["researcher"] = researcher
193 elif (childStatus == user_status['remove']):
194 if childName in slicerec.get_field("PI"):
195 slicerec.get_field("PI").remove(childName)
196 if childName in slicerec.get_field("researcher"):
197 slicerec.get_field("researcher").remove(childName)
202 def getResearchers(self):
204 item = self.invisibleRootItem()
205 children = item.rowCount()
206 for row in range(0, children):
207 childName = str(item.child(row, NAME_COLUMN).data(Qt.DisplayRole).toString())
208 childStatus = str(item.child(row, MEMBERSHIP_STATUS_COLUMN).data(Qt.DisplayRole).toString())
210 if (childStatus == user_status['add']) or (childStatus == user_status['in']):
211 researchers.append(childName)
215 class UsersWidget(QWidget):
216 def __init__(self, parent):
217 QWidget.__init__(self, parent)
219 self.process = SfiProcess(self)
221 self.slicename = QLabel("", self)
222 self.updateSliceName()
223 self.slicename.setScaledContents(False)
224 searchlabel = QLabel ("Search: ", self)
225 searchlabel.setScaledContents(False)
226 searchbox = QLineEdit(self)
227 searchbox.setAttribute(Qt.WA_MacShowFocusRect, 0)
229 toplayout = QHBoxLayout()
230 toplayout.addWidget(self.slicename, 0, Qt.AlignLeft)
231 toplayout.addStretch()
232 toplayout.addWidget(searchlabel, 0, Qt.AlignRight)
233 toplayout.addWidget(searchbox, 0, Qt.AlignRight)
235 self.userView = UserView()
237 refresh = QPushButton("Refresh", self)
238 refresh.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
239 submit = QPushButton("Submit", self)
240 submit.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
242 bottomlayout = QHBoxLayout()
243 bottomlayout.addWidget(refresh, 0, Qt.AlignLeft)
244 bottomlayout.addStretch()
245 bottomlayout.addWidget(submit, 0, Qt.AlignRight)
247 layout = QVBoxLayout()
248 layout.addLayout(toplayout)
249 layout.addWidget(self.userView)
250 layout.addLayout(bottomlayout)
251 self.setLayout(layout)
252 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
254 self.userModel = UserModel(parent=self)
256 self.connect(refresh, SIGNAL('clicked()'), self.refresh)
257 self.connect(submit, SIGNAL('clicked()'), self.submit)
261 def submitFinished(self):
262 self.disconnect(self.process, SIGNAL('finished()'), self.submitFinished)
264 faultString = self.process.getFaultString()
266 self.setStatus("<font color='green'>Slice user data submitted.</font>")
267 QTimer.singleShot(1000, self.refresh)
269 self.setStatus("<font color='red'>Slice user submit failed: %s</font>" % (faultString))
271 def getSliceRecordFinished(self):
272 self.disconnect(self.process, SIGNAL('finished()'), self.getSliceRecordFinished)
274 faultString = self.process.getFaultString()
276 self.setStatus("<font color='green'>Slice record refreshed.</font>")
277 self.refreshAuthority()
279 self.setStatus("<font color='red'>Slice rec refresh error: %s</font>" % (faultString))
281 def getAuthorityRecordFinished(self):
282 self.disconnect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished)
284 faultString = self.process.getFaultString()
286 self.setStatus("<font color='green'>User data refreshed.</font>")
288 #self.parent().signalAll("usersUpdated")
290 self.setStatus("<font color='red'>Authority rec refresh error: %s</font>" % (faultString))
292 def setStatus(self, msg, timeout=None):
293 self.parent().setStatus(msg, timeout)
295 def checkRunningProcess(self):
296 if self.process.isRunning():
297 self.setStatus("<font color='red'>There is already a process running. Please wait.</font>")
302 if self.checkRunningProcess():
305 rec = SfiData().getSliceRecord()
306 change = self.userModel.updateRecord(rec)
309 self.setStatus("<font color=red>No change in slice data. Not submitting!</font>", timeout=3000)
312 rec_file = config.getSliceRecordFile()
313 file(rec_file, "w").write(rec.save_to_string())
315 self.connect(self.process, SIGNAL('finished()'), self.submitFinished)
317 self.process.updateRecord(rec_file)
318 self.setStatus("Sending slice record. This will take some time...")
321 if not config.getSlice():
322 self.setStatus("<font color='red'>Slice not set yet!</font>")
325 if self.process.isRunning():
326 self.setStatus("<font color='red'>There is already a process running. Please wait.</font>")
329 self.connect(self.process, SIGNAL('finished()'), self.getSliceRecordFinished)
331 self.process.getSliceRecord()
332 self.setStatus("Refreshing slice record. This will take some time...")
334 def refreshAuthority(self):
335 self.connect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished)
337 self.process.listRecords(config.getAuthority(), None)
338 self.setStatus("Refreshing user records. This will take some time...")
340 def updateView(self):
341 sliceRec = SfiData().getSliceRecord()
344 # wait until we've resolved the slicerecord before displaying
345 # anything to the user.
346 self.userModel.clear()
348 self.userModel.updateModel(sliceRec)
350 self.userView.setModel(self.userModel)
351 self.userView.hideColumn(SERVER_MEMBERSHIP_STATUS_COLUMN)
352 self.userView.resizeColumnToContents(0)
354 def updateSliceName(self):
355 self.slicename.setText("Slice : %s" % (config.getSlice() or "None"))
360 class UserScreen(SfaScreen):
361 def __init__(self, parent):
362 SfaScreen.__init__(self, parent)
364 slice = UsersWidget(self)
365 self.init(slice, "Users", "OneLab SFA crawler")
367 def configurationChanged(self):
368 self.widget.updateSliceName()
369 self.widget.updateView()