Add on save hooks for events, feed and jobads
This commit is contained in:
+139
-61
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user