53c279fb1a46429f3dea2741777565cc8831a56c
[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 = 2
16 SERVER_MEMBERSHIP_STATUS_COLUMN = 3
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"):
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         for researcher in sliceRec.get_field("researcher"):
228             name = str(researcher)
229             if not name in added_persons:
230                  slice_persons.append({"name": name, "role": "researcher", "member": user_status["in"]})
231                  added_persons.append(name)
232
233         i=1
234         while (os.path.exists(config.getAuthorityListFile(i))):
235             rec = self.readUserRecord(i)
236             if rec:
237                 name = str(rec.get_name())
238                 if not name in added_persons:
239                     slice_persons.append({"name": name, "role": "", "member": user_status["out"]})
240                     added_persons.append(name)
241             i=i+1
242
243         rootItem = self.userModel.invisibleRootItem()
244
245         for person in slice_persons:
246             rootItem.appendRow([QStandardItem(QString(person["name"])),
247                                QStandardItem(QString(person["role"])),
248                                QStandardItem(QString(person["member"])),
249                                QStandardItem(QString(person["member"]))])
250
251         headers = QStringList() << "User Name" << "Role" << "Status" << "ServerStatus"
252         self.userModel.setHorizontalHeaderLabels(headers)
253
254     def updateView(self):
255         self.updateModel()
256
257         self.userView.setModel(self.userModel)
258         self.userView.hideColumn(SERVER_MEMBERSHIP_STATUS_COLUMN)
259         self.userView.resizeColumnToContents(0)
260
261     def updateSliceName(self):
262         self.slicename.setText("Slice : %s" % (config.getSlice() or "None"))
263
264     def nodeSelectionChanged(self, hostname):
265         self.parent().nodeSelectionChanged(hostname)
266
267     def process_table(self, slicerec):
268         change = False
269
270         item = self.userModel.invisibleRootItem()
271         children = item.rowCount()
272         for row in range(0, children):
273             childName = str(item.child(row, NAME_COLUMN).data(Qt.DisplayRole).toString())
274             childStatus = str(item.child(row, MEMBERSHIP_STATUS_COLUMN).data(Qt.DisplayRole).toString())
275
276             if (childStatus == user_status['add']):
277                 slicerec.get_field("researcher").append(childName)
278                 change = True
279             elif (childStatus == user_status['remove']):
280                 if childName in slicerec.get_field("PI"):
281                      slicerec.get_field("PI").remove(childName)
282                 if childName in slicerec.get_field("researcher"):
283                      slicerec.get_field("researcher").remove(childName)
284                 change = True
285
286         print "XXX", slicerec.get_field("researcher")
287         return change
288
289
290 class MainScreen(SfaScreen):
291     def __init__(self, parent):
292         SfaScreen.__init__(self, parent)
293
294         slice = UsersWidget(self)
295         self.init(slice, "Users", "OneLab SFA crawler")
296
297     def configurationChanged(self):
298         self.widget.updateSliceName()
299         self.widget.updateView()
300
301