6 import util.coroutines as coroutines
9 _moduleLogger = logging.getLogger("gvoice.conversations")
12 class Conversations(object):
14 def __init__(self, backend):
15 self._backend = backend
16 self._conversations = {}
18 self.updateSignalHandler = coroutines.CoTee()
20 def update(self, force=False):
21 if not force and self._conversations:
24 oldConversationIds = set(self._conversations.iterkeys())
26 updateConversationIds = set()
27 conversations = list(self._backend.get_conversations())
29 for conversation in conversations:
30 key = conversation.contactId, conversation.number
32 mergedConversations = self._conversations[key]
34 mergedConversations = MergedConversations()
35 self._conversations[key] = mergedConversations
38 mergedConversations.append_conversation(conversation)
39 isConversationUpdated = True
40 except RuntimeError, e:
42 _moduleLogger.info("Skipping conversation for %r because '%s'" % (key, e))
43 isConversationUpdated = False
45 if isConversationUpdated:
46 updateConversationIds.add(key)
48 if updateConversationIds:
49 message = (self, updateConversationIds, )
50 self.updateSignalHandler.stage.send(message)
52 def get_conversations(self):
53 return self._conversations.iterkeys()
55 def get_conversation(self, key):
56 return self._conversations[key]
58 def clear_conversation(self, key):
60 del self._conversations[key]
62 _moduleLogger.info("Conversation never existed for %r" % (key,))
65 self._conversations.clear()
68 class MergedConversations(object):
71 self._conversations = []
73 def append_conversation(self, newConversation):
74 self._validate(newConversation)
75 for similarConversation in self._find_related_conversation(newConversation.id):
76 self._update_previous_related_conversation(similarConversation, newConversation)
77 self._remove_repeats(similarConversation, newConversation)
78 self._conversations.append(newConversation)
81 def conversations(self):
82 return self._conversations
84 def _validate(self, newConversation):
85 if not self._conversations:
88 for constantField in ("contactId", "number"):
89 assert getattr(self._conversations[0], constantField) == getattr(newConversation, constantField), "Constant field changed, soemthing is seriously messed up: %r v %r" % (
90 getattr(self._conversations[0], constantField),
91 getattr(newConversation, constantField),
94 if newConversation.time <= self._conversations[-1].time:
95 raise RuntimeError("Conversations got out of order")
97 def _find_related_conversation(self, convId):
98 similarConversations = (
100 for conversation in self._conversations
101 if conversation.id == convId
103 return similarConversations
105 def _update_previous_related_conversation(self, relatedConversation, newConversation):
106 for commonField in ("isRead", "isSpam", "isTrash", "isArchived"):
107 newValue = getattr(newConversation, commonField)
108 setattr(relatedConversation, commonField, newValue)
110 def _remove_repeats(self, relatedConversation, newConversation):
111 newConversationMessages = newConversation.messages
112 newConversation.messages = [
114 for newMessage in newConversationMessages
115 if newMessage not in relatedConversation.messages
117 _moduleLogger.info("Found %d new messages in conversation %s (%d/%d)" % (
118 len(newConversationMessages) - len(newConversation.messages),
120 len(newConversation.messages),
121 len(newConversationMessages),
123 assert 0 < len(newConversation.messages), "Everything shouldn't have been removed"