Doing some initial location work
authorEd Page <eopage@byu.net>
Wed, 28 Apr 2010 00:42:29 +0000 (19:42 -0500)
committerEd Page <eopage@byu.net>
Wed, 28 Apr 2010 00:42:29 +0000 (19:42 -0500)
src/gvoice/locations.py [new file with mode: 0644]
src/location.py

diff --git a/src/gvoice/locations.py b/src/gvoice/locations.py
new file mode 100644 (file)
index 0000000..ed85620
--- /dev/null
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+
+from __future__ import with_statement
+
+import re
+import logging
+
+try:
+       import cPickle
+       pickle = cPickle
+except ImportError:
+       import pickle
+
+try:
+       import simplejson as _simplejson
+       simplejson = _simplejson
+except ImportError:
+       simplejson = None
+
+import constants
+import util.coroutines as coroutines
+import util.misc as misc_utils
+import util.go_utils as gobject_utils
+
+import browser_emu
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+class Locations(object):
+
+       OLDEST_COMPATIBLE_FORMAT_VERSION = misc_utils.parse_version("0.8.0")
+
+       def __init__(self, asyncPool):
+               self._asyncPool = asyncPool
+               self._locations = {}
+               self._browser = browser_emu.MozillaEmulator()
+
+               self.updateSignalHandler = coroutines.CoTee()
+
+       def load(self, path):
+               _moduleLogger.debug("Loading cache")
+               assert not self._locations
+               try:
+                       with open(path, "rb") as f:
+                               fileVersion, fileBuild, locs = pickle.load(f)
+               except (pickle.PickleError, IOError, EOFError, ValueError):
+                       _moduleLogger.exception("While loading for %s" % self._name)
+                       return
+
+               if misc_utils.compare_versions(
+                       self.OLDEST_COMPATIBLE_FORMAT_VERSION,
+                       misc_utils.parse_version(fileVersion),
+               ) <= 0:
+                       _moduleLogger.info("Loaded cache")
+                       self._locations = locs
+               else:
+                       _moduleLogger.debug(
+                               "Skipping cache due to version mismatch (%s-%s)" % (
+                                       fileVersion, fileBuild
+                               )
+                       )
+
+       def save(self, path):
+               _moduleLogger.info("Saving cache")
+               if not self._locations:
+                       _moduleLogger.info("Odd, no conversations to cache.  Did we never load the cache?")
+                       return
+
+               try:
+                       dataToDump = (constants.__version__, constants.__build__, self._locations)
+                       with open(path, "wb") as f:
+                               pickle.dump(dataToDump, f, pickle.HIGHEST_PROTOCOL)
+               except (pickle.PickleError, IOError):
+                       _moduleLogger.exception("While saving for %s" % self._name)
+               _moduleLogger.info("%s Cache saved" % (self._name, ))
+
+       def request_location(self, number):
+               try:
+                       return self._locations[number]
+               except KeyError:
+                       le = gobject_utils.AsyncLinearExecution(self._asyncPool, self._request_location)
+                       le.start(number)
+                       return None
+
+       def _download_location(self, number):
+               numberURL = "http://digits.cloudvox.com/%s.json" % number
+               page = self._browser.download(numberURL)
+               data = parse_json(page)
+               return data
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _request_location(self, number):
+               try:
+                       locationData = yield (
+                               self._download_location,
+                               (number, ),
+                               {},
+                       )
+               except Exception:
+                       _moduleLogger.exception("%s While updating conversations" % (self._name, ))
+                       return
+
+               self._locations[number] = locationData
+               message = (locationData, )
+               self.updateSignalHandler.stage.send(message)
+
+
+def safe_eval(s):
+       _TRUE_REGEX = re.compile("true")
+       _FALSE_REGEX = re.compile("false")
+       s = _TRUE_REGEX.sub("True", s)
+       s = _FALSE_REGEX.sub("False", s)
+       return eval(s, {}, {})
+
+
+def _fake_parse_json(flattened):
+       return safe_eval(flattened)
+
+
+def _actual_parse_json(flattened):
+       return simplejson.loads(flattened)
+
+
+if simplejson is None:
+       parse_json = _fake_parse_json
+else:
+       parse_json = _actual_parse_json
index f8ecad5..32b5823 100644 (file)
@@ -3,6 +3,7 @@ import logging
 import telepathy
 
 import util.misc as misc_utils
+import handle
 
 
 _moduleLogger = logging.getLogger(__name__)
@@ -27,14 +28,22 @@ class LocationMixin(object):
                """
                @returns {Contact: {Location Type: Location}}
                """
-               raise telepathy.errors.NotImplemented("Yet")
+               contactLocation = (
+                       (contact, self._get_location(contact))
+                       for contact in contacts
+               )
+               return dict(
+                       (contact, location)
+                       for (contact, location) in contactLocation
+                       if location
+               )
 
        @misc_utils.log_exception(_moduleLogger)
        def RequestLocation(self, contact):
                """
                @returns {Location Type: Location}
                """
-               raise telepathy.errors.NotImplemented("Yet")
+               return self._get_location(contact)
 
        @misc_utils.log_exception(_moduleLogger)
        def SetLocation(self, location):
@@ -42,3 +51,20 @@ class LocationMixin(object):
                Since presence is based off of phone numbers, not allowing the client to change it
                """
                raise telepathy.errors.PermissionDenied()
+
+       def _get_location(self, contact):
+               h = self.get_handle_by_id(telepathy.HANDLE_TYPE_CONTACT, contact)
+               if isinstance(h, handle.ConnectionHandle):
+                       number = self.session.backend.get_callback_number()
+               else:
+                       number = h.phoneNumber
+
+               rawData = self.session.location.request_location(number)
+               if rawData is None:
+                       return {}
+
+               data = {
+                       "country": rawData["country"],
+                       "city": rawData["city"],
+                       "region": rawData["region"],
+               }