feat: add announcements

This commit is contained in:
moanos [he/him] 2024-05-31 09:58:03 +02:00
parent 8295716f07
commit e5a5fd5a10
9 changed files with 238 additions and 3 deletions

View File

@ -5,7 +5,7 @@ from django.utils.html import format_html
from .models import User, Language, Text, ReportComment, ReportAdoptionNotice from .models import User, Language, Text, ReportComment, ReportAdoptionNotice
from .models import Animal, Species, RescueOrganization, AdoptionNotice, Location, Rule, Image, ModerationAction, \ from .models import Animal, Species, RescueOrganization, AdoptionNotice, Location, Rule, Image, ModerationAction, \
Member, Comment, Report Member, Comment, Report, Announcement
# Define an inline admin descriptor for Employee model # Define an inline admin descriptor for Employee model
@ -62,3 +62,4 @@ admin.site.register(Image)
admin.site.register(ModerationAction) admin.site.register(ModerationAction)
admin.site.register(Language) admin.site.register(Language)
admin.site.register(Text) admin.site.register(Text)
admin.site.register(Announcement)

View File

@ -0,0 +1,41 @@
# Generated by Django 5.0.6 on 2024-06-05 04:21
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("fellchensammlung", "0004_alter_report_reported_broken_rules"),
]
operations = [
migrations.CreateModel(
name="Announcement",
fields=[
(
"text_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="fellchensammlung.text",
),
),
("logged_in_only", models.BooleanField(default=False)),
("created_at", models.DateTimeField(auto_now_add=True)),
(
"publish_start_time",
models.DateTimeField(verbose_name="Veröffentlichungszeitpunk"),
),
(
"publish_end_time",
models.DateTimeField(verbose_name="Veröffentlichungsende"),
),
],
bases=("fellchensammlung.text",),
),
]

View File

@ -0,0 +1,26 @@
# Generated by Django 5.0.6 on 2024-06-05 06:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("fellchensammlung", "0005_announcement"),
]
operations = [
migrations.AddField(
model_name="announcement",
name="type",
field=models.CharField(
choices=[
("important", "important"),
("warning", "warning"),
("info", "info"),
],
default="info",
max_length=100,
),
),
]

View File

@ -4,6 +4,7 @@ from django.db import models
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from datetime import datetime from datetime import datetime
from django.utils import timezone
from django.dispatch import receiver from django.dispatch import receiver
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
@ -371,6 +372,55 @@ class Text(models.Model):
return f"{self.title} ({self.language})" return f"{self.title} ({self.language})"
class Announcement(Text):
"""
Class to store announcements that should be displayed for all users
"""
logged_in_only = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
publish_start_time = models.DateTimeField(verbose_name="Veröffentlichungszeitpunk")
publish_end_time = models.DateTimeField(verbose_name="Veröffentlichungsende")
IMPORTANT = "important"
WARNING = "warning"
INFO = "info"
TYPES = {
IMPORTANT: "important",
WARNING: "warning",
INFO: "info",
}
type = models.CharField(choices=TYPES, max_length=100, default=INFO)
@property
def is_active(self):
return self.publish_start_time < timezone.now() < self.publish_end_time
def __str__(self):
return f"[{'🟢' if self.is_active else '🔴'}]{self.title} ({self.language})"
@staticmethod
def get_active_announcements(logged_in=False, language=None):
if logged_in:
all_active_announcements = [a for a in Announcement.objects.all() if a.is_active]
else:
all_active_announcements = [a for a in Announcement.objects.filter(logged_in_only=False) if a.is_active]
if language is None:
return all_active_announcements
else:
if logged_in:
announcements_in_language = Announcement.objects.filter(language=language)
else:
announcements_in_language = Announcement.objects.filter(language=language, logged_in_only=False)
active_announcements_in_language = [a for a in announcements_in_language if a.is_active]
untranslated_announcements = []
text_codes = [announcement.text_code for announcement in active_announcements_in_language]
for announcement in all_active_announcements:
if announcement.language != language and announcement.text_code not in text_codes:
untranslated_announcements.append(announcement)
return active_announcements_in_language + untranslated_announcements
class Comment(models.Model): class Comment(models.Model):
""" """
Class to store comments in markdown content Class to store comments in markdown content

View File

@ -446,4 +446,33 @@ textarea {
.btn { .btn {
margin: 5px; margin: 5px;
} }
}
.announcement {
flex: 1 100%;
margin: 10px;
border-radius: 8px;
padding: 5px;
background: var(--background-three);
color: var(--text-two);
h1 {
font-size: 1.2rem;
margin: 0px;
padding: 0px;
color: var(--text-two);
text-shadow: none;
}
}
.important {
border: #e01137 4px solid;
}
.warning {
border: #e09e11 4px solid;
}
.info {
border: rgba(17, 58, 224, 0.51) 4px solid;
} }

View File

