5 from PyQt4 import QtCore
7 from util import qore_utils
8 from util import concurrent
10 from backends import gv_backend
13 _moduleLogger = logging.getLogger(__name__)
16 class _DraftContact(object):
18 def __init__(self, title, description, numbersWithDescriptions):
20 self.description = description
21 self.numbers = numbersWithDescriptions
22 self.selectedNumber = numbersWithDescriptions[0][0]
25 class Draft(QtCore.QObject):
27 sendingMessage = QtCore.pyqtSignal()
28 sentMessage = QtCore.pyqtSignal()
29 calling = QtCore.pyqtSignal()
30 called = QtCore.pyqtSignal()
31 cancelling = QtCore.pyqtSignal()
32 cancelled = QtCore.pyqtSignal()
33 error = QtCore.pyqtSignal(str)
35 recipientsChanged = QtCore.pyqtSignal()
37 def __init__(self, pool):
38 QtCore.QObject.__init__(self)
43 assert 0 < len(self._contacts)
44 le = concurrent.AsyncLinearExecution(self._pool, self._send)
48 assert len(self._contacts) == 1
49 le = concurrent.AsyncLinearExecution(self._pool, self._call)
53 le = concurrent.AsyncLinearExecution(self._pool, self._cancel)
56 def add_contact(self, contactId, title, description, numbersWithDescriptions):
57 assert contactId not in self._contacts
58 contactDetails = _DraftContact(title, description, numbersWithDescriptions)
59 self._contacts[contactId] = contactDetails
60 self.recipientsChanged.emit()
62 def remove_contact(self, contactId):
63 assert contactId in self._contacts
64 del self._contacts[contactId]
65 self.recipientsChanged.emit()
67 def get_contacts(self):
68 return self._contacts.iterkeys()
70 def get_num_contacts(self):
71 return len(self._contacts)
73 def get_title(self, cid):
74 return self._contacts[cid].title
76 def get_description(self, cid):
77 return self._contacts[cid].description
79 def get_numbers(self, cid):
80 return self._contacts[cid].numbers
82 def get_selected_number(self, cid):
83 return self._contacts[cid].selectedNumber
85 def set_selected_number(self, cid, number):
86 # @note I'm lazy, this isn't firing any kind of signal since only one
87 # controller right now and that is the viewer
88 return self._contacts[cid].numbers
91 oldContacts = self._contacts
94 self.recipientsChanged.emit()
96 def _send(self, text):
97 self.sendingMessage.emit()
99 self.error.emit("Not Implemented")
100 self.sentMessage.emit()
103 self.error.emit(str(e))
108 self.error.emit("Not Implemented")
112 self.error.emit(str(e))
115 self.cancelling.emit()
118 self._backend.cancel,
122 self.cancelled.emit()
124 self.error.emit(str(e))
127 class Session(QtCore.QObject):
129 stateChange = QtCore.pyqtSignal(str)
130 loggedOut = QtCore.pyqtSignal()
131 loggedIn = QtCore.pyqtSignal()
132 callbackNumberChanged = QtCore.pyqtSignal(str)
134 contactsUpdated = QtCore.pyqtSignal()
135 messagesUpdated = QtCore.pyqtSignal()
136 historyUpdated = QtCore.pyqtSignal()
137 dndStateChange = QtCore.pyqtSignal(bool)
139 error = QtCore.pyqtSignal(str)
141 LOGGEDOUT_STATE = "logged out"
142 LOGGINGIN_STATE = "logging in"
143 LOGGEDIN_STATE = "logged in"
148 def __init__(self, cachePath = None):
149 QtCore.QObject.__init__(self)
150 self._pool = qore_utils.AsyncPool()
152 self._loggedInTime = self._LOGGEDOUT_TIME
154 self._cachePath = cachePath
155 self._username = None
156 self._draft = Draft(self._pool)
167 self._LOGGEDOUT_TIME: self.LOGGEDOUT_STATE,
168 self._LOGGINGIN_TIME: self.LOGGINGIN_STATE,
169 }.get(self._loggedInTime, self.LOGGEDIN_STATE)
175 def login(self, username, password):
176 assert self.state == self.LOGGEDOUT_STATE
177 assert username != ""
178 if self._cachePath is not None:
179 cookiePath = os.path.join(self._cachePath, "%s.cookies" % username)
183 if self._username != username or self._backend is None:
184 self._backend = gv_backend.GVDialer(cookiePath)
187 le = concurrent.AsyncLinearExecution(self._pool, self._login)
188 le.start(username, password)
191 assert self.state != self.LOGGEDOUT_STATE
193 self._loggedInTime = self._LOGGEDOUT_TIME
194 self._backend.persist()
195 self._save_to_cache()
198 assert self.state == self.LOGGEDOUT_STATE
199 self._backend.logout()
204 def logout_and_clear(self):
205 assert self.state != self.LOGGEDOUT_STATE
207 self._loggedInTime = self._LOGGEDOUT_TIME
210 def update_contacts(self):
211 le = concurrent.AsyncLinearExecution(self._pool, self._update_contacts)
212 self._perform_op_while_loggedin(le)
214 def get_contacts(self):
215 return self._contacts
217 def update_messages(self):
218 le = concurrent.AsyncLinearExecution(self._pool, self._update_messages)
219 self._perform_op_while_loggedin(le)
221 def get_messages(self):
222 return self._messages
224 def update_history(self):
225 le = concurrent.AsyncLinearExecution(self._pool, self._update_history)
226 self._perform_op_while_loggedin(le)
228 def get_history(self):
231 def update_dnd(self):
232 le = concurrent.AsyncLinearExecution(self._pool, self._update_dnd)
233 self._perform_op_while_loggedin(le)
235 def set_dnd(self, dnd):
236 # I'm paranoid about our state geting out of sync so we set no matter
237 # what but act as if we have the cannonical state
238 assert self.state == self.LOGGEDIN_STATE
242 self._backend.set_dnd,
247 self.error.emit(str(e))
250 if oldDnd != self._dnd:
251 self.dndStateChange.emit(self._dnd)
256 def get_callback_numbers(self):
257 # @todo Remove evilness
258 return self._backend.get_callback_numbers()
260 def get_callback_number(self):
261 return self._callback
263 def set_callback_number(self, callback):
264 # I'm paranoid about our state geting out of sync so we set no matter
265 # what but act as if we have the cannonical state
266 assert self.state == self.LOGGEDIN_STATE
267 oldCallback = self._callback
270 self._backend.set_callback_number,
275 self.error.emit(str(e))
277 self._callback = callback
278 if oldCallback != self._callback:
279 self.callbackNumberChanged.emit(self._callback)
281 def _login(self, username, password):
282 self._loggedInTime = self._LOGGINGIN_TIME
283 self.stateChange.emit(self.LOGGINGIN_STATE)
284 finalState = self.LOGGEDOUT_STATE
288 if not isLoggedIn and self._backend.is_quick_login_possible():
290 self._backend.is_authed,
295 _moduleLogger.info("Logged in through cookies")
297 # Force a clearing of the cookies
299 self._backend.logout,
307 (username, password),
311 _moduleLogger.info("Logged in through credentials")
314 self._loggedInTime = int(time.time())
315 oldUsername = self._username
316 self._username = username
317 finalState = self.LOGGEDIN_STATE
319 if oldUsername != self._username:
320 self._load_from_cache()
321 loginOps = self._loginOps[:]
322 del self._loginOps[:]
323 for asyncOp in loginOps:
326 self.error.emit(str(e))
328 self.stateChange.emit(finalState)
330 def _load_from_cache(self):
331 updateContacts = len(self._contacts) != 0
332 updateMessages = len(self._messages) != 0
333 updateHistory = len(self._history) != 0
335 oldCallback = self._callback
344 self.contactsUpdated.emit()
346 self.messagesUpdated.emit()
348 self.historyUpdated.emit()
349 if oldDnd != self._dnd:
350 self.dndStateChange.emit(self._dnd)
351 if oldCallback != self._callback:
352 self.callbackNumberChanged.emit(self._callback)
354 def _save_to_cache(self):
358 def _clear_cache(self):
359 updateContacts = len(self._contacts) != 0
360 updateMessages = len(self._messages) != 0
361 updateHistory = len(self._history) != 0
363 oldCallback = self._callback
372 self.contactsUpdated.emit()
374 self.messagesUpdated.emit()
376 self.historyUpdated.emit()
377 if oldDnd != self._dnd:
378 self.dndStateChange.emit(self._dnd)
379 if oldCallback != self._callback:
380 self.callbackNumberChanged.emit(self._callback)
382 self._save_to_cache()
384 def _update_contacts(self):
386 self._contacts = yield (
387 self._backend.get_contacts,
392 self.error.emit(str(e))
394 self.contactsUpdated.emit()
396 def _update_messages(self):
398 self._messages = yield (
399 self._backend.get_messages,
404 self.error.emit(str(e))
406 self.messagesUpdated.emit()
408 def _update_history(self):
410 self._history = yield (
411 self._backend.get_recent,
416 self.error.emit(str(e))
418 self.historyUpdated.emit()
420 def _update_dnd(self):
424 self._backend.is_dnd,
429 self.error.emit(str(e))
431 if oldDnd != self._dnd:
432 self.dndStateChange(self._dnd)
434 def _perform_op_while_loggedin(self, op):
435 if self.state == self.LOGGEDIN_STATE:
438 self._push_login_op(op)
440 def _push_login_op(self, asyncOp):
441 assert self.state != self.LOGGEDIN_STATE
442 if asyncOp in self._loginOps:
443 _moduleLogger.info("Skipping queueing duplicate op: %r" % asyncOp)
445 self._loginOps.append(asyncOp)