3 from __future__ import with_statement
14 import util.coroutines as coroutines
15 import util.misc as util_misc
18 _moduleLogger = logging.getLogger("gvoice.conversations")
21 class Conversations(object):
23 def __init__(self, getter):
24 self._get_raw_conversations = getter
25 self._conversations = {}
27 self.updateSignalHandler = coroutines.CoTee()
31 return repr(self._get_raw_conversations.__name__)
34 assert not self._conversations
36 with open(path, "rb") as f:
37 fileVersion, fileBuild, convs = pickle.load(f)
38 except (pickle.PickleError, IOError):
39 _moduleLogger.exception("While loading for %s" % self._name)
42 if fileVersion == constants.__version__ and fileBuild == constants.__build__:
43 self._conversations = convs
46 "%s Skipping cache due to version mismatch (%s-%s)" % (self._name, fileVersion, fileBuild)
51 dataToDump = (constants.__version__, constants.__build__, self._conversations)
52 with open(path, "wb") as f:
53 pickle.dump(dataToDump, f, pickle.HIGHEST_PROTOCOL)
54 except (pickle.PickleError, IOError):
55 _moduleLogger.exception("While saving for %s" % self._name)
57 def update(self, force=False):
58 if not force and self._conversations:
61 oldConversationIds = set(self._conversations.iterkeys())
63 updateConversationIds = set()
64 conversations = list(self._get_raw_conversations())
66 for conversation in conversations:
67 key = util_misc.normalize_number(conversation.number)
69 mergedConversations = self._conversations[key]
71 mergedConversations = MergedConversations()
72 self._conversations[key] = mergedConversations
75 mergedConversations.append_conversation(conversation)
76 isConversationUpdated = True
77 except RuntimeError, e:
79 _moduleLogger.debug("%s Skipping conversation for %r because '%s'" % (self._name, key, e))
80 isConversationUpdated = False
82 if isConversationUpdated:
83 updateConversationIds.add(key)
85 if updateConversationIds:
86 message = (self, updateConversationIds, )
87 self.updateSignalHandler.stage.send(message)
89 def get_conversations(self):
90 return self._conversations.iterkeys()
92 def get_conversation(self, key):
93 return self._conversations[key]
95 def clear_conversation(self, key):
97 del self._conversations[key]
99 _moduleLogger.info("%s Conversation never existed for %r" % (self._name, key, ))
102 self._conversations.clear()
105 class MergedConversations(object):
108 self._conversations = []
110 def append_conversation(self, newConversation):
111 self._validate(newConversation)
112 for similarConversation in self._find_related_conversation(newConversation.id):
113 self._update_previous_related_conversation(similarConversation, newConversation)
114 self._remove_repeats(similarConversation, newConversation)
115 self._conversations.append(newConversation)
119 selfDict["conversations"] = [conv.to_dict() for conv in self._conversations]
123 def conversations(self):
124 return self._conversations
126 def _validate(self, newConversation):
127 if not self._conversations:
130 for constantField in ("number", ):
131 assert getattr(self._conversations[0], constantField) == getattr(newConversation, constantField), "Constant field changed, soemthing is seriously messed up: %r v %r" % (
132 getattr(self._conversations[0], constantField),
133 getattr(newConversation, constantField),
136 if newConversation.time <= self._conversations[-1].time:
137 raise RuntimeError("Conversations got out of order")
139 def _find_related_conversation(self, convId):
140 similarConversations = (
142 for conversation in self._conversations
143 if conversation.id == convId
145 return similarConversations
147 def _update_previous_related_conversation(self, relatedConversation, newConversation):
148 for commonField in ("isRead", "isSpam", "isTrash", "isArchived"):
149 newValue = getattr(newConversation, commonField)
150 setattr(relatedConversation, commonField, newValue)
152 def _remove_repeats(self, relatedConversation, newConversation):
153 newConversationMessages = newConversation.messages
154 newConversation.messages = [
156 for newMessage in newConversationMessages
157 if newMessage not in relatedConversation.messages
159 _moduleLogger.debug("Found %d new messages in conversation %s (%d/%d)" % (
160 len(newConversationMessages) - len(newConversation.messages),
162 len(newConversation.messages),
163 len(newConversationMessages),
165 assert 0 < len(newConversation.messages), "Everything shouldn't have been removed"