fix: Format using black

This commit is contained in:
PapaTutuWawa 2021-06-15 20:24:00 +02:00
parent b8be375ddb
commit 79280d9d59
4 changed files with 156 additions and 124 deletions

View File

@ -1,4 +1,4 @@
''' """
This file is part of JANINE. This file is part of JANINE.
JANINE is free software: you can redistribute it and/or modify JANINE is free software: you can redistribute it and/or modify
@ -13,58 +13,62 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with JANINE. If not, see <https://www.gnu.org/licenses/>. along with JANINE. If not, see <https://www.gnu.org/licenses/>.
''' """
import datetime import datetime
def pad_time_component(c): def pad_time_component(c):
''' """
If we have 12:08, it gets turned into 12:8, so we need to pad If we have 12:08, it gets turned into 12:8, so we need to pad
the components with a leading zero, if there is none. the components with a leading zero, if there is none.
''' """
if len(c) != 2: if len(c) != 2:
return f'0{c}' return f"0{c}"
return c return c
def format_time(time_str): def format_time(time_str):
''' """
Reformat ISO style time data to a more Reformat ISO style time data to a more
readable format. readable format.
''' """
try: try:
date = datetime.datetime.fromisoformat(time_str) date = datetime.datetime.fromisoformat(time_str)
except ValueError: except ValueError:
return time_str return time_str
return f'{date.day}.{date.month}.{date.year} {pad_time_component(date.hour)}:{pad_time_component(date.minute)}' return f"{date.day}.{date.month}.{date.year} {pad_time_component(date.hour)}:{pad_time_component(date.minute)}"
def format_warning(warning): def format_warning(warning):
''' """
Send a warning to all the recipients Send a warning to all the recipients
''' """
# Reformat the message a bit # Reformat the message a bit
effective_time = format_time(warning.effective_from) effective_time = format_time(warning.effective_from)
expiry_time = format_time(warning.expires) expiry_time = format_time(warning.expires)
body = f'''*{warning.headline}* body = f"""*{warning.headline}*
({effective_time} bis {expiry_time}) ({effective_time} bis {expiry_time})
{warning.description}''' {warning.description}"""
if warning.instruction: if warning.instruction:
body = f'''{body} body = f"""{body}
{warning.instruction}''' {warning.instruction}"""
# Smells like script injection, but okay # Smells like script injection, but okay
body = body.replace('<br>', '\n') body = body.replace("<br>", "\n")
body = body.replace('<br/>', '\n') body = body.replace("<br/>", "\n")
return body return body
def find_one(func, array): def find_one(func, array):
''' """
Utility function Utility function
Return the first element in array for which func returns True. Return the first element in array for which func returns True.
''' """
for e in array: for e in array:
if func(e): if func(e):
return e return e

View File

@ -1,4 +1,4 @@
''' """
This file is part of JANINE. This file is part of JANINE.
JANINE is free software: you can redistribute it and/or modify JANINE is free software: you can redistribute it and/or modify
@ -13,7 +13,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with JANINE. If not, see <https://www.gnu.org/licenses/>. along with JANINE. If not, see <https://www.gnu.org/licenses/>.
''' """
import logging import logging
import asyncio import asyncio
from urllib3.exceptions import MaxRetryError from urllib3.exceptions import MaxRetryError
@ -26,9 +26,10 @@ from janine.modules.janine.helpers import format_warning
import requests import requests
import aioxmpp import aioxmpp
logger = logging.getLogger('mira.modules.janine.janine') logger = logging.getLogger("mira.modules.janine.janine")
NAME = "janine"
NAME = 'janine'
class JanineModule(BaseModule): class JanineModule(BaseModule):
__instance = None __instance = None
@ -42,57 +43,58 @@ class JanineModule(BaseModule):
def __init__(self, base, **kwargs): def __init__(self, base, **kwargs):
if JanineModule.__instance != None: if JanineModule.__instance != None:
raise Exception('Trying to init singleton twice') raise Exception("Trying to init singleton twice")
super().__init__(base, **kwargs) super().__init__(base, **kwargs)
JanineModule.__instance = self JanineModule.__instance = self
self._subcommand_table = { self._subcommand_table = {
'subscribe': self._subscribe, "subscribe": self._subscribe,
'unsubscribe': self._unsubscribe, "unsubscribe": self._unsubscribe,
'hilfe': self._help, "hilfe": self._help,
'*': self._any "*": self._any,
} }
self._channels = self._stm.get_data('channels') self._channels = self._stm.get_data("channels")
if not self._channels: if not self._channels:
# TODO: Move out of the constructor. Perform this asynchronously, # TODO: Move out of the constructor. Perform this asynchronously,
# and just refuse to process commands before we're done. Start the # and just refuse to process commands before we're done. Start the
# request loop afterwards. # request loop afterwards.
logger.info('Channels do not exist. Downloading...') logger.info("Channels do not exist. Downloading...")
req = requests.get(MiscDataSources.channels()) req = requests.get(MiscDataSources.channels())
channels = req.json() channels = req.json()
self._channels = [channels[key].get('NAME', '') for key in channels] self._channels = [channels[key].get("NAME", "") for key in channels]
self._stm.set_data('channels', self._channels) self._stm.set_data("channels", self._channels)
logger.info('Done') logger.info("Done")
self._warnings0 = list(map(stub_warning, self._stm.get_data('warnings'))) self._warnings0 = list(map(stub_warning, self._stm.get_data("warnings")))
self._warnings1 = [] self._warnings1 = []
self._refresh_timeout = self.get_option('refresh_timeout', 15 * 60) self._refresh_timeout = self.get_option("refresh_timeout", 15 * 60)
self._sources = list(map(WarningSources.source_by_name, self._config['sources'])) self._sources = list(
map(WarningSources.source_by_name, self._config["sources"])
)
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
periodic = loop.create_task(self._periodic_ticker()) periodic = loop.create_task(self._periodic_ticker())
async def _periodic_ticker(self): async def _periodic_ticker(self):
''' """
"Executes" every self._refresh_timeout seconds to fetch all "Executes" every self._refresh_timeout seconds to fetch all
configured warnings and send them to the users, if there are configured warnings and send them to the users, if there are
any new ones. any new ones.
''' """
while True: while True:
logger.debug('Refreshing warning list') logger.debug("Refreshing warning list")
await self._request_warnings() await self._request_warnings()
self._stm.set_data('warnings', self._stm.set_data("warnings", [x.id for x in self._warnings0])
[x.id for x in self._warnings0])
await asyncio.sleep(self._refresh_timeout) await asyncio.sleep(self._refresh_timeout)
async def _request_warnings(self): async def _request_warnings(self):
''' """
Requests warnings from all configured warning sources and Requests warnings from all configured warning sources and
sends new ones as notifications sends new ones as notifications
''' """
self._warnings1 = self._warnings0 self._warnings1 = self._warnings0
self._warnings0 = [] self._warnings0 = []
for source in self._sources: for source in self._sources:
@ -100,7 +102,7 @@ class JanineModule(BaseModule):
req = requests.get(source) req = requests.get(source)
self._warnings0 += parse_data(req.json()) self._warnings0 += parse_data(req.json())
except MaxRetryError: except MaxRetryError:
logger.warn('Connection timeout for request to %s', source) logger.warn("Connection timeout for request to %s", source)
continue continue
# Find new warnings and send them out # Find new warnings and send them out
@ -115,18 +117,21 @@ class JanineModule(BaseModule):
async def _subscribe(self, cmd, msg): async def _subscribe(self, cmd, msg):
if len(cmd) < 2: if len(cmd) < 2:
self.send_message(msg.from_, 'Du hast keinen Landkreis angegeben') self.send_message(msg.from_, "Du hast keinen Landkreis angegeben")
return return
landkreis = ' '.join(cmd[1:]) landkreis = " ".join(cmd[1:])
if not landkreis in self._channels: if not landkreis in self._channels:
self.send_message(msg.from_, 'Der angegebene Landkreis "%s" existiert nicht' % (landkreis)) self.send_message(
msg.from_, 'Der angegebene Landkreis "%s" existiert nicht' % (landkreis)
)
return return
bare = str(msg.from_.bare()) bare = str(msg.from_.bare())
if self._sum.is_subscribed_to(bare, landkreis): if self._sum.is_subscribed_to(bare, landkreis):
self.send_message(msg.from_, self.send_message(
'Du hast den "%s" bereits abonniert' % (landkreis)) msg.from_, 'Du hast den "%s" bereits abonniert' % (landkreis)
)
return return
self._sum.add_subscription_for(bare, landkreis) self._sum.add_subscription_for(bare, landkreis)
@ -137,33 +142,40 @@ class JanineModule(BaseModule):
async def _unsubscribe(self, cmd, msg): async def _unsubscribe(self, cmd, msg):
if len(cmd) < 2: if len(cmd) < 2:
self.send_message(msg.from_, 'Du hast keinen Landkreis angegeben') self.send_message(msg.from_, "Du hast keinen Landkreis angegeben")
return return
landkreis = ' '.join(cmd[1:]) landkreis = " ".join(cmd[1:])
bare = str(msg.from_.bare()) bare = str(msg.from_.bare())
if not self._sum.is_subscribed_to(bare, landkreis): if not self._sum.is_subscribed_to(bare, landkreis):
self.send_message(msg.from_, 'Du hast "%s" nicht abonniert' % (landkreis)) self.send_message(msg.from_, 'Du hast "%s" nicht abonniert' % (landkreis))
return return
self._sum.remove_subscription_for(bare, landkreis) self._sum.remove_subscription_for(bare, landkreis)
self.send_message(msg.from_, 'Du erhälst nun keine Nachrichten zu "%s" mehr' % (landkreis)) self.send_message(
msg.from_, 'Du erhälst nun keine Nachrichten zu "%s" mehr' % (landkreis)
)
async def _help(self, cmd, msg): async def _help(self, cmd, msg):
body = '''Verfügbare Befehle: body = """Verfügbare Befehle:
subscribe <Landkreis> - Abonniere einen Landkreis subscribe <Landkreis> - Abonniere einen Landkreis
unsubscribe <Landkreis> - Entferne das Abonnement zu einem Landkreis unsubscribe <Landkreis> - Entferne das Abonnement zu einem Landkreis
help - Gebe diese Hilfe aus''' help - Gebe diese Hilfe aus"""
self.send_message(msg.from_, body) self.send_message(msg.from_, body)
async def _any(self, cmd, msg): async def _any(self, cmd, msg):
if not cmd: if not cmd:
self.send_message(msg.from_, self.send_message(
'Ich bin die Jabber Anwendung für Notfallinformations- und -Nachrichten-Empfang') msg.from_,
"Ich bin die Jabber Anwendung für Notfallinformations- und -Nachrichten-Empfang",
)
else: else:
self.send_message(msg.from_, self.send_message(
'Unbekannter Befehl "%s". "janine hilfe" gibt alle bekannten Befehle aus' % (cmd[0])) msg.from_,
'Unbekannter Befehl "%s". "janine hilfe" gibt alle bekannten Befehle aus'
% (cmd[0]),
)
def get_instance(base, **kwargs): def get_instance(base, **kwargs):

View File

@ -1,4 +1,4 @@
''' """
This file is part of JANINE. This file is part of JANINE.
JANINE is free software: you can redistribute it and/or modify JANINE is free software: you can redistribute it and/or modify
@ -13,44 +13,48 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with JANINE. If not, see <https://www.gnu.org/licenses/>. along with JANINE. If not, see <https://www.gnu.org/licenses/>.
''' """
class WarningSources: class WarningSources:
''' """
A collection of sources of the BBK A collection of sources of the BBK
''' """
@staticmethod @staticmethod
def bbk_dwd(): def bbk_dwd():
return 'https://warnung.bund.de/bbk.dwd/unwetter.json' return "https://warnung.bund.de/bbk.dwd/unwetter.json"
@staticmethod @staticmethod
def bbk_mowas(): def bbk_mowas():
return 'https://warnung.bund.de/bbk.mowas/gefahrendurchsagen.json' return "https://warnung.bund.de/bbk.mowas/gefahrendurchsagen.json"
@staticmethod @staticmethod
def bbk_biwapp(): def bbk_biwapp():
return 'https://warnung.bund.de/bbk.biwapp/warnmeldungen.json' return "https://warnung.bund.de/bbk.biwapp/warnmeldungen.json"
@staticmethod @staticmethod
def bbk_ihp(): def bbk_ihp():
return 'https://warnung.bund.de/bbk.lhp/hochwassermeldungen.json' return "https://warnung.bund.de/bbk.lhp/hochwassermeldungen.json"
@staticmethod @staticmethod
def source_by_name(name): def source_by_name(name):
return { return {
'IHP': WarningSources.bbk_ihp(), "IHP": WarningSources.bbk_ihp(),
'DWD': WarningSources.bbk_dwd(), "DWD": WarningSources.bbk_dwd(),
'MOWAS': WarningSources.bbk_mowas(), "MOWAS": WarningSources.bbk_mowas(),
'BIWAPP': WarningSources.bbk_biwapp() "BIWAPP": WarningSources.bbk_biwapp(),
}[name] }[name]
class MiscDataSources: class MiscDataSources:
''' """
A collection of other data sources for various use cases A collection of other data sources for various use cases
''' """
@staticmethod @staticmethod
def channels(): def channels():
''' """
These are the valid names to retrieve warnings for These are the valid names to retrieve warnings for
''' """
return 'https://warnung.bund.de/assets/json/converted_gemeinden.json' return "https://warnung.bund.de/assets/json/converted_gemeinden.json"

