diff --git a/doc/NOTES.md b/doc/NOTES.md index 789b9d2..da29f2c 100644 --- a/doc/NOTES.md +++ b/doc/NOTES.md @@ -22,7 +22,6 @@ - allow handlers to specify whether to update or not (e.g. scroll) - API documentation - github pages -- central geolocation service? (currency, weather, rtt) ## TODO - themes: use colors to improve theme readability diff --git a/tests/util/test_location.py b/tests/util/test_location.py new file mode 100644 index 0000000..7e54ee5 --- /dev/null +++ b/tests/util/test_location.py @@ -0,0 +1,43 @@ +import unittest +import json +import urllib.request + +import util.location + +class location(unittest.TestCase): + def setUp(self): + patcher = unittest.mock.patch('util.location.urllib.request') + self.addCleanup(patcher.stop) + self.request = patcher.start() + util.location.reset() + + self.primary = { + 'country': 'Middle Earth', + 'longitude': '10.0', + 'latitude': '20.5', + 'ip': '127.0.0.1', + } + self.secondary = { + 'country_name': 'Rivia', + 'longitude': '-10.0', + 'latitude': '-23', + 'ip': '127.0.0.6', + } + + def test_primary_provider(self): + self.request.urlopen.return_value.read.return_value = json.dumps(self.primary) + util.location.country() + self.assertEqual(self.primary['country'], util.location.country()) + self.assertEqual((self.primary['latitude'], self.primary['longitude']), util.location.coordinates()) + self.assertEqual(self.primary['ip'], util.location.public_ip()) + + def test_secondary_provider(self): + urlopen = unittest.mock.MagicMock() + urlopen.read.return_value = json.dumps(self.secondary) + self.request.urlopen.side_effect = [ RuntimeError(), urlopen ] + + self.assertEqual(self.secondary['country_name'], util.location.country()) + self.assertEqual((self.secondary['latitude'], self.secondary['longitude']), util.location.coordinates()) + self.assertEqual(self.secondary['ip'], util.location.public_ip()) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/util/location.py b/util/location.py new file mode 100644 index 0000000..632532a --- /dev/null +++ b/util/location.py @@ -0,0 +1,69 @@ +import json +import time +import urllib.request + +__document = None +__data = {} +__next = 0 + +__sources = [ + { + 'url': 'http://free.ipwhois.io/json/', + 'mapping': { + 'latitude': 'latitude', + 'longitude': 'longitude', + 'country': 'country', + 'ip': 'public_ip', + } + }, + { + 'url': 'http://ipapi.co/json', + 'mapping': { + 'latitude': 'latitude', + 'longitude': 'longitude', + 'country_name': 'country', + 'ip': 'public_ip', + } + } +] + +def __expired(): + global __next + return __next <= time.time() + +def __load(): + global __data + global __next + + __data = {} + for src in __sources: + try: + tmp = json.loads(urllib.request.urlopen(src['url']).read()) + for k, v in src['mapping'].items(): + __data[v] = tmp.get(k, None) + __next = time.time() + 60*60*12 # update once every 12h + return + except Exception as e: + pass + __next = time.time() + 60*30 # error - try again every 30m + +def __get(name, default=None): + global __data + if not __data or __expired(): + __load() + return __data.get(name, default) + +def reset(): + global __next + __next = 0 + +def coordinates(): + return __get('latitude'), __get('longitude') + +def country(): + return __get('country') + +def public_ip(): + return __get('public_ip') + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4