9adf116258fbbf7339d22aa4c48c76a2056d6492
[sface.git] / sface / screens / userscreen.py
1
2 import datetime
3 import os
4 import pickle
5 from PyQt4.QtCore import *
6 from PyQt4.QtGui import *
7
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
13 NAME_COLUMN = 0
14 #ROLE_COLUMN = 1
15 MEMBERSHIP_STATUS_COLUMN = 1
16 SERVER_MEMBERSHIP_STATUS_COLUMN = 2
17
18 user_status = { "in": "Already Selected",
19                 "out": "Not Selected",
20                 "add": "To be Added",
21                 "remove": "To be Removed"}
22
23 class UserView(QTableView):
24     def __init__(self, parent=None):
25         QTableView.__init__(self, parent)
26
27         self.setSelectionBehavior(QAbstractItemView.SelectRows)
28         self.setSelectionMode(QAbstractItemView.SingleSelection)
29         self.setAlternatingRowColors(True)
30         self.setAttribute(Qt.WA_MacShowFocusRect, 0)
31         self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
32         self.setToolTip("Double click on a row to change its status.")
33
34     def mouseDoubleClickEvent(self, event):
35         index = self.currentIndex()
36         model = index.model()
37         status_index = model.index(index.row(), MEMBERSHIP_STATUS_COLUMN, index.parent())
38         status_data = status_index.data().toString()
39         server_status_data = model.index(index.row(), SERVER_MEMBERSHIP_STATUS_COLUMN, index.parent()).data().toString()
40         node_index = model.index(index.row(), NAME_COLUMN, index.parent())
41         node_data = node_index.data().toString()
42
43         # This is a hostname
44         if status_data == user_status['in']:
45             model.setData(status_index, QString(user_status['remove']))
46         elif status_data == user_status['out']:
47             model.setData(status_index, QString(user_status['add']))
48         elif status_data in (user_status['add'], user_status['remove']):
49             if server_status_data == user_status["in"]:
50                 model.setData(status_index, QString(user_status['in']))
51             else:
52                 model.setData(status_index, QString(user_status['out']))
53
54         model.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), node_index, node_index)
55
56     def currentChanged(self, current, previous):
57         model = current.model()
58         node_index = model.index(current.row(), 0, current.parent())
59         node_data = node_index.data().toString()
60         self.emit(SIGNAL('hostnameClicked(QString)'), node_data)
61
62
63 class UsersWidget(QWidget):
64     def __init__(self, parent):
65         QWidget.__init__(self, parent)
66
67         self.process = SfiProcess(self)
68
69         self.slicename = QLabel("", self)
70         self.updateSliceName()
71         self.slicename.setScaledContents(False)
72         searchlabel = QLabel ("Search: ", self)
73         searchlabel.setScaledContents(False)
74         searchbox = QLineEdit(self)
75         searchbox.setAttribute(Qt.WA_MacShowFocusRect, 0)
76
77         toplayout = QHBoxLayout()
78         toplayout.addWidget(self.slicename, 0, Qt.AlignLeft)
79         toplayout.addStretch()
80         toplayout.addWidget(searchlabel, 0, Qt.AlignRight)
81         toplayout.addWidget(searchbox, 0, Qt.AlignRight)
82
83         self.userView = UserView()
84         #self.userView.setSelectionBehavior(QAbstractItemView.SelectRows)
85         #self.userView.setSelectionMode(QAbstractItemView.SingleSelection)
86
87         refresh = QPushButton("Refresh", self)
88         refresh.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
89         submit = QPushButton("Submit", self)
90         submit.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
91
92         bottomlayout = QHBoxLayout()
93         bottomlayout.addWidget(refresh, 0, Qt.AlignLeft)
94         bottomlayout.addStretch()
95         bottomlayout.addWidget(submit, 0, Qt.AlignRight)
96
97         layout = QVBoxLayout()
98         layout.addLayout(toplayout)
99         layout.addWidget(self.userView)
100         layout.addLayout(bottomlayout)
101         self.setLayout(layout)
102         self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
103
104         self.userModel = QStandardItemModel(0, 4, self)
105
106         self.connect(refresh, SIGNAL('clicked()'), self.refresh)
107         self.connect(submit, SIGNAL('clicked()'), self.submit)
108
109         self.updateView()
110
111     def submitFinished(self):
112         self.setStatus("<font color='green'>Slice data submitted.</font>")
113         QTimer.singleShot(1000, self.refresh)
114
115     def getSliceRecordFinished(self):
116         self.setStatus("<font color='green'>Authority data refreshed.</font>", timeout=5000)
117         self.refreshAuthority()
118
119     def getAuthorityRecordFinished(self):
120         self.setStatus("<font color='green'>Slice data refreshed.</font>", timeout=5000)
121         self.updateView()
122         #self.parent().signalAll("usersUpdated")
123
124     def readSliceRecord(self):
125         rec_file = config.getSliceRecordFile()
126         if os.path.exists(rec_file):
127             xml = open(rec_file).read()
128             rec = SliceRecord()
129             rec.load_from_string(xml)
130             return rec
131         return None
132
133     def readAuthorityRecord(self):
134         rec_file = config.getAuthorityRecordFile()
135         if os.path.exists(rec_file):
136             xml = open(rec_file).read()
137             rec = AuthorityRecord()
138             rec.load_from_string(xml)
139             return rec
140         return None
141
142     def readUserRecord(self, i):
143         rec_file = config.getAuthorityListFile(i)
144         if os.path.exists(rec_file):
145             xml = open(rec_file).read()
146             rec = UserRecord()
147             rec.load_from_string(xml)
148             return rec
149         return None
150
151     def setStatus(self, msg, timeout=None):
152         self.parent().setStatus(msg, timeout)
153
154     def checkRunningProcess(self):
155         if self.process.isRunning():
156             self.setStatus("<font color='red'>There is already a process running. Please wait.</font>")
157             return True
158         return False
159
160     def submit(self):
161         if self.checkRunningProcess():
162             return
163
164         rec = self.readSliceRecord()
165         change = self.process_table(rec)
166
167         if not change:
168             self.setStatus("<font color=red>No change in slice data. Not submitting!</font>", timeout=3000)
169             return
170
171         rec_file = config.getSliceRecordFile()
172         file(rec_file, "w").write(rec.save_to_string())
173
174         self.disconnect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished)
175         self.connect(self.process, SIGNAL('finished()'), self.submitFinished)
176
177         self.process.updateRecord(rec_file)
178         self.setStatus("Sending slice record. This will take some time...")
179
180     def refresh(self):
181         if not config.getSlice():
182             self.setStatus("<font color='red'>Slice not set yet!</font>")
183             return
184
185         if self.process.isRunning():
186             self.setStatus("<font color='red'>There is already a process running. Please wait.</font>")
187             return
188
189         self.disconnect(self.process, SIGNAL('finished()'), self.submitFinished)
190         self.connect(self.process, SIGNAL('finished()'), self.getSliceRecordFinished)
191
192         self.process.getSliceRecord()
193         self.setStatus("Refreshing slice record. This will take some time...")
194
195     def refreshAuthority(self):
196         self.disconnect(self.process, SIGNAL('finished()'), self.getSliceRecordFinished)
197         self.connect(self.process, SIGNAL('finished()'), self.getAuthorityRecordFinished)
198
199         self.process.listRecords(config.getAuthority(), "user", config.getAuthorityListFile())
200         self.setStatus("Refreshing user records. This will take some time...")
201
202     def addTableItem(self, table, row, col, val, data=None, readonly=True):
203         item = QTableWidgetItem(str(val))
204         if readonly:
205             item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
206         if data:
207             if not isinstance(data, str):
208                data = pickle.dumps(data)
209             item.setData(Qt.UserRole, QVariant(data))
210         table.setItem(row, col, item)
211
212     def updateModel(self):
213         self.userModel.clear()
214
215         sliceRec = self.readSliceRecord()
216         if not sliceRec:
217             return None
218
219         added_persons = []
220         slice_persons = []
221
222         #for pi in sliceRec.get_field("PI", default=[]):
223         #    name = str(pi)
224         #    if not name in added_persons:
225         #         slice_persons.append({"name": name, "role": "PI", "member": user_status["in"]})
226         #         added_persons.append(name)
227
228         for researcher in sliceRec.get_field("researcher", default=[]):
229             name = str(researcher)
230             if not name in added_persons:
231                  slice_persons.append({"name": name, "role": "researcher", "member": user_status["in"]})
232                  added_persons.append(name)
233
234         i=1
235         while (os.path.exists(config.getAuthorityListFile(i))):
236             rec = self.readUserRecord(i)
237             if rec:
238                 name = str(rec.get_name())
239                 if not name in added_persons:
240                     slice_persons.append({"name": name, "role": "", "member": user_status["out"]})
241                     added_persons.append(name)
242             i=i+1
243
244         rootItem = self.userModel.invisibleRootItem()
245
246         for person in slice_persons:
247             rootItem.appendRow([QStandardItem(QString(person["name"])),
248                                #QStandardItem(QString(person["role"])),
249                                QStandardItem(QString(person["member"])),
250                                QStandardItem(QString(person["member"]))])
251
252         headers = QStringList() << "User Name" << "Status" << "ServerStatus"
253         self.userModel.setHorizontalHeaderLabels(headers)
254
255     def updateView(self):
256         self.updateModel()
257
258         self.userView.setModel(self.userModel)
259         self.userView.hideColumn(SERVER_MEMBERSHIP_STATUS_COLUMN)
260         self.userView.resizeColumnToContents(0)
261
262     def updateSliceName(self):
263         self.slicename.setText("Slice : %s" % (config.getSlice() or "None"))
264
265     def nodeSelectionChanged(self, hostname):
266         self.parent().nodeSelectionChanged(hostname)
267
268     def process_table(self, slicerec):
269         change = False
270
271         item = self.userModel.invisibleRootItem()
272         children = item.rowCount()
273         for row in range(0, children):
274             childName = str(item.child(row, NAME_COLUMN).data(Qt.DisplayRole).toString())
275             childStatus = str(item.child(row, MEMBERSHIP_STATUS_COLUMN).data(Qt.DisplayRole).toString())
276
277             if (childStatus == user_status['add']):
278                 researcher = slicerec.get_field("researcher", [])
279                 researcher.append(childName)
280                 slicerec["researcher"] = researcher
281                 change = True
282             elif (childStatus == user_status['remove']):
283                 if childName in slicerec.get_field("PI"):
284                      slicerec.get_field("PI").remove(childName)
285                 if childName in slicerec.get_field("researcher"):
286                      slicerec.get_field("researcher").remove(childName)
287                 change = True
288
289         return change
290
291
292 class UserScreen(SfaScreen):
293     def __init__(self, parent):
294         SfaScreen.__init__(self, parent)
295
296         slice = UsersWidget(self)
297         self.init(slice, "Users", "OneLab SFA crawler")
298
299     def configurationChanged(self):
300         self.widget.updateSliceName()
301         self.widget.updateView()
302
303