View File

@ -1,4 +1,4 @@
''' """
Copyright (C) 2021 Alexander "PapaTutuWawa" Copyright (C) 2021 Alexander "PapaTutuWawa"
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
@ -13,70 +13,82 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. along with this program. If not, see <https://www.gnu.org/licenses/>.
''' """
from collections import namedtuple from collections import namedtuple
from janine.modules.janine.helpers import find_one from janine.modules.janine.helpers import find_one
Warning_ = namedtuple('Warning_', ['id', Warning_ = namedtuple(
'sent', "Warning_",
'effective_from', [
'expires', "id",
'urgency', "sent",
'sender', "effective_from",
'headline', "expires",
'description', "urgency",
'instruction', "sender",
'landkreise']) "headline",
"description",
"instruction",
"landkreise",
],
)
def stub_warning(id_): def stub_warning(id_):
''' """
Returns a stubbed warning for loading warnings from disk. Returns a stubbed warning for loading warnings from disk.
The only real attribute is the @id_ . The only real attribute is the @id_ .
''' """
return Warning_(id=id_, return Warning_(
sent='', id=id_,
effective_from='', sent="",
expires='', effective_from="",
urgency='', expires="",
sender='', urgency="",
headline='', sender="",
description='', headline="",
instruction='', description="",
landkreise=[]) instruction="",
landkreise=[],
)
def to_warning(data): def to_warning(data):
''' """
Returns a Warning given the raw data Returns a Warning given the raw data
''' """
info = find_one(lambda x: 'headline' in x.keys(), data['info']) info = find_one(lambda x: "headline" in x.keys(), data["info"])
return Warning_(id=data['identifier'], return Warning_(
sent=data['sent'], id=data["identifier"],
sent=data["sent"],
# Not all items have to have those # Not all items have to have those
effective_from=info.get('effective', 'N/A'), effective_from=info.get("effective", "N/A"),
# Not all items have to have those # Not all items have to have those
expires=info.get('expires', 'N/A'), expires=info.get("expires", "N/A"),
urgency=info['urgency'], urgency=info["urgency"],
# Not all items have to have those # Not all items have to have those
sender=info.get('senderName', 'N/A'), sender=info.get("senderName", "N/A"),
headline=info['headline'], headline=info["headline"],
description=info['description'], description=info["description"],
instruction=info.get('instruction', ''), instruction=info.get("instruction", ""),
landkreise=get_landkreise(data)) landkreise=get_landkreise(data),
)
def get_landkreise(data): def get_landkreise(data):
''' """
Returns the list of Landkreise relevant to the warning in @data Returns the list of Landkreise relevant to the warning in @data
''' """
info = find_one(lambda e: 'area' in e.keys(), data['info']) info = find_one(lambda e: "area" in e.keys(), data["info"])
geocode = find_one(lambda e: 'geocode' in e.keys(), info['area']) geocode = find_one(lambda e: "geocode" in e.keys(), info["area"])
# Note: Some items may have multiple Landkreise # Note: Some items may have multiple Landkreise
return [e['valueName'] for e in geocode['geocode']] return [e["valueName"] for e in geocode["geocode"]]
def parse_data(data): def parse_data(data):
''' """
Reads the remote response, parses it and returns a list of warnings. Reads the remote response, parses it and returns a list of warnings.
''' """
return [to_warning(el) for el in data] return [to_warning(el) for el in data]