From 4a530826a8be852032076274a38be30e3d186ff0 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Thu, 13 Jan 2022 00:05:02 +0200 Subject: [PATCH 1/6] Rewrite TG integration, support for other webhooks --- kaehmy/admin.py | 3 +- .../migrations/0006_delete_telegramchannel.py | 16 ++ kaehmy/models.py | 15 -- kaehmy/tgbot.py | 38 --- kaehmy/views.py | 15 +- locale/en/LC_MESSAGES/django.po | 230 +++++++++++------- locale/fi/LC_MESSAGES/django.po | 212 +++++++++------- ohlhafv/views.py | 13 +- poetry.lock | 17 +- pyproject.toml | 1 + webapp/admin.py | 4 +- ...basewebhook_genericwebhook_telegramhook.py | 59 +++++ webapp/models.py | 72 ++++++ webapp/translation.py | 15 ++ webapp/webhook.py | 9 + 15 files changed, 461 insertions(+), 258 deletions(-) create mode 100644 kaehmy/migrations/0006_delete_telegramchannel.py delete mode 100644 kaehmy/tgbot.py create mode 100644 webapp/migrations/0078_basewebhook_genericwebhook_telegramhook.py create mode 100644 webapp/webhook.py diff --git a/kaehmy/admin.py b/kaehmy/admin.py index a6f11cf..f730ca5 100644 --- a/kaehmy/admin.py +++ b/kaehmy/admin.py @@ -1,10 +1,9 @@ from django.contrib import admin from modeltranslation.admin import TranslationAdmin -from kaehmy.models import Application, Comment, CustomRole, PresetRole, TelegramChannel +from kaehmy.models import Application, Comment, CustomRole, PresetRole admin.site.register(Application) admin.site.register(Comment) admin.site.register(CustomRole) admin.site.register(PresetRole, TranslationAdmin) -admin.site.register(TelegramChannel) diff --git a/kaehmy/migrations/0006_delete_telegramchannel.py b/kaehmy/migrations/0006_delete_telegramchannel.py new file mode 100644 index 0000000..4d963c0 --- /dev/null +++ b/kaehmy/migrations/0006_delete_telegramchannel.py @@ -0,0 +1,16 @@ +# Generated by Django 2.2.26 on 2022-01-12 20:38 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('kaehmy', '0005_auto_20190312_1458'), + ] + + operations = [ + migrations.DeleteModel( + name='TelegramChannel', + ), + ] diff --git a/kaehmy/models.py b/kaehmy/models.py index f47374f..2133378 100644 --- a/kaehmy/models.py +++ b/kaehmy/models.py @@ -148,18 +148,3 @@ class Application(CommentParent): def has_any_board_role(self): return self.preset_roles.filter(is_board=True).exists() or self.custom_roles.filter(is_board=True) - - -# Telegram channel entry for Kaehmys -class TelegramChannel(models.Model): - """Model containing the channel id of a Telegram chat""" - - class Meta: - verbose_name = _('Telegram channel') - verbose_name_plural = _('Telegram channels') - - name = models.CharField(max_length=255) - channel_id = models.CharField(max_length=255, unique=True) - - def __str__(self): - return 'Telegram channel: "{}"'.format(self.name) diff --git a/kaehmy/tgbot.py b/kaehmy/tgbot.py deleted file mode 100644 index 248f7eb..0000000 --- a/kaehmy/tgbot.py +++ /dev/null @@ -1,38 +0,0 @@ -''' -A telegram bot api for whatever purposes. -TODO: kaehmy app is definitely not correct place for this -''' -import logging -import requests -from django.conf import settings -from kaehmy.models import TelegramChannel - - -class TelegramBot: - ''' - A telegram bot api for whatever purposes - Currently only able to broadcast stuff to all registered - channels using broadcast method. - ''' - - def __init__(self, api_token=None): - - self.api_token = api_token or settings.TELEGRAM_BOT_TOKEN - self.send_message_url = "https://api.telegram.org/bot{}/sendMessage".format(self.api_token) - - def broadcast(self, message): - channels_ids = TelegramChannel.objects.values_list("channel_id", flat=True) - for id_ in channels_ids: - self.send_message(id_, message) - - def send_message(self, channel_id, message): - ''' - Send message to a chat with given channel_id - ''' - data = { - 'chat_id': channel_id, - 'text': message, - 'parse_mode': 'Markdown' - } - resp = requests.post(self.send_message_url, json=data) - logging.debug(resp.content) diff --git a/kaehmy/views.py b/kaehmy/views.py index 8e7ab29..8f839f2 100644 --- a/kaehmy/views.py +++ b/kaehmy/views.py @@ -13,10 +13,11 @@ from dealer.git import git from sikweb.settings import URL from members.views.utils import * -from kaehmy.models import Application, CustomRole, PresetRole, TelegramChannel +from kaehmy.models import Application, CustomRole, PresetRole from kaehmy.forms import ApplicationForm, CommentForm from kaehmy.tables import ExportTable from webapp.utils import send_email +from webapp.webhook import processHooks @ensure_csrf_cookie @@ -135,17 +136,7 @@ def submit(request, *args, **kwargs): send_email(email, subject, body) logging.debug('Sent kaehmy email to recipient <{}>'.format(email)) - CHAT_IDS = [channel.channel_id for channel in TelegramChannel.objects.all()] - for chat_id in CHAT_IDS: - tg_string = 'https://api.telegram.org/bot{}/sendMessage?chat_id={}&text={}'.format( - settings.TELEGRAM_BOT_TOKEN, - chat_id, - 'Uusi New kaehmy! {} -> {}'.format(name, url) - ) - response = requests.get(tg_string).json() - logging.debug('Telegram API response:\n{}'.format(response)) - logging.debug('Sent kaehmy announcement to {} channels.'.format(len(CHAT_IDS))) - + processHooks(message=f'Uusi New kaehmy! {name} -> {url}', eventType="kaehmy") else: context = { 'error': form.errors diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index 7b1289e..603910b 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-18 21:36+0200\n" +"POT-Creation-Date: 2022-01-12 23:55+0200\n" "PO-Revision-Date: 2017-11-02 23:09+0200\n" "Last-Translator: \n" "Language-Team: \n" @@ -37,7 +37,7 @@ msgstr "Sössö articles" msgid "Today's lunch" msgstr "" -#: infoscreen/models.py:212 webapp/models.py:70 +#: infoscreen/models.py:212 webapp/models.py:73 msgid "Events" msgstr "Events" @@ -112,8 +112,8 @@ msgstr "Preview" msgid "Delete" msgstr "Delete" -#: infoscreen/templates/tabs/add_remove.html:23 kaehmy/models.py:62 -#: kaehmy/templates/list.html:36 webapp/models.py:144 webapp/models.py:173 +#: infoscreen/templates/tabs/add_remove.html:23 kaehmy/models.py:57 +#: kaehmy/templates/list.html:36 webapp/models.py:147 webapp/models.py:176 msgid "Name" msgstr "Name" @@ -189,7 +189,7 @@ msgstr "Phone number (not public)" msgid "Custom roles" msgstr "Custom roles" -#: kaehmy/forms.py:49 kaehmy/templates/kaehmy.html:43 +#: kaehmy/forms.py:49 kaehmy/templates/kaehmy.html:42 msgid "Preset roles" msgstr "Preset roles" @@ -201,156 +201,148 @@ msgstr "Invalid phone number" msgid "Custom role with the same name already exists." msgstr "Custom role with the same name already exists." -#: kaehmy/models.py:18 +#: kaehmy/models.py:13 msgid "Kaehmy" msgstr "Kaehmy" -#: kaehmy/models.py:25 +#: kaehmy/models.py:20 msgid "Corporate affairs" msgstr "Corporate affairs" -#: kaehmy/models.py:26 webapp/templates/freshmen.html:10 +#: kaehmy/models.py:21 webapp/templates/freshmen.html:10 #: webapp/templates/navigation.html:8 msgid "Freshmen" msgstr "Freshmen" -#: kaehmy/models.py:27 webapp/templates/international.html:10 +#: kaehmy/models.py:22 webapp/templates/international.html:10 #: webapp/templates/navigation.html:14 msgid "International" msgstr "International" -#: kaehmy/models.py:28 +#: kaehmy/models.py:23 msgid "External affairs" msgstr "External affairs" -#: kaehmy/models.py:29 +#: kaehmy/models.py:24 msgid "Media" msgstr "" -#: kaehmy/models.py:30 +#: kaehmy/models.py:25 msgid "Technology" msgstr "" -#: kaehmy/models.py:31 +#: kaehmy/models.py:26 msgid "Wellbeing" msgstr "" -#: kaehmy/models.py:32 +#: kaehmy/models.py:27 msgid "Elepaja" msgstr "" -#: kaehmy/models.py:33 +#: kaehmy/models.py:28 msgid "Ceremonies" msgstr "" -#: kaehmy/models.py:34 +#: kaehmy/models.py:29 msgid "Studies" msgstr "" -#: kaehmy/models.py:35 +#: kaehmy/models.py:30 msgid "Sössö magazine" msgstr "Sössö magazine" -#: kaehmy/models.py:36 +#: kaehmy/models.py:31 msgid "Alumni relations" msgstr "Alumni relations" -#: kaehmy/models.py:37 +#: kaehmy/models.py:32 msgid "Others" msgstr "" -#: kaehmy/models.py:39 +#: kaehmy/models.py:34 msgid "Category" msgstr "" -#: kaehmy/models.py:45 +#: kaehmy/models.py:40 msgid "Description" msgstr "Description" -#: kaehmy/models.py:48 +#: kaehmy/models.py:43 msgid "Preset kaehmy role" msgstr "Preset kaehmy role" -#: kaehmy/models.py:49 +#: kaehmy/models.py:44 msgid "Preset kaehmy roles" msgstr "Preset kaehmy roles" -#: kaehmy/models.py:56 +#: kaehmy/models.py:51 msgid "Custom kaehmy role" msgstr "Custom kaehmy role" -#: kaehmy/models.py:57 +#: kaehmy/models.py:52 msgid "Custom kaehmy roles" msgstr "Custom kaehmy roles" -#: kaehmy/models.py:63 kaehmy/templates/list.html:40 members/models.py:14 +#: kaehmy/models.py:58 kaehmy/templates/list.html:40 members/models.py:14 msgid "Email" msgstr "Email" -#: kaehmy/models.py:64 +#: kaehmy/models.py:59 msgid "Timestamp" msgstr "" -#: kaehmy/models.py:78 +#: kaehmy/models.py:73 msgid "Kaehmykommentti" msgstr "Kaehmy comment" -#: kaehmy/models.py:79 +#: kaehmy/models.py:74 msgid "Kaehmykommentit" msgstr "Kaehmy comments" -#: kaehmy/models.py:81 ohlhafv/models.py:36 +#: kaehmy/models.py:76 ohlhafv/models.py:36 msgid "Message" msgstr "" -#: kaehmy/models.py:100 kaehmy/templates/kaehmy.html:12 +#: kaehmy/models.py:95 kaehmy/templates/kaehmy.html:12 msgid "Kaehmylomake" msgstr "Kaehmy application" -#: kaehmy/models.py:101 +#: kaehmy/models.py:96 msgid "Kaehmylomakkeet" msgstr "Kaehmy applications" -#: kaehmy/models.py:104 +#: kaehmy/models.py:99 msgid "Phone number" msgstr "" -#: kaehmy/models.py:105 +#: kaehmy/models.py:100 msgid "Year" msgstr "" -#: kaehmy/models.py:106 +#: kaehmy/models.py:101 msgid "Text" msgstr "" -#: kaehmy/models.py:108 +#: kaehmy/models.py:103 msgid "Custom role name" msgstr "" -#: kaehmy/models.py:110 webapp/models.py:174 +#: kaehmy/models.py:105 webapp/models.py:177 msgid "Board member" msgstr "Board member" -#: kaehmy/models.py:118 +#: kaehmy/models.py:113 msgid "Kaehmy application: {}" msgstr "Kaehmy application: {}" -#: kaehmy/models.py:140 +#: kaehmy/models.py:135 msgid "Board: {}" msgstr "" -#: kaehmy/models.py:146 +#: kaehmy/models.py:141 msgid "Official: {}" msgstr "" -#: kaehmy/models.py:163 -msgid "Telegram channel" -msgstr "" - -#: kaehmy/models.py:164 -msgid "Telegram channels" -msgstr "" - #: kaehmy/tables.py:13 msgid "Roles" msgstr "" @@ -427,31 +419,36 @@ msgid "Päivämääriä & deadlineja" msgstr "Dates and deadlines" #: kaehmy/templates/kaehmy.html:31 -msgid "Hallitustyrkkypaneeli" -msgstr "Panel for board applicants" +#, fuzzy +#| msgid "Vaalikokous, osa 1 (puheenjohtajan valinta)" +msgid "Vaalikokous, osa 1 (puheenjohtajan valinta) ja hallitustyrkkypaneeli" +msgstr "Election meeting, part 1 (chairman election)" #: kaehmy/templates/kaehmy.html:32 -msgid "Vaalikokous, osa 1 (puheenjohtajan valinta)" -msgstr "Election meeting, part 1 (chairman election)" +msgid "Vaalikokous, osa 2 (hallituksen valinta)" +msgstr "Election meeting, part 2 (board election)" #: kaehmy/templates/kaehmy.html:33 msgid "Toimikunta-appro" msgstr "Guild committee crawl" #: kaehmy/templates/kaehmy.html:34 -msgid "Vaalikokous, osa 2 (hallituksen valinta)" -msgstr "Election meeting, part 2 (board election)" - -#: kaehmy/templates/kaehmy.html:35 msgid "Vaalikokous, osa 3 (toimarien valinta)" msgstr "Election meeting, part 3 (non-board election)" -#: kaehmy/templates/kaehmy.html:78 -#, python-format +#: kaehmy/templates/kaehmy.html:77 +#, fuzzy, python-format +#| msgid "" +#| "\n" +#| " Hyväksyn tietosuojaselosteen ja " +#| "tietojeni tallentamisen.\n" +#| " " msgid "" "\n" -" Hyväksyn tietosuojaselosteen ja tietojeni " "tallentamisen.\n" " " @@ -463,7 +460,7 @@ msgstr "" "of personal data.\n" " " -#: kaehmy/templates/kaehmy.html:84 members/templates/settings.html:23 +#: kaehmy/templates/kaehmy.html:83 members/templates/settings.html:23 msgid "Submit" msgstr "Submit" @@ -639,12 +636,16 @@ msgid "Hienoa! Jäsenhakemuksesi on nyt lähetetty." msgstr "Amazing! Your membership application has been sent." #: members/templates/application_success.html:9 +#, fuzzy +#| msgid "" +#| "Vahvistusviesti on lähetetty sähköpostiisi. Ota yhteyttä " +#| "admin@sahkoinsinoorikilta.fi.fi jos viestiä ei näy." msgid "" -"Vahvistusviesti on lähetetty sähköpostiisi. Ota yhteyttä admin@sahkoinsinoorikilta.fi." -"fi jos viestiä ei näy." +"Vahvistusviesti on lähetetty sähköpostiisi. Ota yhteyttä " +"admin@sahkoinsinoorikilta.fi jos viestiä ei näy." msgstr "" -"Confirmation email is sent to given email address. Contact admin@sahkoinsinoorikilta.fi." -"fi if you didn't receive it." +"Confirmation email is sent to given email address. Contact " +"admin@sahkoinsinoorikilta.fi.fi if you didn't receive it." #: members/templates/application_success.html:10 msgid "Takaisin Sähköinsinöörikillan web-sivuille" @@ -914,11 +915,11 @@ msgstr "Payments in register:" msgid "Language" msgstr "Language" -#: members/templates/settings.html:20 sikweb/base.py:217 +#: members/templates/settings.html:20 sikweb/base.py:216 msgid "Finnish" msgstr "Finnish" -#: members/templates/settings.html:21 sikweb/base.py:218 +#: members/templates/settings.html:21 sikweb/base.py:217 msgid "English" msgstr "English" @@ -1100,7 +1101,9 @@ msgid "Challenge" msgstr "Challenge" #: ohlhafv/views.py:44 -msgid "Sinut on haastettu Øhlhäfviin!" +#, fuzzy +#| msgid "Sinut on haastettu Øhlhäfviin!" +msgid "Sinut on haastettu Ohlhäfviin!" msgstr "You have been challenged at Ohlhafv!" #: templates/admin/base_site.html:44 @@ -1112,91 +1115,135 @@ msgstr "Go" msgid "Aalto-yliopiston Sähköinsinöörikilta ry" msgstr "Aalto-yliopiston Sähköinsinöörikilta ry" -#: webapp/models.py:18 +#: webapp/models.py:21 msgid "Webapp" msgstr "Webapp" -#: webapp/models.py:26 +#: webapp/models.py:29 msgid "Tag" msgstr "Tag" -#: webapp/models.py:27 +#: webapp/models.py:30 msgid "Tags" msgstr "Tags" -#: webapp/models.py:34 +#: webapp/models.py:37 msgid "Tag: {}" msgstr "Tag: {}" -#: webapp/models.py:52 +#: webapp/models.py:55 msgid "Feed" msgstr "" -#: webapp/models.py:53 +#: webapp/models.py:56 msgid "Feeds" msgstr "" -#: webapp/models.py:61 webapp/models.py:80 webapp/models.py:119 -#: webapp/models.py:152 webapp/models.py:198 +#: webapp/models.py:64 webapp/models.py:83 webapp/models.py:122 +#: webapp/models.py:155 webapp/models.py:201 msgid "Deleted: " msgstr "Deleted: " -#: webapp/models.py:62 +#: webapp/models.py:65 msgid "{}Feed: {}" msgstr "" -#: webapp/models.py:69 +#: webapp/models.py:72 msgid "Event" msgstr "" -#: webapp/models.py:81 +#: webapp/models.py:84 msgid "{}Event: {}" msgstr "" -#: webapp/models.py:91 +#: webapp/models.py:93 msgid "Template question" msgstr "" -#: webapp/models.py:92 +#: webapp/models.py:94 msgid "Template questions" msgstr "" -#: webapp/models.py:98 +#: webapp/models.py:101 msgid "Template questions: {}" msgstr "" -#: webapp/models.py:105 +#: webapp/models.py:108 msgid "Signup form" msgstr "" -#: webapp/models.py:106 +#: webapp/models.py:109 msgid "Signup forms" msgstr "" -#: webapp/models.py:120 +#: webapp/models.py:123 msgid "#{} {}{}" msgstr "" -#: webapp/models.py:138 +#: webapp/models.py:141 msgid "Sign-up" msgstr "" -#: webapp/models.py:139 +#: webapp/models.py:142 msgid "Sign-ups" msgstr "" -#: webapp/models.py:178 +#: webapp/models.py:181 msgid "board member" msgstr "board member" -#: webapp/models.py:185 +#: webapp/models.py:188 msgid "JobAd" msgstr "" -#: webapp/models.py:186 +#: webapp/models.py:189 msgid "JobAds" msgstr "" +#: webapp/models.py:211 +#, fuzzy +#| msgid "Kaehmy" +msgid "Hook Kaehmys" +msgstr "Kaehmy" + +#: webapp/models.py:212 +#, fuzzy +#| msgid "Total challenges:" +msgid "Hook Ohlhafv challenges" +msgstr "Total challenges:" + +#: webapp/models.py:213 +msgid "Hook published news" +msgstr "" + +#: webapp/models.py:214 +msgid "Hook published Job Ads" +msgstr "" + +#: webapp/models.py:215 +msgid "Hook published events" +msgstr "" + +#: webapp/models.py:216 +msgid "Hook opened signups" +msgstr "" + +#: webapp/models.py:241 +msgid "Webhook" +msgstr "" + +#: webapp/models.py:242 +msgid "Webhooks" +msgstr "" + +#: webapp/models.py:256 +msgid "Telegram channel" +msgstr "" + +#: webapp/models.py:257 +msgid "Telegram channels" +msgstr "" + #: webapp/templates/contact.html:9 webapp/templates/navigation.html:20 msgid "Contact" msgstr "Contact" @@ -1240,3 +1287,6 @@ msgstr "Sössö" #: webapp/templates/navigation.html:24 msgid "Corporate" msgstr "Corporate" + +#~ msgid "Hallitustyrkkypaneeli" +#~ msgstr "Panel for board applicants" diff --git a/locale/fi/LC_MESSAGES/django.po b/locale/fi/LC_MESSAGES/django.po index cf602f6..480a23c 100644 --- a/locale/fi/LC_MESSAGES/django.po +++ b/locale/fi/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-18 21:36+0200\n" +"POT-Creation-Date: 2022-01-12 23:55+0200\n" "PO-Revision-Date: 2017-11-02 23:04+0200\n" "Last-Translator: \n" "Language-Team: \n" @@ -38,7 +38,7 @@ msgstr "Sössön artikkelit" msgid "Today's lunch" msgstr "Päivän lounas" -#: infoscreen/models.py:212 webapp/models.py:70 +#: infoscreen/models.py:212 webapp/models.py:73 msgid "Events" msgstr "Tapahtumat" @@ -113,8 +113,8 @@ msgstr "Esikatsele" msgid "Delete" msgstr "Poista" -#: infoscreen/templates/tabs/add_remove.html:23 kaehmy/models.py:62 -#: kaehmy/templates/list.html:36 webapp/models.py:144 webapp/models.py:173 +#: infoscreen/templates/tabs/add_remove.html:23 kaehmy/models.py:57 +#: kaehmy/templates/list.html:36 webapp/models.py:147 webapp/models.py:176 msgid "Name" msgstr "Nimi" @@ -190,7 +190,7 @@ msgstr "Puhelinnumero (ei julkinen)" msgid "Custom roles" msgstr "Uudet virat" -#: kaehmy/forms.py:49 kaehmy/templates/kaehmy.html:43 +#: kaehmy/forms.py:49 kaehmy/templates/kaehmy.html:42 msgid "Preset roles" msgstr "Kaehmyvirat" @@ -202,156 +202,148 @@ msgstr "Virheellinen puhelinnumero" msgid "Custom role with the same name already exists." msgstr "Samanniminen virka on jo olemassa." -#: kaehmy/models.py:18 +#: kaehmy/models.py:13 msgid "Kaehmy" msgstr "Kaehmy" -#: kaehmy/models.py:25 +#: kaehmy/models.py:20 msgid "Corporate affairs" msgstr "Yrityssuhteet" -#: kaehmy/models.py:26 webapp/templates/freshmen.html:10 +#: kaehmy/models.py:21 webapp/templates/freshmen.html:10 #: webapp/templates/navigation.html:8 msgid "Freshmen" msgstr "Fuksit" -#: kaehmy/models.py:27 webapp/templates/international.html:10 +#: kaehmy/models.py:22 webapp/templates/international.html:10 #: webapp/templates/navigation.html:14 msgid "International" msgstr "International" -#: kaehmy/models.py:28 +#: kaehmy/models.py:23 msgid "External affairs" msgstr "Ulkosuhteet" -#: kaehmy/models.py:29 +#: kaehmy/models.py:24 msgid "Media" msgstr "Media" -#: kaehmy/models.py:30 +#: kaehmy/models.py:25 msgid "Technology" msgstr "Teknologia" -#: kaehmy/models.py:31 +#: kaehmy/models.py:26 msgid "Wellbeing" msgstr "Hyvinvointi" -#: kaehmy/models.py:32 +#: kaehmy/models.py:27 msgid "Elepaja" msgstr "Elepaja" -#: kaehmy/models.py:33 +#: kaehmy/models.py:28 msgid "Ceremonies" msgstr "Hupitapahtumat" -#: kaehmy/models.py:34 +#: kaehmy/models.py:29 msgid "Studies" msgstr "Opinnot" -#: kaehmy/models.py:35 +#: kaehmy/models.py:30 msgid "Sössö magazine" msgstr "Kiltalehti Sössö" -#: kaehmy/models.py:36 +#: kaehmy/models.py:31 msgid "Alumni relations" msgstr "Alumnisuhteet" -#: kaehmy/models.py:37 +#: kaehmy/models.py:32 msgid "Others" msgstr "Muut" -#: kaehmy/models.py:39 +#: kaehmy/models.py:34 msgid "Category" msgstr "Kategoria" -#: kaehmy/models.py:45 +#: kaehmy/models.py:40 msgid "Description" msgstr "Kuvaus" -#: kaehmy/models.py:48 +#: kaehmy/models.py:43 msgid "Preset kaehmy role" msgstr "Kaehmyvirka" -#: kaehmy/models.py:49 +#: kaehmy/models.py:44 msgid "Preset kaehmy roles" msgstr "Kaehmyvirat" -#: kaehmy/models.py:56 +#: kaehmy/models.py:51 msgid "Custom kaehmy role" msgstr "Uusi virka" -#: kaehmy/models.py:57 +#: kaehmy/models.py:52 msgid "Custom kaehmy roles" msgstr "Uudet kaehmyvirat" -#: kaehmy/models.py:63 kaehmy/templates/list.html:40 members/models.py:14 +#: kaehmy/models.py:58 kaehmy/templates/list.html:40 members/models.py:14 msgid "Email" msgstr "Sähköposti" -#: kaehmy/models.py:64 +#: kaehmy/models.py:59 msgid "Timestamp" msgstr "Aikaleima" -#: kaehmy/models.py:78 +#: kaehmy/models.py:73 msgid "Kaehmykommentti" msgstr "Kaehmykommentti" -#: kaehmy/models.py:79 +#: kaehmy/models.py:74 msgid "Kaehmykommentit" msgstr "Kaehmykommentit" -#: kaehmy/models.py:81 ohlhafv/models.py:36 +#: kaehmy/models.py:76 ohlhafv/models.py:36 msgid "Message" msgstr "Viesti" -#: kaehmy/models.py:100 kaehmy/templates/kaehmy.html:12 +#: kaehmy/models.py:95 kaehmy/templates/kaehmy.html:12 msgid "Kaehmylomake" msgstr "Kaehmylomake" -#: kaehmy/models.py:101 +#: kaehmy/models.py:96 msgid "Kaehmylomakkeet" msgstr "Kaehmylomakkeet" -#: kaehmy/models.py:104 +#: kaehmy/models.py:99 msgid "Phone number" msgstr "Puhelinnumero" -#: kaehmy/models.py:105 +#: kaehmy/models.py:100 msgid "Year" msgstr "Vuosi" -#: kaehmy/models.py:106 +#: kaehmy/models.py:101 msgid "Text" msgstr "Teksti" -#: kaehmy/models.py:108 +#: kaehmy/models.py:103 msgid "Custom role name" msgstr "Uusi virka" -#: kaehmy/models.py:110 webapp/models.py:174 +#: kaehmy/models.py:105 webapp/models.py:177 msgid "Board member" msgstr "Hallituksen jäsen" -#: kaehmy/models.py:118 +#: kaehmy/models.py:113 msgid "Kaehmy application: {}" msgstr "Kaehmy: {}" -#: kaehmy/models.py:140 +#: kaehmy/models.py:135 msgid "Board: {}" msgstr "Hallitus: {}" -#: kaehmy/models.py:146 +#: kaehmy/models.py:141 msgid "Official: {}" msgstr "Toimari: {}" -#: kaehmy/models.py:163 -msgid "Telegram channel" -msgstr "Telegram-kanava" - -#: kaehmy/models.py:164 -msgid "Telegram channels" -msgstr "Telegram-kanavat" - #: kaehmy/tables.py:13 msgid "Roles" msgstr "Roolit" @@ -404,8 +396,8 @@ msgstr "" " Koska lista ei ole koskaan täydellinen, voit myös ehdottaa ihan " "uutta toimenkuvaa.\n" " Jos sinulla on kysyttävää mistä tahansa virasta, kannattaa " -"konsultoida kaehmyopasta \n" +"konsultoida kaehmyopasta \n" " tai olla yhteydessä kyseistä virkaa tänä vuonna toimittavaan " "henkilöön." @@ -430,37 +422,33 @@ msgid "Päivämääriä & deadlineja" msgstr "Päivämääriä & deadlineja" #: kaehmy/templates/kaehmy.html:31 -msgid "Hallitustyrkkypaneeli" -msgstr "" +msgid "Vaalikokous, osa 1 (puheenjohtajan valinta) ja hallitustyrkkypaneeli" +msgstr "Vaalikokous, osa 1 (puheenjohtajan valinta)" #: kaehmy/templates/kaehmy.html:32 -msgid "Vaalikokous, osa 1 (puheenjohtajan valinta)" -msgstr "Vaalikokous, osa 1 (puheenjohtajan valinta)" +msgid "Vaalikokous, osa 2 (hallituksen valinta)" +msgstr "Vaalikokous, osa 2 (hallituksen valinta)" #: kaehmy/templates/kaehmy.html:33 msgid "Toimikunta-appro" msgstr "" #: kaehmy/templates/kaehmy.html:34 -msgid "Vaalikokous, osa 2 (hallituksen valinta)" -msgstr "Vaalikokous, osa 2 (hallituksen valinta)" - -#: kaehmy/templates/kaehmy.html:35 msgid "Vaalikokous, osa 3 (toimarien valinta)" msgstr "Vaalikokous, osa 3 (toimarien valinta)" -#: kaehmy/templates/kaehmy.html:78 +#: kaehmy/templates/kaehmy.html:77 #, python-format msgid "" "\n" -" Hyväksyn tietosuojaselosteen ja tietojeni " +" Hyväksyn tietosuojaselosteen ja tietojeni " "tallentamisen.\n" " " msgstr "" -#: kaehmy/templates/kaehmy.html:84 members/templates/settings.html:23 +#: kaehmy/templates/kaehmy.html:83 members/templates/settings.html:23 msgid "Submit" msgstr "Lisää" @@ -637,8 +625,8 @@ msgstr "Hienoa! Jäsenhakemuksesi on nyt lähetetty." #: members/templates/application_success.html:9 msgid "" -"Vahvistusviesti on lähetetty sähköpostiisi. Ota yhteyttä admin@sahkoinsinoorikilta.fi." -"fi jos viestiä ei näy." +"Vahvistusviesti on lähetetty sähköpostiisi. Ota yhteyttä " +"admin@sahkoinsinoorikilta.fi jos viestiä ei näy." msgstr "" #: members/templates/application_success.html:10 @@ -905,11 +893,11 @@ msgstr "Maksutapahtumia:" msgid "Language" msgstr "Kieli" -#: members/templates/settings.html:20 sikweb/base.py:217 +#: members/templates/settings.html:20 sikweb/base.py:216 msgid "Finnish" msgstr "suomi" -#: members/templates/settings.html:21 sikweb/base.py:218 +#: members/templates/settings.html:21 sikweb/base.py:217 msgid "English" msgstr "englanti" @@ -1018,11 +1006,11 @@ msgstr "Øhlhäfv" #: ohlhafv/models.py:22 msgid "Ohlhafv challenge" -msgstr "Ohlhafv haaste" +msgstr "Øhlhäfv-haaste" #: ohlhafv/models.py:23 msgid "Ohlhafv challenges" -msgstr "Ohlhafv haasteet" +msgstr "Øhlhäfv-haasteet" #: ohlhafv/models.py:29 msgid "Team Challenge (1 x 0.33 L, 2 x 0.5 L, 1 x 1.0 L)" @@ -1046,7 +1034,7 @@ msgstr "Sarja" #: ohlhafv/models.py:40 msgid "Ohlhafv challenge: {} vs. {}" -msgstr "Ohlhafv-haaste: {} vs. {}" +msgstr "Øhlhäfv-haaste: {} vs. {}" #: ohlhafv/templates/email.html:4 msgid "on haastanut sinut oluenjuontimittelöön" @@ -1101,91 +1089,131 @@ msgstr "Vaihda" msgid "Aalto-yliopiston Sähköinsinöörikilta ry" msgstr "Aalto-yliopiston Sähköinsinöörikilta ry" -#: webapp/models.py:18 +#: webapp/models.py:21 msgid "Webapp" msgstr "Nettisivut" -#: webapp/models.py:26 +#: webapp/models.py:29 msgid "Tag" msgstr "Tunniste" -#: webapp/models.py:27 +#: webapp/models.py:30 msgid "Tags" msgstr "Tunnisteet" -#: webapp/models.py:34 +#: webapp/models.py:37 msgid "Tag: {}" msgstr "Tunniste: {}" -#: webapp/models.py:52 +#: webapp/models.py:55 msgid "Feed" msgstr "Uutinen" -#: webapp/models.py:53 +#: webapp/models.py:56 msgid "Feeds" msgstr "Uutiset" -#: webapp/models.py:61 webapp/models.py:80 webapp/models.py:119 -#: webapp/models.py:152 webapp/models.py:198 +#: webapp/models.py:64 webapp/models.py:83 webapp/models.py:122 +#: webapp/models.py:155 webapp/models.py:201 msgid "Deleted: " msgstr "Poistettu: " -#: webapp/models.py:62 +#: webapp/models.py:65 msgid "{}Feed: {}" msgstr "{}Uutinen: {}" -#: webapp/models.py:69 +#: webapp/models.py:72 msgid "Event" msgstr "Tapahtuma" -#: webapp/models.py:81 +#: webapp/models.py:84 msgid "{}Event: {}" msgstr "{}Tapahtuma: {}" -#: webapp/models.py:91 +#: webapp/models.py:93 msgid "Template question" msgstr "Vakiokysymys" -#: webapp/models.py:92 +#: webapp/models.py:94 msgid "Template questions" msgstr "Vakiokysymykset" -#: webapp/models.py:98 +#: webapp/models.py:101 msgid "Template questions: {}" msgstr "Vakiokysymykset: {}" -#: webapp/models.py:105 +#: webapp/models.py:108 msgid "Signup form" msgstr "Ilmoittautumislomake" -#: webapp/models.py:106 +#: webapp/models.py:109 msgid "Signup forms" msgstr "Ilmoittautumislomakkeet" -#: webapp/models.py:120 +#: webapp/models.py:123 msgid "#{} {}{}" msgstr "" -#: webapp/models.py:138 +#: webapp/models.py:141 msgid "Sign-up" msgstr "Ilmoittautuminen" -#: webapp/models.py:139 +#: webapp/models.py:142 msgid "Sign-ups" msgstr "Ilmoittautumiset" -#: webapp/models.py:178 +#: webapp/models.py:181 msgid "board member" msgstr "hallituksen jäsen" -#: webapp/models.py:185 +#: webapp/models.py:188 msgid "JobAd" msgstr "Työpaikkailmoitus" -#: webapp/models.py:186 +#: webapp/models.py:189 msgid "JobAds" msgstr "Työpaikkailmoitukset" +#: webapp/models.py:211 +msgid "Hook Kaehmys" +msgstr "Lähetä Kähmyt" + +#: webapp/models.py:212 +msgid "Hook Ohlhafv challenges" +msgstr "Lähetä Øhlhäfv-haasteet" + +#: webapp/models.py:213 +msgid "Hook published news" +msgstr "Lähetä julkaistut uutiset" + +#: webapp/models.py:214 +msgid "Hook published Job Ads" +msgstr "Lähetä työpaikkailmoitukset" + +#: webapp/models.py:215 +msgid "Hook published events" +msgstr "Lähetä julkaistut tapahtumat" + +#: webapp/models.py:216 +msgid "Hook opened signups" +msgstr "Lähetä auenneet ilmot" + +#: webapp/models.py:241 +msgid "Webhook" +msgstr "Webhook" + +#: webapp/models.py:242 +msgid "Webhooks" +msgstr "Webhookit" + +#: webapp/models.py:256 +msgid "Telegram channel" +msgstr "Telegram-kanava" + +#: webapp/models.py:257 +msgid "Telegram channels" +msgstr "Telegram-kanavat" + #: webapp/templates/contact.html:9 webapp/templates/navigation.html:20 msgid "Contact" msgstr "Yhteystiedot" diff --git a/ohlhafv/views.py b/ohlhafv/views.py index 76067e0..b62eaf0 100644 --- a/ohlhafv/views.py +++ b/ohlhafv/views.py @@ -20,7 +20,7 @@ from ohlhafv.models import OhlhafvChallenge from ohlhafv.forms import OhlhafvForm from ohlhafv.tables import OhlhafvTable from webapp.utils import send_email -from kaehmy.tgbot import TelegramBot +from webapp.webhook import processHooks @require_http_methods(["GET"]) @@ -43,22 +43,21 @@ def ohlhafv_submit(request, *args, **kwargs): url = f'https://{URL}/ohlhafv/list' subject = _('Sinut on haastettu Ohlhäfviin!') - message = render_to_string( + email_body = render_to_string( 'ohlhafv:email.html', { 'challenge': challenge, 'url': url, } ) - send_email(email, subject, message) + send_email(email, subject, email_body) try: - tg_message = render_to_string( + webhook_message = render_to_string( 'ohlhafv:tgmsg.tpl', { 'challenge': challenge, 'url': url}) - bot = TelegramBot() - bot.broadcast(tg_message) - except Exception: # tg spam is not critical. Ignore on failure + processHooks(message=webhook_message, eventType="ohlhafv") + except Exception: pass logging.debug( diff --git a/poetry.lock b/poetry.lock index 15b2654..8b9b07f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -243,6 +243,17 @@ phonenumbers = {version = ">=7.0.2", optional = true, markers = "extra == \"phon phonenumbers = ["phonenumbers (>=7.0.2)"] phonenumberslite = ["phonenumberslite (>=7.0.2)"] +[[package]] +name = "django-polymorphic" +version = "3.1.0" +description = "Seamless polymorphic inheritance for Django models" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +Django = ">=2.1" + [[package]] name = "django-suit" version = "0.2.28" @@ -763,7 +774,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "755a353d6f70eab1933125429b38d1e7249f57a9a61769507a3ef624f2d3cddb" +content-hash = "9d958b16dbad0528e51f6e4a2022cb13fbd5c210ef5e4d9d898afdae373ad470" [metadata.files] attrs = [ @@ -900,6 +911,10 @@ django-phonenumber-field = [ {file = "django-phonenumber-field-4.0.0.tar.gz", hash = "sha256:d4580cc3352f4433962825f9927e6669852c1b40ec484fcb5a74064dabc1201a"}, {file = "django_phonenumber_field-4.0.0-py3-none-any.whl", hash = "sha256:2ca3bb0ada0ebc164bd903a981a34f1202a4294006e520b0da961bd7ce9f20a4"}, ] +django-polymorphic = [ + {file = "django-polymorphic-3.1.0.tar.gz", hash = "sha256:d6955b5308bf6e41dcb22ba7c96f00b51dfa497a8a5ab1e9c06c7951bf417bf8"}, + {file = "django_polymorphic-3.1.0-py3-none-any.whl", hash = "sha256:08bc4f4f4a773a19b2deced5a56deddd1ef56ebd15207bf4052e2901c25ef57e"}, +] django-suit = [ {file = "django-suit-0.2.28.tar.gz", hash = "sha256:bacd8a079fcc08deb6efd0d7f60241e3c319526939ae1abe8ccfbc1b03e97104"}, {file = "django_suit-0.2.28-py2.py3-none-any.whl", hash = "sha256:256412597ac8e9461780542eebb12b37f65ff702bf23de23d07d245510c64ff2"}, diff --git a/pyproject.toml b/pyproject.toml index 41d3e64..1556927 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,7 @@ gunicorn = "^20.1.0" Pillow = "^8.4.0" sendgrid = "^6.7.0" sentry-sdk = "^1.4.3" +django-polymorphic = "^3.1.0" [tool.poetry.dev-dependencies] coverage = "^5.5" diff --git a/webapp/admin.py b/webapp/admin.py index 32d910a..7ab6571 100644 --- a/webapp/admin.py +++ b/webapp/admin.py @@ -1,7 +1,7 @@ """File containing webapp app admin registers.""" from django.contrib import admin -from webapp.models import Feed, Tag, Event, Signup, SignupForm, TemplateQuestion, JobAd +from webapp.models import Feed, Tag, Event, Signup, SignupForm, TemplateQuestion, JobAd, BaseWebhook, GenericWebhook, TelegramHook from modeltranslation.admin import TranslationAdmin from django.contrib.auth.models import Permission # this is needed so that the models get registered for translation @@ -16,3 +16,5 @@ admin.site.register(SignupForm, TranslationAdmin) admin.site.register(Signup, TranslationAdmin) admin.site.register(TemplateQuestion, TranslationAdmin) admin.site.register(JobAd, TranslationAdmin) +admin.site.register(GenericWebhook, TranslationAdmin) +admin.site.register(TelegramHook, TranslationAdmin) diff --git a/webapp/migrations/0078_basewebhook_genericwebhook_telegramhook.py b/webapp/migrations/0078_basewebhook_genericwebhook_telegramhook.py new file mode 100644 index 0000000..a48ae4c --- /dev/null +++ b/webapp/migrations/0078_basewebhook_genericwebhook_telegramhook.py @@ -0,0 +1,59 @@ +# Generated by Django 2.2.26 on 2022-01-12 21:32 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('webapp', '0077_templatequestion_deleted'), + ] + + operations = [ + migrations.CreateModel( + name='BaseWebhook', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('url', models.URLField()), + ('kaehmy_submit', models.BooleanField(default=False)), + ('ohlhafv_submit', models.BooleanField(default=False)), + ('feed_published', models.BooleanField(default=False)), + ('jobad_published', models.BooleanField(default=False)), + ('event_published', models.BooleanField(default=False)), + ('signup_opened', models.BooleanField(default=False)), + ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_webapp.basewebhook_set+', to='contenttypes.ContentType')), + ], + options={ + 'abstract': False, + 'base_manager_name': 'objects', + }, + ), + migrations.CreateModel( + name='TelegramHook', + fields=[ + ('basewebhook_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='webapp.BaseWebhook')), + ('channel_id', models.CharField(max_length=255, unique=True)), + ], + options={ + 'verbose_name': 'Telegram channel', + 'verbose_name_plural': 'Telegram channels', + }, + bases=('webapp.basewebhook',), + ), + migrations.CreateModel( + name='GenericWebhook', + fields=[ + ], + options={ + 'verbose_name': 'Webhook', + 'verbose_name_plural': 'Webhooks', + 'proxy': True, + 'indexes': [], + 'constraints': [], + }, + bases=('webapp.basewebhook',), + ), + ] diff --git a/webapp/models.py b/webapp/models.py index a1276e8..e7c697b 100644 --- a/webapp/models.py +++ b/webapp/models.py @@ -2,16 +2,19 @@ from django.conf import settings from django.db import models +from django.db.models.fields import CharField from django.utils import timezone # from datetime import timedelta from django.contrib.auth.models import User from django.db.models.signals import post_save from django.dispatch import receiver +import requests from webapp.utils import month_from_now, send_signup_email from django.utils.translation import ugettext_lazy as _ from auditlog.registry import auditlog from phonenumber_field.modelfields import PhoneNumberField from django.contrib.postgres.fields import JSONField +from polymorphic.models import PolymorphicModel from uuid import uuid4 import logging @@ -199,9 +202,78 @@ class JobAd(models.Model): return f'{delete_str}{self.title}' +class BaseWebhook(PolymorphicModel): + """Webhook base class instance""" + + name = models.CharField(max_length=255) + url = models.URLField() # URL where webhook message is broadcast. For example Telegram webhook API + # "Plugs"; which notifications are sent to this specific webhook instance + kaehmy_submit = models.BooleanField(_("Hook Kaehmys"), default=False) + ohlhafv_submit = models.BooleanField(_("Hook Ohlhafv challenges"),default=False) + feed_published = models.BooleanField(_("Hook published news"),default=False) + jobad_published = models.BooleanField(_("Hook published Job Ads"),default=False) + event_published = models.BooleanField(_("Hook published events"),default=False) + signup_opened = models.BooleanField(_("Hook opened signups"),default=False) + + @property + def plugs(self): + return { + "kaehmy": self.kaehmy_submit, + "ohlhafv": self.ohlhafv_submit, + "feed": self.feed_published, + "jobad": self.jobad_published, + "event": self.event_published, + "signup":self.signup_opened, + } + + def parseData(self): + pass + + def broadcast(self, message): + resp = requests.post(self.url, json=self.parseData(message)) + logging.debug(f'Webhook API response: HTTP{resp.status_code}') + logging.debug(resp.content) + + +class GenericWebhook(BaseWebhook): + class Meta: + proxy = True + verbose_name = _('Webhook') + verbose_name_plural = _('Webhooks') + + def __str__(self): + return 'Webhook "{}"'.format(self.name) + + def parseData(self, message): + return { + "text": message + } + +class TelegramHook(BaseWebhook): + """Model containing the channel id of a Telegram chat""" + + class Meta: + verbose_name = _('Telegram channel') + verbose_name_plural = _('Telegram channels') + + channel_id = models.CharField(max_length=255, unique=True) + + def __str__(self): + return 'Telegram channel: "{}"'.format(self.name) + + def parseData(self, message): + return { + 'text': message, + 'chat_id': self.channel_id, + 'parse_mode': 'Markdown' + } + + auditlog.register(Tag) auditlog.register(Feed) auditlog.register(Event) auditlog.register(SignupForm) auditlog.register(Signup) auditlog.register(JobAd) +auditlog.register(GenericWebhook) +auditlog.register(TelegramHook) diff --git a/webapp/translation.py b/webapp/translation.py index c7e293b..42e69f0 100644 --- a/webapp/translation.py +++ b/webapp/translation.py @@ -42,3 +42,18 @@ class TemplateQuestionTranslationOptions(TranslationOptions): @register(JobAd) class JobAdTranslationOptions(TranslationOptions): fields = ('title', 'description', 'content',) + + +@register(BaseWebhook) +class BaseWebhookOptions(TranslationOptions): + fields = () + + +@register(GenericWebhook) +class GenericWebhookOptions(TranslationOptions): + fields = () + + +@register(TelegramHook) +class TelegramHookTranslationOptions(TranslationOptions): + fields = () diff --git a/webapp/webhook.py b/webapp/webhook.py new file mode 100644 index 0000000..0e06960 --- /dev/null +++ b/webapp/webhook.py @@ -0,0 +1,9 @@ +from webapp.models import BaseWebhook +import logging + +def processHooks(message: str, eventType: str): + allHooks = BaseWebhook.objects.all() + for hook in list(allHooks): + logging.debug(hook) + if (hook.plugs[eventType] == True): + hook.broadcast(message) From 86a65e4680aefd56156bebad5600837754ce631c Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Thu, 13 Jan 2022 00:08:12 +0200 Subject: [PATCH 2/6] Locale changes --- locale/en/LC_MESSAGES/django.mo | Bin 16380 -> 15328 bytes locale/en/LC_MESSAGES/django.po | 8 +++----- locale/fi/LC_MESSAGES/django.mo | Bin 16205 -> 16704 bytes locale/fi/LC_MESSAGES/django.po | 6 +++--- ohlhafv/views.py | 2 +- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/locale/en/LC_MESSAGES/django.mo b/locale/en/LC_MESSAGES/django.mo index 6ccda0d96dd2c9feec5edbad6335b8861ddf5a79..061d4f09e5f2152b469df85881403158ad4077a7 100644 GIT binary patch delta 4800 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 delta 5797 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 Bw6p*K delta 5355 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 diff --git a/locale/fi/LC_MESSAGES/django.po b/locale/fi/LC_MESSAGES/django.po index 480a23c..9685a24 100644 --- a/locale/fi/LC_MESSAGES/django.po +++ b/locale/fi/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-01-12 23:55+0200\n" +"POT-Creation-Date: 2022-01-13 00:07+0200\n" "PO-Revision-Date: 2017-11-02 23:04+0200\n" "Last-Translator: \n" "Language-Team: \n" @@ -1077,8 +1077,8 @@ msgid "Challenge" msgstr "Haasta" #: ohlhafv/views.py:44 -msgid "Sinut on haastettu Øhlhäfviin!" -msgstr "" +msgid "You have been challenged at Ohlhafv!" +msgstr "Sinut on haastettu Øhlhäfviin!" #: templates/admin/base_site.html:44 msgid "Go" diff --git a/ohlhafv/views.py b/ohlhafv/views.py index b62eaf0..2f0b4e2 100644 --- a/ohlhafv/views.py +++ b/ohlhafv/views.py @@ -41,7 +41,7 @@ def ohlhafv_submit(request, *args, **kwargs): email = form.cleaned_data.get('victim_email', '') url = f'https://{URL}/ohlhafv/list' - subject = _('Sinut on haastettu Ohlhäfviin!') + subject = _('You have been challenged at Ohlhafv!') email_body = render_to_string( 'ohlhafv:email.html', { From 798c8600913636f1ebf6dfd45a3e00dcd0c0b825 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Thu, 13 Jan 2022 01:41:26 +0200 Subject: [PATCH 3/6] Add on save hooks for events, feed and jobads --- kaehmy/views.py | 2 +- ohlhafv/views.py | 17 +-- webapp/models.py | 200 ++++++++++++++++++++++---------- webapp/templates/tg_message.tpl | 5 + webapp/webhook.py | 9 -- 5 files changed, 149 insertions(+), 84 deletions(-) create mode 100644 webapp/templates/tg_message.tpl delete mode 100644 webapp/webhook.py diff --git a/kaehmy/views.py b/kaehmy/views.py index 8f839f2..948e24c 100644 --- a/kaehmy/views.py +++ b/kaehmy/views.py @@ -17,7 +17,7 @@ from kaehmy.models import Application, CustomRole, PresetRole from kaehmy.forms import ApplicationForm, CommentForm from kaehmy.tables import ExportTable from webapp.utils import send_email -from webapp.webhook import processHooks +from webapp.models import processHooks @ensure_csrf_cookie diff --git a/ohlhafv/views.py b/ohlhafv/views.py index 2f0b4e2..583e695 100644 --- a/ohlhafv/views.py +++ b/ohlhafv/views.py @@ -1,26 +1,17 @@ -"""Ohlhafv views.""" - -from django.db.models import Count -from django.shortcuts import render, redirect -from django.contrib.auth import login, logout, authenticate +import logging +from django.shortcuts import render from django.views.decorators.http import require_http_methods from django.views.decorators.csrf import ensure_csrf_cookie -from django.http import HttpResponse, HttpResponseRedirect -from django.contrib.auth.decorators import permission_required, login_required -from django.conf import settings +from django.http import HttpResponseRedirect from django.utils.translation import ugettext_lazy as _ from django.template.loader import render_to_string -import logging -import requests -from dealer.git import git from sikweb.settings import URL from ohlhafv.models import OhlhafvChallenge from ohlhafv.forms import OhlhafvForm -from ohlhafv.tables import OhlhafvTable from webapp.utils import send_email -from webapp.webhook import processHooks +from webapp.models import processHooks @require_http_methods(["GET"]) diff --git a/webapp/models.py b/webapp/models.py index e7c697b..183435a 100644 --- a/webapp/models.py +++ b/webapp/models.py @@ -1,24 +1,23 @@ """Webapp app models.""" -from django.conf import settings from django.db import models -from django.db.models.fields import CharField +from django.template.loader import render_to_string from django.utils import timezone -# from datetime import timedelta -from django.contrib.auth.models import User from django.db.models.signals import post_save from django.dispatch import receiver + +# from datetime import timedelta import requests -from webapp.utils import month_from_now, send_signup_email -from django.utils.translation import ugettext_lazy as _ -from auditlog.registry import auditlog -from phonenumber_field.modelfields import PhoneNumberField -from django.contrib.postgres.fields import JSONField -from polymorphic.models import PolymorphicModel from uuid import uuid4 import logging +from django.utils.translation import ugettext_lazy as _ +from django.contrib.postgres.fields import JSONField +from auditlog.registry import auditlog +from polymorphic.models import PolymorphicModel +from webapp.utils import month_from_now, send_signup_email +from sikweb.settings import FRONTEND_URL -VERBOSE_NAME = _('Webapp') +VERBOSE_NAME = _("Webapp") EMAIL_REGEX = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)" @@ -26,15 +25,15 @@ class Tag(models.Model): """Model for tag.""" class Meta: - verbose_name = _('Tag') - verbose_name_plural = _('Tags') + verbose_name = _("Tag") + verbose_name_plural = _("Tags") slug = models.SlugField(unique=True) name = models.CharField(max_length=127) icon = models.ImageField() def __str__(self): - return _('Tag: {}').format(self.slug) + return _("Tag: {}").format(self.slug) class BaseFeed(models.Model): @@ -52,8 +51,8 @@ class Feed(BaseFeed): """Model representing feed.""" class Meta: - verbose_name = _('Feed') - verbose_name_plural = _('Feeds') + verbose_name = _("Feed") + verbose_name_plural = _("Feeds") publish_time = models.DateTimeField(default=timezone.now) autohide = models.DateTimeField(default=month_from_now) @@ -62,26 +61,67 @@ class Feed(BaseFeed): def __str__(self): delete_str = _("Deleted: ") if self.deleted else "" - return _('{}Feed: {}').format(delete_str, self.title) + return _("{}Feed: {}").format(delete_str, self.title) + + __previousVisible = False + + def __init__(self, *args, **kwargs): + super(Feed, self).__init__(*args, **kwargs) + self.__previousVisible = self.visible + + def save(self, force_insert=False, force_update=False, *args, **kwargs): + created = self.pk is None + super(Feed, self).save(force_insert, force_update, *args, **kwargs) + + if self.visible and (created or not self.__previousVisible): + self.refresh_from_db() # Fetch so we can use primary key + url = f"https://{FRONTEND_URL}/feed/{self.pk}" + processHooks( + message=generateMessage( + "Uusi uutinen", self.title, self.description, url + ), + eventType="feed", + ) + self.__previousVisible = self.visible class Event(BaseFeed): """Model for event in guild calendar""" class Meta: - verbose_name = _('Event') - verbose_name_plural = _('Events') + verbose_name = _("Event") + verbose_name_plural = _("Events") start_time = models.DateTimeField(default=timezone.now) end_time = models.DateTimeField(default=timezone.now) - signupForm = models.ManyToManyField( - 'SignupForm', blank=True, related_name="event") + signupForm = models.ManyToManyField("SignupForm", blank=True, related_name="event") location = models.CharField(max_length=255, blank=True) deleted = models.BooleanField(default=False) def __str__(self): delete_str = _("Deleted: ") if self.deleted else "" - return _('{}Event: {}').format(delete_str, self.title) + return _("{}Event: {}").format(delete_str, self.title) + + __previousVisible = False + + def __init__(self, *args, **kwargs): + super(Event, self).__init__(*args, **kwargs) + self.__previousVisible = self.visible + + def save(self, force_insert=False, force_update=False, *args, **kwargs): + created = self.pk is None + super(Event, self).save(force_insert, force_update, *args, **kwargs) + + if self.visible and (created or not self.__previousVisible): + self.refresh_from_db() # Fetch so we can use primary key + url = f"https://{FRONTEND_URL}/events/{self.pk}" + processHooks( + message=generateMessage( + "Uusi tapahtuma", self.title, self.description, url + ), + eventType="event", + ) + self.__previousVisible = self.visible class TemplateQuestion(models.Model): @@ -90,23 +130,23 @@ class TemplateQuestion(models.Model): """ class Meta: - verbose_name = _('Template question') - verbose_name_plural = _('Template questions') + verbose_name = _("Template question") + verbose_name_plural = _("Template questions") name = models.CharField(max_length=255) questions = JSONField() deleted = models.BooleanField(default=False) def __str__(self): - return _('Template questions: {}').format(self.name) + return _("Template questions: {}").format(self.name) class SignupForm(models.Model): """Model for event signup form. Stores questions in JSON format.""" class Meta: - verbose_name = _('Signup form') - verbose_name_plural = _('Signup forms') + verbose_name = _("Signup form") + verbose_name_plural = _("Signup forms") title = models.CharField(max_length=255) start_time = models.DateTimeField(default=timezone.now) @@ -120,11 +160,11 @@ class SignupForm(models.Model): def __str__(self): delete_str = _("Deleted: ") if self.deleted else "" - return _('#{} {}{}').format(self.id, delete_str, self.title) + return _("#{} {}{}").format(self.id, delete_str, self.title) @property def signups(self): - return Signup.objects.filter(signupForm=self, deleted=False).order_by('pk') + return Signup.objects.filter(signupForm=self, deleted=False).order_by("pk") @property def isOpen(self): @@ -138,13 +178,14 @@ class Signup(models.Model): """ class Meta: - verbose_name = _('Sign-up') - verbose_name_plural = _('Sign-ups') - signupForm = models.ForeignKey('SignupForm', on_delete=models.CASCADE) + verbose_name = _("Sign-up") + verbose_name_plural = _("Sign-ups") + + signupForm = models.ForeignKey("SignupForm", on_delete=models.CASCADE) time = models.DateTimeField(default=timezone.now) answer = JSONField() # Answer we use in signupForm signups field. Frontend uses first questions answer as this value. - list_name = models.CharField(_('Name'), max_length=255) + list_name = models.CharField(_("Name"), max_length=255) # If there is email in questions, we save it as own field email = models.EmailField(blank=True, null=True) # Random unique identifier. Used for signup editing by the user. @@ -167,26 +208,32 @@ def email_on_signup(sender, instance, created, **kwargs): except AttributeError: # subject = _(f"Olet ilmoittautunut ilmoon {instance.signupForm.title}") subject = f"Olet ilmoittautunut ilmoon {instance.signupForm.title}" - send_signup_email(instance.email, subject, instance.id, instance.uuid, instance.signupForm.email_content) + send_signup_email( + instance.email, + subject, + instance.id, + instance.uuid, + instance.signupForm.email_content, + ) class BaseRole(models.Model): """Base model for occupations/roles.""" - name = models.CharField(_('Name'), max_length=255) - is_board = models.BooleanField(_('Board member')) + name = models.CharField(_("Name"), max_length=255) + is_board = models.BooleanField(_("Board member")) def __str__(self): n = self.name.capitalize() - return '{} ({})'.format(n, _('board member')) if self.is_board else n + return "{} ({})".format(n, _("board member")) if self.is_board else n class JobAd(models.Model): """Job advertisements shown on Corporate relations page""" class Meta: - verbose_name = _('JobAd') - verbose_name_plural = _('JobAds') + verbose_name = _("JobAd") + verbose_name_plural = _("JobAds") title = models.CharField(max_length=255) description = models.CharField(max_length=255) @@ -199,21 +246,57 @@ class JobAd(models.Model): def __str__(self): delete_str = _("Deleted: ") if self.deleted else "" - return f'{delete_str}{self.title}' + return f"{delete_str}{self.title}" + + __previousVisible = False + + def __init__(self, *args, **kwargs): + super(JobAd, self).__init__(*args, **kwargs) + self.__previousVisible = self.visible + + def save(self, force_insert=False, force_update=False, *args, **kwargs): + created = self.pk is None + super(JobAd, self).save(force_insert, force_update, *args, **kwargs) + + if self.visible and (created or not self.__previousVisible): + self.refresh_from_db() # Fetch so we can use primary key + url = f"https://{FRONTEND_URL}/jobads/{self.pk}" + processHooks( + message=generateMessage( + "Uusi työpaikkailmoitus", self.title, self.description, url + ), + eventType="jobad", + ) + self.__previousVisible = self.visible + +def generateMessage(heading: str, title: str, description: str, url: str): + return render_to_string( + "webapp:tg_message.tpl", + {"heading": heading, "title": title, "description": description, "url": url}, + ) + + +def processHooks(message: str, eventType: str): + allHooks = BaseWebhook.objects.all() + for hook in list(allHooks): + if hook.plugs[eventType] == True: + hook.broadcast(message) class BaseWebhook(PolymorphicModel): """Webhook base class instance""" name = models.CharField(max_length=255) - url = models.URLField() # URL where webhook message is broadcast. For example Telegram webhook API + url = ( + models.URLField() + ) # URL where webhook message is broadcast. For example Telegram webhook API # "Plugs"; which notifications are sent to this specific webhook instance - kaehmy_submit = models.BooleanField(_("Hook Kaehmys"), default=False) - ohlhafv_submit = models.BooleanField(_("Hook Ohlhafv challenges"),default=False) - feed_published = models.BooleanField(_("Hook published news"),default=False) - jobad_published = models.BooleanField(_("Hook published Job Ads"),default=False) - event_published = models.BooleanField(_("Hook published events"),default=False) - signup_opened = models.BooleanField(_("Hook opened signups"),default=False) + kaehmy_submit = models.BooleanField(_("Hook Kaehmys"), default=False) + ohlhafv_submit = models.BooleanField(_("Hook Ohlhafv challenges"), default=False) + feed_published = models.BooleanField(_("Hook published news"), default=False) + jobad_published = models.BooleanField(_("Hook published Job Ads"), default=False) + event_published = models.BooleanField(_("Hook published events"), default=False) + signup_opened = models.BooleanField(_("Hook opened signups"), default=False) @property def plugs(self): @@ -223,7 +306,7 @@ class BaseWebhook(PolymorphicModel): "feed": self.feed_published, "jobad": self.jobad_published, "event": self.event_published, - "signup":self.signup_opened, + "signup": self.signup_opened, } def parseData(self): @@ -231,30 +314,29 @@ class BaseWebhook(PolymorphicModel): def broadcast(self, message): resp = requests.post(self.url, json=self.parseData(message)) - logging.debug(f'Webhook API response: HTTP{resp.status_code}') + logging.debug(f"Webhook API response: HTTP{resp.status_code}") logging.debug(resp.content) class GenericWebhook(BaseWebhook): class Meta: proxy = True - verbose_name = _('Webhook') - verbose_name_plural = _('Webhooks') + verbose_name = _("Webhook") + verbose_name_plural = _("Webhooks") def __str__(self): return 'Webhook "{}"'.format(self.name) def parseData(self, message): - return { - "text": message - } + return {"text": message} + class TelegramHook(BaseWebhook): """Model containing the channel id of a Telegram chat""" class Meta: - verbose_name = _('Telegram channel') - verbose_name_plural = _('Telegram channels') + verbose_name = _("Telegram channel") + verbose_name_plural = _("Telegram channels") channel_id = models.CharField(max_length=255, unique=True) @@ -262,11 +344,7 @@ class TelegramHook(BaseWebhook): return 'Telegram channel: "{}"'.format(self.name) def parseData(self, message): - return { - 'text': message, - 'chat_id': self.channel_id, - 'parse_mode': 'Markdown' - } + return {"text": message, "chat_id": self.channel_id, "parse_mode": "Markdown"} auditlog.register(Tag) diff --git a/webapp/templates/tg_message.tpl b/webapp/templates/tg_message.tpl new file mode 100644 index 0000000..bab3c8f --- /dev/null +++ b/webapp/templates/tg_message.tpl @@ -0,0 +1,5 @@ +{{ heading }}: {{ title }} +==================== +{{ description }} + +Lisätietoa osoitteessa: {{ url }} \ No newline at end of file diff --git a/webapp/webhook.py b/webapp/webhook.py deleted file mode 100644 index 0e06960..0000000 --- a/webapp/webhook.py +++ /dev/null @@ -1,9 +0,0 @@ -from webapp.models import BaseWebhook -import logging - -def processHooks(message: str, eventType: str): - allHooks = BaseWebhook.objects.all() - for hook in list(allHooks): - logging.debug(hook) - if (hook.plugs[eventType] == True): - hook.broadcast(message) From 867996ae276899e1b5dc788cb1aedba224b1e875 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Thu, 13 Jan 2022 21:00:42 +0200 Subject: [PATCH 4/6] fix lint --- webapp/models.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/webapp/models.py b/webapp/models.py index 183435a..5d077c5 100644 --- a/webapp/models.py +++ b/webapp/models.py @@ -74,7 +74,7 @@ class Feed(BaseFeed): super(Feed, self).save(force_insert, force_update, *args, **kwargs) if self.visible and (created or not self.__previousVisible): - self.refresh_from_db() # Fetch so we can use primary key + self.refresh_from_db() # Fetch so we can use primary key url = f"https://{FRONTEND_URL}/feed/{self.pk}" processHooks( message=generateMessage( @@ -113,7 +113,7 @@ class Event(BaseFeed): super(Event, self).save(force_insert, force_update, *args, **kwargs) if self.visible and (created or not self.__previousVisible): - self.refresh_from_db() # Fetch so we can use primary key + self.refresh_from_db() # Fetch so we can use primary key url = f"https://{FRONTEND_URL}/events/{self.pk}" processHooks( message=generateMessage( @@ -259,7 +259,7 @@ class JobAd(models.Model): super(JobAd, self).save(force_insert, force_update, *args, **kwargs) if self.visible and (created or not self.__previousVisible): - self.refresh_from_db() # Fetch so we can use primary key + self.refresh_from_db() # Fetch so we can use primary key url = f"https://{FRONTEND_URL}/jobads/{self.pk}" processHooks( message=generateMessage( @@ -269,6 +269,7 @@ class JobAd(models.Model): ) self.__previousVisible = self.visible + def generateMessage(heading: str, title: str, description: str, url: str): return render_to_string( "webapp:tg_message.tpl", @@ -279,7 +280,7 @@ def generateMessage(heading: str, title: str, description: str, url: str): def processHooks(message: str, eventType: str): allHooks = BaseWebhook.objects.all() for hook in list(allHooks): - if hook.plugs[eventType] == True: + if hook.plugs[eventType] is True: hook.broadcast(message) From 7cb03d40d42723a84457c4f62005cac129f3ff10 Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Thu, 13 Jan 2022 23:02:57 +0200 Subject: [PATCH 5/6] fix ci --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b40e402..d2f196d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -54,7 +54,7 @@ lint:py: needs: [] script: - pip install black==21.12b0 - - black --diff . + - black --check . lint:js: image: node:14 From 79cc0bcd55bf875f5095c4878b8108d9f60e923c Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Thu, 13 Jan 2022 23:07:11 +0200 Subject: [PATCH 6/6] fix lint --- .../migrations/0006_delete_telegramchannel.py | 4 +- kaehmy/models.py | 4 +- webapp/admin.py | 13 ++- ...basewebhook_genericwebhook_telegramhook.py | 86 ++++++++++++------- webapp/translation.py | 6 +- 5 files changed, 78 insertions(+), 35 deletions(-) diff --git a/kaehmy/migrations/0006_delete_telegramchannel.py b/kaehmy/migrations/0006_delete_telegramchannel.py index 4d963c0..5f28971 100644 --- a/kaehmy/migrations/0006_delete_telegramchannel.py +++ b/kaehmy/migrations/0006_delete_telegramchannel.py @@ -6,11 +6,11 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('kaehmy', '0005_auto_20190312_1458'), + ("kaehmy", "0005_auto_20190312_1458"), ] operations = [ migrations.DeleteModel( - name='TelegramChannel', + name="TelegramChannel", ), ] diff --git a/kaehmy/models.py b/kaehmy/models.py index 58bef2e..6582a63 100644 --- a/kaehmy/models.py +++ b/kaehmy/models.py @@ -159,4 +159,6 @@ class Application(CommentParent): return ", ".join(combined) if len(combined) > 0 else "" def has_any_board_role(self): - return self.preset_roles.filter(is_board=True).exists() or self.custom_roles.filter(is_board=True) + return self.preset_roles.filter( + is_board=True + ).exists() or self.custom_roles.filter(is_board=True) diff --git a/webapp/admin.py b/webapp/admin.py index 0d33837..eb4b569 100644 --- a/webapp/admin.py +++ b/webapp/admin.py @@ -1,7 +1,18 @@ """File containing webapp app admin registers.""" from django.contrib import admin -from webapp.models import Feed, Tag, Event, Signup, SignupForm, TemplateQuestion, JobAd, BaseWebhook, GenericWebhook, TelegramHook +from webapp.models import ( + Feed, + Tag, + Event, + Signup, + SignupForm, + TemplateQuestion, + JobAd, + BaseWebhook, + GenericWebhook, + TelegramHook, +) from modeltranslation.admin import TranslationAdmin from django.contrib.auth.models import Permission diff --git a/webapp/migrations/0078_basewebhook_genericwebhook_telegramhook.py b/webapp/migrations/0078_basewebhook_genericwebhook_telegramhook.py index a48ae4c..07e1c1a 100644 --- a/webapp/migrations/0078_basewebhook_genericwebhook_telegramhook.py +++ b/webapp/migrations/0078_basewebhook_genericwebhook_telegramhook.py @@ -7,53 +7,79 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('contenttypes', '0002_remove_content_type_name'), - ('webapp', '0077_templatequestion_deleted'), + ("contenttypes", "0002_remove_content_type_name"), + ("webapp", "0077_templatequestion_deleted"), ] operations = [ migrations.CreateModel( - name='BaseWebhook', + name="BaseWebhook", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('url', models.URLField()), - ('kaehmy_submit', models.BooleanField(default=False)), - ('ohlhafv_submit', models.BooleanField(default=False)), - ('feed_published', models.BooleanField(default=False)), - ('jobad_published', models.BooleanField(default=False)), - ('event_published', models.BooleanField(default=False)), - ('signup_opened', models.BooleanField(default=False)), - ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_webapp.basewebhook_set+', to='contenttypes.ContentType')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), + ("url", models.URLField()), + ("kaehmy_submit", models.BooleanField(default=False)), + ("ohlhafv_submit", models.BooleanField(default=False)), + ("feed_published", models.BooleanField(default=False)), + ("jobad_published", models.BooleanField(default=False)), + ("event_published", models.BooleanField(default=False)), + ("signup_opened", models.BooleanField(default=False)), + ( + "polymorphic_ctype", + models.ForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="polymorphic_webapp.basewebhook_set+", + to="contenttypes.ContentType", + ), + ), ], options={ - 'abstract': False, - 'base_manager_name': 'objects', + "abstract": False, + "base_manager_name": "objects", }, ), migrations.CreateModel( - name='TelegramHook', + name="TelegramHook", fields=[ - ('basewebhook_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='webapp.BaseWebhook')), - ('channel_id', models.CharField(max_length=255, unique=True)), + ( + "basewebhook_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="webapp.BaseWebhook", + ), + ), + ("channel_id", models.CharField(max_length=255, unique=True)), ], options={ - 'verbose_name': 'Telegram channel', - 'verbose_name_plural': 'Telegram channels', + "verbose_name": "Telegram channel", + "verbose_name_plural": "Telegram channels", }, - bases=('webapp.basewebhook',), + bases=("webapp.basewebhook",), ), migrations.CreateModel( - name='GenericWebhook', - fields=[ - ], + name="GenericWebhook", + fields=[], options={ - 'verbose_name': 'Webhook', - 'verbose_name_plural': 'Webhooks', - 'proxy': True, - 'indexes': [], - 'constraints': [], + "verbose_name": "Webhook", + "verbose_name_plural": "Webhooks", + "proxy": True, + "indexes": [], + "constraints": [], }, - bases=('webapp.basewebhook',), + bases=("webapp.basewebhook",), ), ] diff --git a/webapp/translation.py b/webapp/translation.py index a54af24..df4b799 100644 --- a/webapp/translation.py +++ b/webapp/translation.py @@ -41,7 +41,11 @@ class TemplateQuestionTranslationOptions(TranslationOptions): @register(JobAd) class JobAdTranslationOptions(TranslationOptions): - fields = ('title', 'description', 'content',) + fields = ( + "title", + "description", + "content", + ) @register(BaseWebhook)