mirror of
https://github.com/Mikaela/Limnoria.git
synced 2025-02-18 14:40:51 +01:00
Geography: Add 'timezone' command
This commit is contained in:
parent
696d82ccfe
commit
36ade18319
@ -28,18 +28,91 @@
|
|||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
from supybot import utils, plugins, ircutils, callbacks
|
from supybot import utils, plugins, ircutils, callbacks
|
||||||
from supybot.commands import *
|
from supybot.commands import *
|
||||||
from supybot.i18n import PluginInternationalization
|
from supybot.i18n import PluginInternationalization
|
||||||
|
|
||||||
|
from . import nominatim
|
||||||
|
from . import wikidata
|
||||||
|
|
||||||
_ = PluginInternationalization("Geography")
|
_ = PluginInternationalization("Geography")
|
||||||
|
|
||||||
|
|
||||||
class Geography(callbacks.Plugin):
|
class Geography(callbacks.Plugin):
|
||||||
"""Provides geography facts, such as timezones."""
|
"""Provides geography facts, such as timezones.
|
||||||
|
|
||||||
|
This plugin uses data from `Wikidata <https://wikidata.org/>`_
|
||||||
|
and `OSM/Nominatim <https://nominatim.openstreetmap.org/>`.
|
||||||
|
"""
|
||||||
|
|
||||||
threaded = True
|
threaded = True
|
||||||
|
|
||||||
|
@wrap(["text"])
|
||||||
|
def timezone(self, irc, msg, args, query):
|
||||||
|
"""<location name to search>
|
||||||
|
|
||||||
|
Returns the timezone used in the given location. For example,
|
||||||
|
the name could be "Paris" or "Paris, France".
|
||||||
|
This uses data from Wikidata and Nominatim."""
|
||||||
|
osmids = nominatim.search_osmids(query)
|
||||||
|
if not osmids:
|
||||||
|
irc.error(_("Could not find the location"), Raise=True)
|
||||||
|
|
||||||
|
now = datetime.datetime.now(tz=datetime.timezone.utc)
|
||||||
|
|
||||||
|
for osmid in osmids:
|
||||||
|
uri = wikidata.uri_from_osmid(osmid)
|
||||||
|
if not uri:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Get the timezone object (and handle various errors)
|
||||||
|
try:
|
||||||
|
timezone = wikidata.timezone_from_uri(uri)
|
||||||
|
except utils.time.UnknownTimeZone as e:
|
||||||
|
irc.error(
|
||||||
|
format(_("Could not understand timezone: %s"), e.args[0]),
|
||||||
|
Raise=True,
|
||||||
|
)
|
||||||
|
except utils.time.MissingTimezoneLibrary:
|
||||||
|
irc.error(
|
||||||
|
_(
|
||||||
|
"Timezone-related commands are not available. "
|
||||||
|
"Your administrator need to either upgrade Python to "
|
||||||
|
"version 3.9 or greater, or install pytz."
|
||||||
|
),
|
||||||
|
Raise=True,
|
||||||
|
)
|
||||||
|
except utils.time.TimezoneException as e:
|
||||||
|
irc.error(e.args[0], Raise=True)
|
||||||
|
|
||||||
|
# Extract a human-friendly name, depending on the type of
|
||||||
|
# the timezone object:
|
||||||
|
if hasattr(timezone, "key"):
|
||||||
|
# instance of zoneinfo.ZoneInfo
|
||||||
|
irc.reply(timezone.key)
|
||||||
|
return
|
||||||
|
elif hasattr(timezone, "zone"):
|
||||||
|
# instance of pytz.timezone
|
||||||
|
irc.reply(timezone.zone)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
# probably datetime.timezone built from a constant offset
|
||||||
|
try:
|
||||||
|
offset = timezone.utcoffset(now).seconds
|
||||||
|
except NotImplementedError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
hours = int(offset / 3600)
|
||||||
|
minutes = int(offset / 60 % 60)
|
||||||
|
irc.reply("UTC+%0.2i:%0.2i" % (hours, minutes))
|
||||||
|
return
|
||||||
|
|
||||||
|
irc.error(
|
||||||
|
_("Could not find the timezone of this location."), Raise=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
Class = Geography
|
Class = Geography
|
||||||
|
|
||||||
|
@ -29,7 +29,19 @@
|
|||||||
###
|
###
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import contextlib
|
||||||
from unittest import skipIf
|
from unittest import skipIf
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pytz
|
||||||
|
except ImportError:
|
||||||
|
pytz = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
import zoneinfo
|
||||||
|
except ImportError:
|
||||||
|
zoneinfo = None
|
||||||
|
|
||||||
from supybot.test import *
|
from supybot.test import *
|
||||||
from supybot import utils
|
from supybot import utils
|
||||||
@ -41,6 +53,43 @@ from . import nominatim
|
|||||||
class GeographyTestCase(PluginTestCase):
|
class GeographyTestCase(PluginTestCase):
|
||||||
plugins = ("Geography",)
|
plugins = ("Geography",)
|
||||||
|
|
||||||
|
@skipIf(not pytz, "pytz is not available")
|
||||||
|
@patch.object(nominatim, "search_osmids", return_value=[42])
|
||||||
|
@patch.object(wikidata, "uri_from_osmid", return_value="foo")
|
||||||
|
def testTimezonePytz(self, _, __):
|
||||||
|
tz = pytz.timezone("Europe/Paris")
|
||||||
|
|
||||||
|
with patch.object(wikidata, "timezone_from_uri", return_value=tz):
|
||||||
|
self.assertResponse("timezone Foo Bar", "Europe/Paris")
|
||||||
|
|
||||||
|
@skipIf(not zoneinfo, "Python is older than 3.9")
|
||||||
|
@patch.object(nominatim, "search_osmids", return_value=[42])
|
||||||
|
@patch.object(wikidata, "uri_from_osmid", return_value="foo")
|
||||||
|
def testTimezoneZoneinfo(self, _, __):
|
||||||
|
tz = zoneinfo.ZoneInfo("Europe/Paris")
|
||||||
|
|
||||||
|
with patch.object(wikidata, "timezone_from_uri", return_value=tz):
|
||||||
|
self.assertResponse("timezone Foo Bar", "Europe/Paris")
|
||||||
|
|
||||||
|
@skipIf(not zoneinfo, "Python is older than 3.9")
|
||||||
|
@patch.object(nominatim, "search_osmids", return_value=[42])
|
||||||
|
@patch.object(wikidata, "uri_from_osmid", return_value="foo")
|
||||||
|
def testTimezoneAbsolute(self, _, __):
|
||||||
|
tz = datetime.timezone(datetime.timedelta(hours=4))
|
||||||
|
|
||||||
|
with patch.object(wikidata, "timezone_from_uri", return_value=tz):
|
||||||
|
self.assertResponse("timezone Foo Bar", "UTC+04:00")
|
||||||
|
|
||||||
|
tz = datetime.timezone(datetime.timedelta(hours=4, minutes=30))
|
||||||
|
|
||||||
|
with patch.object(wikidata, "timezone_from_uri", return_value=tz):
|
||||||
|
self.assertResponse("timezone Foo Bar", "UTC+04:30")
|
||||||
|
|
||||||
|
@skipIf(not network, "Network test")
|
||||||
|
def testTimezoneIntegration(self):
|
||||||
|
self.assertResponse("timezone Metz, France", "Europe/Paris")
|
||||||
|
self.assertResponse("timezone Saint-Denis, La Réunion", "UTC+04:00")
|
||||||
|
|
||||||
|
|
||||||
class GeographyWikidataTestCase(SupyTestCase):
|
class GeographyWikidataTestCase(SupyTestCase):
|
||||||
@skipIf(not network, "Network test")
|
@skipIf(not network, "Network test")
|
||||||
@ -56,7 +105,7 @@ class GeographyWikidataTestCase(SupyTestCase):
|
|||||||
|
|
||||||
@skipIf(not network, "Network test")
|
@skipIf(not network, "Network test")
|
||||||
def testDirect(self):
|
def testDirect(self):
|
||||||
"""The queried object directly has a timezone property"""
|
# The queried object directly has a timezone property
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
# New York
|
# New York
|
||||||
wikidata.timezone_from_uri("http://www.wikidata.org/entity/Q1384"),
|
wikidata.timezone_from_uri("http://www.wikidata.org/entity/Q1384"),
|
||||||
@ -65,8 +114,8 @@ class GeographyWikidataTestCase(SupyTestCase):
|
|||||||
|
|
||||||
@skipIf(not network, "Network test")
|
@skipIf(not network, "Network test")
|
||||||
def testParent(self):
|
def testParent(self):
|
||||||
"""The queried object does not have a TZ property
|
# The queried object does not have a TZ property but it is part
|
||||||
but it is part of an object that does"""
|
# of an object that does
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
# Metz, France
|
# Metz, France
|
||||||
wikidata.timezone_from_uri(
|
wikidata.timezone_from_uri(
|
||||||
@ -77,8 +126,8 @@ class GeographyWikidataTestCase(SupyTestCase):
|
|||||||
|
|
||||||
@skipIf(not network, "Network test")
|
@skipIf(not network, "Network test")
|
||||||
def testParentAndIgnoreSelf(self):
|
def testParentAndIgnoreSelf(self):
|
||||||
"""The queried object has a TZ property, but it's useless to us;
|
# The queried object has a TZ property, but it's useless to us;
|
||||||
however it is part of an object that has a useful one."""
|
# however it is part of an object that has a useful one."""
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
# New York City, NY
|
# New York City, NY
|
||||||
wikidata.timezone_from_uri("http://www.wikidata.org/entity/Q60"),
|
wikidata.timezone_from_uri("http://www.wikidata.org/entity/Q60"),
|
||||||
@ -87,11 +136,12 @@ class GeographyWikidataTestCase(SupyTestCase):
|
|||||||
|
|
||||||
@skipIf(not network, "Network test")
|
@skipIf(not network, "Network test")
|
||||||
def testParentQualifiedIgnorePreferred(self):
|
def testParentQualifiedIgnorePreferred(self):
|
||||||
"""The queried object does not have a TZ property,
|
# The queried object does not have a TZ property,
|
||||||
and is part of an object that does.
|
# and is part of an object that does.
|
||||||
However, this parent's 'preferred' timezone is not the
|
# However, this parent's 'preferred' timezone is not the
|
||||||
right one, so we must make sure to select the right one
|
# right one, so we must make sure to select the right one
|
||||||
based on P518 ('applies to part')."""
|
# based on P518 ('applies to part').
|
||||||
|
|
||||||
# La Réunion is a French region, but in UTC+4.
|
# La Réunion is a French region, but in UTC+4.
|
||||||
# France has a bunch of timezone statements, and 'Europe/Paris'
|
# France has a bunch of timezone statements, and 'Europe/Paris'
|
||||||
# is marked as Preferred because it is the time of metropolitan
|
# is marked as Preferred because it is the time of metropolitan
|
||||||
@ -113,5 +163,8 @@ class GeographyNominatimTestCase(SupyTestCase):
|
|||||||
results = nominatim.search_osmids("Metz, France")
|
results = nominatim.search_osmids("Metz, France")
|
||||||
self.assertEqual(results[0], 450381, results)
|
self.assertEqual(results[0], 450381, results)
|
||||||
|
|
||||||
|
results = nominatim.search_osmids("Saint-Denis, La Réunion")
|
||||||
|
self.assertEqual(results[0], 192468, results)
|
||||||
|
|
||||||
|
|
||||||
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user