Add on save hooks for events, feed and jobads

This commit is contained in:
Aarni Halinen
2022-01-13 01:41:26 +02:00
parent 86a65e4680
commit 798c860091
5 changed files with 149 additions and 84 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ from kaehmy.models import Application, CustomRole, PresetRole
from kaehmy.forms import ApplicationForm, CommentForm from kaehmy.forms import ApplicationForm, CommentForm
from kaehmy.tables import ExportTable from kaehmy.tables import ExportTable
from webapp.utils import send_email from webapp.utils import send_email
from webapp.webhook import processHooks from webapp.models import processHooks
@ensure_csrf_cookie @ensure_csrf_cookie
+4 -13
View File
@@ -1,26 +1,17 @@
"""Ohlhafv views.""" import logging
from django.shortcuts import render
from django.db.models import Count
from django.shortcuts import render, redirect
from django.contrib.auth import login, logout, authenticate
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponseRedirect
from django.contrib.auth.decorators import permission_required, login_required
from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.template.loader import render_to_string from django.template.loader import render_to_string
import logging
import requests
from dealer.git import git
from sikweb.settings import URL from sikweb.settings import URL
from ohlhafv.models import OhlhafvChallenge from ohlhafv.models import OhlhafvChallenge
from ohlhafv.forms import OhlhafvForm from ohlhafv.forms import OhlhafvForm
from ohlhafv.tables import OhlhafvTable
from webapp.utils import send_email from webapp.utils import send_email
from webapp.webhook import processHooks from webapp.models import processHooks
@require_http_methods(["GET"]) @require_http_methods(["GET"])
+139 -61
View File
@@ -1,24 +1,23 @@
"""Webapp app models.""" """Webapp app models."""
from django.conf import settings
from django.db import models 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 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.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
# from datetime import timedelta
import requests 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 from uuid import uuid4
import logging 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-.]+$)" 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.""" """Model for tag."""
class Meta: class Meta:
verbose_name = _('Tag') verbose_name = _("Tag")
verbose_name_plural = _('Tags') verbose_name_plural = _("Tags")
slug = models.SlugField(unique=True) slug = models.SlugField(unique=True)
name = models.CharField(max_length=127) name = models.CharField(max_length=127)
icon = models.ImageField() icon = models.ImageField()
def __str__(self): def __str__(self):
return _('Tag: {}').format(self.slug) return _("Tag: {}").format(self.slug)
class BaseFeed(models.Model): class BaseFeed(models.Model):
@@ -52,8 +51,8 @@ class Feed(BaseFeed):
"""Model representing feed.""" """Model representing feed."""
class Meta: class Meta:
verbose_name = _('Feed') verbose_name = _("Feed")
verbose_name_plural = _('Feeds') verbose_name_plural = _("Feeds")
publish_time = models.DateTimeField(default=timezone.now) publish_time = models.DateTimeField(default=timezone.now)
autohide = models.DateTimeField(default=month_from_now) autohide = models.DateTimeField(default=month_from_now)
@@ -62,26 +61,67 @@ class Feed(BaseFeed):
def __str__(self): def __str__(self):
delete_str = _("Deleted: ") if self.deleted else "" 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): class Event(BaseFeed):
"""Model for event in guild calendar""" """Model for event in guild calendar"""
class Meta: class Meta:
verbose_name = _('Event') verbose_name = _("Event")
verbose_name_plural = _('Events') verbose_name_plural = _("Events")
start_time = models.DateTimeField(default=timezone.now) start_time = models.DateTimeField(default=timezone.now)
end_time = models.DateTimeField(default=timezone.now) end_time = models.DateTimeField(default=timezone.now)
signupForm = models.ManyToManyField( signupForm = models.ManyToManyField("SignupForm", blank=True, related_name="event")
'SignupForm', blank=True, related_name="event")
location = models.CharField(max_length=255, blank=True) location = models.CharField(max_length=255, blank=True)
deleted = models.BooleanField(default=False) deleted = models.BooleanField(default=False)
def __str__(self): def __str__(self):
delete_str = _("Deleted: ") if self.deleted else "" 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): class TemplateQuestion(models.Model):
@@ -90,23 +130,23 @@ class TemplateQuestion(models.Model):
""" """
class Meta: class Meta:
verbose_name = _('Template question') verbose_name = _("Template question")
verbose_name_plural = _('Template questions') verbose_name_plural = _("Template questions")
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
questions = JSONField() questions = JSONField()
deleted = models.BooleanField(default=False) deleted = models.BooleanField(default=False)
def __str__(self): def __str__(self):
return _('Template questions: {}').format(self.name) return _("Template questions: {}").format(self.name)
class SignupForm(models.Model): class SignupForm(models.Model):
"""Model for event signup form. Stores questions in JSON format.""" """Model for event signup form. Stores questions in JSON format."""
class Meta: class Meta:
verbose_name = _('Signup form') verbose_name = _("Signup form")
verbose_name_plural = _('Signup forms') verbose_name_plural = _("Signup forms")
title = models.CharField(max_length=255) title = models.CharField(max_length=255)
start_time = models.DateTimeField(default=timezone.now) start_time = models.DateTimeField(default=timezone.now)
@@ -120,11 +160,11 @@ class SignupForm(models.Model):
def __str__(self): def __str__(self):
delete_str = _("Deleted: ") if self.deleted else "" delete_str = _("Deleted: ") if self.deleted else ""
return _('#{} {}{}').format(self.id, delete_str, self.title) return _("#{} {}{}").format(self.id, delete_str, self.title)
@property @property
def signups(self): 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 @property
def isOpen(self): def isOpen(self):
@@ -138,13 +178,14 @@ class Signup(models.Model):
""" """
class Meta: class Meta:
verbose_name = _('Sign-up') verbose_name = _("Sign-up")
verbose_name_plural = _('Sign-ups') verbose_name_plural = _("Sign-ups")
signupForm = models.ForeignKey('SignupForm', on_delete=models.CASCADE)
signupForm = models.ForeignKey("SignupForm", on_delete=models.CASCADE)
time = models.DateTimeField(default=timezone.now) time = models.DateTimeField(default=timezone.now)
answer = JSONField() answer = JSONField()
# Answer we use in signupForm signups field. Frontend uses first questions answer as this value. # 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 # If there is email in questions, we save it as own field
email = models.EmailField(blank=True, null=True) email = models.EmailField(blank=True, null=True)
# Random unique identifier. Used for signup editing by the user. # Random unique identifier. Used for signup editing by the user.
@@ -167,26 +208,32 @@ def email_on_signup(sender, instance, created, **kwargs):
except AttributeError: except AttributeError:
# subject = _(f"Olet ilmoittautunut ilmoon {instance.signupForm.title}") # subject = _(f"Olet ilmoittautunut ilmoon {instance.signupForm.title}")
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): class BaseRole(models.Model):
"""Base model for occupations/roles.""" """Base model for occupations/roles."""
name = models.CharField(_('Name'), max_length=255) name = models.CharField(_("Name"), max_length=255)
is_board = models.BooleanField(_('Board member')) is_board = models.BooleanField(_("Board member"))
def __str__(self): def __str__(self):
n = self.name.capitalize() 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): class JobAd(models.Model):
"""Job advertisements shown on Corporate relations page""" """Job advertisements shown on Corporate relations page"""
class Meta: class Meta:
verbose_name = _('JobAd') verbose_name = _("JobAd")
verbose_name_plural = _('JobAds') verbose_name_plural = _("JobAds")
title = models.CharField(max_length=255) title = models.CharField(max_length=255)
description = models.CharField(max_length=255) description = models.CharField(max_length=255)
@@ -199,21 +246,57 @@ class JobAd(models.Model):
def __str__(self): def __str__(self):
delete_str = _("Deleted: ") if self.deleted else "" 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): class BaseWebhook(PolymorphicModel):
"""Webhook base class instance""" """Webhook base class instance"""
name = models.CharField(max_length=255) 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 # "Plugs"; which notifications are sent to this specific webhook instance
kaehmy_submit = models.BooleanField(_("Hook Kaehmys"), default=False) kaehmy_submit = models.BooleanField(_("Hook Kaehmys"), default=False)
ohlhafv_submit = models.BooleanField(_("Hook Ohlhafv challenges"),default=False) ohlhafv_submit = models.BooleanField(_("Hook Ohlhafv challenges"), default=False)
feed_published = models.BooleanField(_("Hook published news"),default=False) feed_published = models.BooleanField(_("Hook published news"), default=False)
jobad_published = models.BooleanField(_("Hook published Job Ads"),default=False) jobad_published = models.BooleanField(_("Hook published Job Ads"), default=False)
event_published = models.BooleanField(_("Hook published events"),default=False) event_published = models.BooleanField(_("Hook published events"), default=False)
signup_opened = models.BooleanField(_("Hook opened signups"),default=False) signup_opened = models.BooleanField(_("Hook opened signups"), default=False)
@property @property
def plugs(self): def plugs(self):
@@ -223,7 +306,7 @@ class BaseWebhook(PolymorphicModel):
"feed": self.feed_published, "feed": self.feed_published,
"jobad": self.jobad_published, "jobad": self.jobad_published,
"event": self.event_published, "event": self.event_published,
"signup":self.signup_opened, "signup": self.signup_opened,
} }
def parseData(self): def parseData(self):
@@ -231,30 +314,29 @@ class BaseWebhook(PolymorphicModel):
def broadcast(self, message): def broadcast(self, message):
resp = requests.post(self.url, json=self.parseData(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) logging.debug(resp.content)
class GenericWebhook(BaseWebhook): class GenericWebhook(BaseWebhook):
class Meta: class Meta:
proxy = True proxy = True
verbose_name = _('Webhook') verbose_name = _("Webhook")
verbose_name_plural = _('Webhooks') verbose_name_plural = _("Webhooks")
def __str__(self): def __str__(self):
return 'Webhook "{}"'.format(self.name) return 'Webhook "{}"'.format(self.name)
def parseData(self, message): def parseData(self, message):
return { return {"text": message}
"text": message
}
class TelegramHook(BaseWebhook): class TelegramHook(BaseWebhook):
"""Model containing the channel id of a Telegram chat""" """Model containing the channel id of a Telegram chat"""
class Meta: class Meta:
verbose_name = _('Telegram channel') verbose_name = _("Telegram channel")
verbose_name_plural = _('Telegram channels') verbose_name_plural = _("Telegram channels")
channel_id = models.CharField(max_length=255, unique=True) channel_id = models.CharField(max_length=255, unique=True)
@@ -262,11 +344,7 @@ class TelegramHook(BaseWebhook):
return 'Telegram channel: "{}"'.format(self.name) return 'Telegram channel: "{}"'.format(self.name)
def parseData(self, message): def parseData(self, message):
return { return {"text": message, "chat_id": self.channel_id, "parse_mode": "Markdown"}
'text': message,
'chat_id': self.channel_id,
'parse_mode': 'Markdown'
}
auditlog.register(Tag) auditlog.register(Tag)
+5
View File
@@ -0,0 +1,5 @@
{{ heading }}: {{ title }}
====================
{{ description }}
Lisätietoa osoitteessa: {{ url }}
-9
View File
@@ -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)