@ -2,6 +2,9 @@
{% load i18n %} {% load i18n %}
{% block content %} {% block content %}
{% for announcement in announcements %}
{% include "fellchensammlung/partials/partial-announcement.html" %}
{% endfor %}
<h1>{% translate "Notfellchen - Vermittlungen finden" %}</h1> <h1>{% translate "Notfellchen - Vermittlungen finden" %}</h1>
<p>{% translate "Alle Tiere brauchen ein liebendes Zuhause. Damit keins vergessen wird gibt es diese Seite. Entwickelt und betreut von " %}<em><a <p>{% translate "Alle Tiere brauchen ein liebendes Zuhause. Damit keins vergessen wird gibt es diese Seite. Entwickelt und betreut von " %}<em><a
href="https://hyteck.de">moanos</a></em>!</p> href="https://hyteck.de">moanos</a></em>!</p>

View File

@ -0,0 +1,10 @@
{% load i18n %}
{% load custom_tags %}
<div class="announcement {{ announcement.type }}">
<div class="announcement-header">
<h1 class="announcement">{{ announcement.title }}</h1>
</div>
<p>
{{ announcement.content | render_markdown }}
</p>
</div>

View File

@ -14,13 +14,16 @@ from fellchensammlung import logger
from fellchensammlung.models import AdoptionNotice, Text, Animal, Rule, Image, Report, ModerationAction, \ from fellchensammlung.models import AdoptionNotice, Text, Animal, Rule, Image, Report, ModerationAction, \
Member Member
from .forms import AdoptionNoticeForm, ImageForm, ReportAdoptionNoticeForm, CommentForm, ReportCommentForm, AnimalForm from .forms import AdoptionNoticeForm, ImageForm, ReportAdoptionNoticeForm, CommentForm, ReportCommentForm, AnimalForm
from .models import Language from .models import Language, Announcement
def index(request): def index(request):
"""View function for home page of site.""" """View function for home page of site."""
latest_adoption_list = AdoptionNotice.objects.order_by("-created_at")[:5] latest_adoption_list = AdoptionNotice.objects.order_by("-created_at")[:5]
context = {"adoption_notices": latest_adoption_list} language_code = translation.get_language()
lang = Language.objects.get(languagecode=language_code)
active_announcements = Announcement.get_active_announcements(lang)
context = {"adoption_notices": latest_adoption_list, "announcements": active_announcements}
return render(request, 'fellchensammlung/index.html', context=context) return render(request, 'fellchensammlung/index.html', context=context)

72
src/tests/test_models.py Normal file
View File

@ -0,0 +1,72 @@
from datetime import datetime, timedelta
from django.utils import timezone
from django.test import TestCase
from model_bakery import baker
from fellchensammlung.models import Announcement, Language
class AnnouncementTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.language_de = baker.make(Language, name="Deutsch_", languagecode="de")
cls.language_en = baker.make(Language, name="English_", languagecode="en")
cls.announcement1 = baker.make(Announcement, title="Notfellchen reduziert um 1000%",
content="Jetzt adoptieren was da ist!",
publish_start_time=timezone.now() + timedelta(hours=-1),
publish_end_time=timezone.now() + timedelta(hours=1),
text_code="advert1",
language=cls.language_de)
cls.announcement2 = baker.make(Announcement, title="Notfellchen now on sale!",
content="Adopt now!",
publish_start_time=timezone.now() + timedelta(hours=-1),
publish_end_time=timezone.now() + timedelta(hours=1),
text_code="advert1",
language=cls.language_en)
cls.announcement3 = baker.make(Announcement, title="We got hacked",
content="Hackers threaten to release incredibly sweet animal photos!",
publish_start_time=timezone.now() + timedelta(hours=-1),
publish_end_time=timezone.now() + timedelta(hours=1),
text_code="hacked",
language=cls.language_en)
cls.announcement4 = baker.make(Announcement, title="New function: Nothing",
content="You can now also do NOTHING on this side! NOTHING will help you to be "
"more productive",
publish_start_time=timezone.now() + timedelta(hours=1),
publish_end_time=datetime.now() + timedelta(hours=2),
text_code="inactive",
language=cls.language_en)
cls.announcement5 = baker.make(Announcement, title="Secret for all logged in",
content="You can create adoption notices yourself",
publish_start_time=timezone.now() + timedelta(hours=-1),
publish_end_time=datetime.now() + timedelta(hours=2),
text_code="secret",
language=cls.language_en,
logged_in_only=True)
def test_active_announcements(self):
active_announcements = Announcement.get_active_announcements()
self.assertTrue(self.announcement1 in active_announcements)
self.assertTrue(self.announcement2 in active_announcements)
self.assertTrue(self.announcement3 in active_announcements)
self.assertTrue(self.announcement4 not in active_announcements)
self.assertTrue(self.announcement5 not in active_announcements)
active_announcements = Announcement.get_active_announcements(language=self.language_de)
self.assertTrue(self.announcement1 in active_announcements)
self.assertTrue(self.announcement3 in active_announcements)
self.assertTrue(self.announcement2 not in active_announcements)
self.assertTrue(self.announcement4 not in active_announcements)
self.assertTrue(self.announcement5 not in active_announcements)
active_announcements = Announcement.get_active_announcements(language=self.language_de, logged_in=True)
self.assertTrue(self.announcement1 in active_announcements)
self.assertTrue(self.announcement3 in active_announcements)
self.assertTrue(self.announcement2 not in active_announcements)
self.assertTrue(self.announcement4 not in active_announcements)
self.assertTrue(self.announcement5 in active_announcements)