Geography: Add 'localtime' command

This commit is contained in:
Valentin Lorentz 2021-11-09 23:10:55 +01:00
parent 36ade18319
commit 93a407a9ac
2 changed files with 111 additions and 29 deletions

View File

@ -30,7 +30,7 @@
import datetime
from supybot import utils, plugins, ircutils, callbacks
from supybot import conf, utils, plugins, ircutils, callbacks
from supybot.commands import *
from supybot.i18n import PluginInternationalization
@ -40,6 +40,27 @@ from . import wikidata
_ = PluginInternationalization("Geography")
def timezone_from_uri(irc, uri):
try:
return 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)
class Geography(callbacks.Plugin):
"""Provides geography facts, such as timezones.
@ -49,6 +70,38 @@ class Geography(callbacks.Plugin):
threaded = True
@wrap(["text"])
def localtime(self, irc, msg, args, query):
"""<location name to search>
Returns the current used in the given location. For example,
the name could be "Paris" or "Paris, France". The response is
formatted according to supybot.reply.format.time
This uses data from Wikidata and Nominatim."""
osmids = nominatim.search_osmids(query)
if not osmids:
irc.error(_("Could not find the location"), Raise=True)
for osmid in osmids:
uri = wikidata.uri_from_osmid(osmid)
if not uri:
continue
# Get the timezone object (and handle various errors)
timezone = timezone_from_uri(irc, uri)
# Get the local time
now = datetime.datetime.now(tz=timezone)
format_ = conf.supybot.reply.format.time.getSpecific(
channel=msg.channel, network=irc.network
)()
# Return it
irc.reply(now.strftime(format_))
return
@wrap(["text"])
def timezone(self, irc, msg, args, query):
"""<location name to search>
@ -68,24 +121,7 @@ class Geography(callbacks.Plugin):
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)
timezone = timezone_from_uri(irc, uri)
# Extract a human-friendly name, depending on the type of
# the timezone object:

View File

@ -29,6 +29,7 @@
###
import datetime
import functools
import contextlib
from unittest import skipIf
from unittest.mock import patch
@ -50,31 +51,38 @@ from . import wikidata
from . import nominatim
class GeographyTestCase(PluginTestCase):
def mock(f):
@functools.wraps(f)
def newf(self):
with patch.object(wikidata, "uri_from_osmid", return_value="foo"):
with patch.object(nominatim, "search_osmids", return_value=[42]):
f(self)
return newf
class GeographyTimezoneTestCase(PluginTestCase):
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, _, __):
@mock
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, _, __):
@mock
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, _, __):
@mock
def testTimezoneAbsolute(self):
tz = datetime.timezone(datetime.timedelta(hours=4))
with patch.object(wikidata, "timezone_from_uri", return_value=tz):
@ -91,6 +99,44 @@ class GeographyTestCase(PluginTestCase):
self.assertResponse("timezone Saint-Denis, La Réunion", "UTC+04:00")
class GeographyLocaltimeTestCase(PluginTestCase):
plugins = ("Geography",)
@skipIf(not pytz, "pytz is not available")
@mock
def testLocaltimePytz(self):
tz = pytz.timezone("Europe/Paris")
with patch.object(wikidata, "timezone_from_uri", return_value=tz):
self.assertRegexp("localtime Foo Bar", r".*\+0[12]00$")
@skipIf(not zoneinfo, "Python is older than 3.9")
@mock
def testLocaltimeZoneinfo(self):
tz = zoneinfo.ZoneInfo("Europe/Paris")
with patch.object(wikidata, "timezone_from_uri", return_value=tz):
self.assertRegexp("localtime Foo Bar", r".*\+0[12]00$")
@skipIf(not zoneinfo, "Python is older than 3.9")
@mock
def testLocaltimeAbsolute(self):
tz = datetime.timezone(datetime.timedelta(hours=4))
with patch.object(wikidata, "timezone_from_uri", return_value=tz):
self.assertRegexp("localtime Foo Bar", r".*\+0400$")
tz = datetime.timezone(datetime.timedelta(hours=4, minutes=30))
with patch.object(wikidata, "timezone_from_uri", return_value=tz):
self.assertRegexp("localtime Foo Bar", r".*\+0430$")
@skipIf(not network, "Network test")
def testLocaltimeIntegration(self):
self.assertRegexp("localtime Metz, France", r".*\+0[12]00$")
self.assertRegexp("localtime Saint-Denis, La Réunion", r".*\+0400$")
class GeographyWikidataTestCase(SupyTestCase):
@skipIf(not network, "Network test")
def testOsmidToTimezone(self):