6 import util.coroutines as coroutines
7 import util.misc as util_misc
10 _moduleLogger = logging.getLogger("gvoice.conversations")
13 class Conversations(object):
15 def __init__(self, getter):
16 self._get_raw_conversations = getter
17 self._conversations = {}
19 self.updateSignalHandler = coroutines.CoTee()
21 def update(self, force=False):
22 if not force and self._conversations:
25 oldConversationIds = set(self._conversations.iterkeys())
27 updateConversationIds = set()
28 conversations = list(self._get_raw_conversations())
30 for conversation in conversations:
31 key = conversation.contactId, util_misc.strip_number(conversation.number)
33 mergedConversations = self._conversations[key]
35 mergedConversations = MergedConversations()
36 self._conversations[key] = mergedConversations
39 mergedConversations.append_conversation(conversation)
40 isConversationUpdated = True
41 except RuntimeError, e:
43 _moduleLogger.info("Skipping conversation for %r because '%s'" % (key, e))
44 isConversationUpdated = False
46 if isConversationUpdated:
47 updateConversationIds.add(key)
49 if updateConversationIds:
50 message = (self, updateConversationIds, )
51 self.updateSignalHandler.stage.send(message)
53 def get_conversations(self):
54 return self._conversations.iterkeys()
56 def get_conversation(self, key):
57 return self._conversations[key]
59 def clear_conversation(self, key):
61 del self._conversations[key]
63 _moduleLogger.info("Conversation never existed for %r" % (key,))
66 self._conversations.clear()
69 class MergedConversations(object):
72 self._conversations = []
74 def append_conversation(self, newConversation):
75 self._validate(newConversation)
76 for similarConversation in self._find_related_conversation(newConversation.id):
77 self._update_previous_related_conversation(similarConversation, newConversation)
78 self._remove_repeats(similarConversation, newConversation)
79 self._conversations.append(newConversation)
82 def conversations(self):
83 return self._conversations
85 def _validate(self, newConversation):
86 if not self._conversations:
89 for constantField in ("contactId", "number"):
90 assert getattr(self._conversations[0], constantField) == getattr(newConversation, constantField), "Constant field changed, soemthing is seriously messed up: %r v %r" % (
91 getattr(self._conversations[0], constantField),
92 getattr(newConversation, constantField),
95 if newConversation.time <= self._conversations[-1].time:
96 raise RuntimeError("Conversations got out of order")
98 def _find_related_conversation(self, convId):
99 similarConversations = (
101 for conversation in self._conversations
102 if conversation.id == convId
104 return similarConversations
106 def _update_previous_related_conversation(self, relatedConversation, newConversation):
107 for commonField in ("isRead", "isSpam", "isTrash", "isArchived"):
108 newValue = getattr(newConversation, commonField)
109 setattr(relatedConversation, commonField, newValue)
111 def _remove_repeats(self, relatedConversation, newConversation):
112 newConversationMessages = newConversation.messages
113 newConversation.messages = [
115 for newMessage in newConversationMessages
116 if newMessage not in relatedConversation.messages
118 _moduleLogger.info("Found %d new messages in conversation %s (%d/%d)" % (
119 len(newConversationMessages) - len(newConversation.messages),
121 len(newConversation.messages),
122 len(newConversationMessages),
124 assert 0 < len(newConversation.messages), "Everything shouldn't have been removed"