2024-03-22 11:45:50 +00:00
|
|
|
import uuid
|
|
|
|
|
2024-03-17 10:26:32 +00:00
|
|
|
from django.db import models
|
2024-03-18 21:50:39 +00:00
|
|
|
from django.urls import reverse
|
2024-03-18 07:26:21 +00:00
|
|
|
from django.utils.translation import gettext_lazy as _
|
2024-03-19 05:15:38 +00:00
|
|
|
from datetime import datetime
|
2024-05-31 07:58:03 +00:00
|
|
|
from django.utils import timezone
|
2024-03-23 21:20:31 +00:00
|
|
|
from django.dispatch import receiver
|
|
|
|
from django.db.models.signals import post_save
|
2024-04-07 07:06:18 +00:00
|
|
|
from django.contrib.auth.models import Group
|
2024-04-07 09:33:41 +00:00
|
|
|
from django.contrib.auth.models import AbstractUser
|
|
|
|
|
2024-03-19 05:15:38 +00:00
|
|
|
from fellchensammlung.tools import misc
|
2024-05-10 11:54:16 +00:00
|
|
|
from notfellchen.settings import MEDIA_URL
|
2024-03-18 07:26:21 +00:00
|
|
|
|
|
|
|
|
2024-04-14 13:56:42 +00:00
|
|
|
class Language(models.Model):
|
|
|
|
"""Model representing a Language (e.g. English, French, Japanese, etc.)"""
|
|
|
|
name = models.CharField(max_length=200,
|
|
|
|
help_text=_("Der Name einer natürliche Sprache wie Deutsch, Englisch oder Arabisch."),
|
|
|
|
unique=True)
|
|
|
|
|
|
|
|
languagecode = models.CharField(max_length=10,
|
|
|
|
# Translators: This helptext includes an URL
|
|
|
|
help_text=_(
|
|
|
|
"Der standartisierte Sprachcode. Mehr Informationen: http://www.i18nguy.com/unicode/language-identifiers.html"),
|
|
|
|
verbose_name=_('Sprachcode'))
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
"""String for representing the Model object (in Admin site etc.)"""
|
|
|
|
return self.name
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
verbose_name = _('Sprache')
|
|
|
|
verbose_name_plural = _('Sprachen')
|
|
|
|
|
|
|
|
|
2024-03-18 16:10:48 +00:00
|
|
|
class Image(models.Model):
|
|
|
|
title = models.CharField(max_length=200)
|
|
|
|
image = models.ImageField(upload_to='images')
|
|
|
|
alt_text = models.TextField(max_length=2000)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.title
|
|
|
|
|
2024-05-10 11:54:16 +00:00
|
|
|
@property
|
|
|
|
def as_html(self):
|
2024-05-30 11:58:24 +00:00
|
|
|
return f'<img src="{MEDIA_URL}/{self.image}" alt="{self.alt_text}">'
|
2024-05-10 11:54:16 +00:00
|
|
|
|
2024-03-18 16:10:48 +00:00
|
|
|
|
2024-03-18 07:26:21 +00:00
|
|
|
class Species(models.Model):
|
|
|
|
"""Model representing a species of animal."""
|
2024-04-13 11:25:09 +00:00
|
|
|
name = models.CharField(max_length=200, help_text=_('Name der Tierart'),
|
2024-03-18 07:26:21 +00:00
|
|
|
verbose_name=_('Name'))
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
"""String for representing the Model object."""
|
|
|
|
return self.name
|
|
|
|
|
|
|
|
class Meta:
|
2024-04-13 11:25:09 +00:00
|
|
|
verbose_name = _('Tierart')
|
|
|
|
verbose_name_plural = _('Tierarten')
|
2024-03-18 07:26:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Location(models.Model):
|
|
|
|
def __str__(self):
|
|
|
|
return f"{self.name}"
|
|
|
|
|
2024-05-31 08:58:57 +00:00
|
|
|
place_id = models.IntegerField()
|
|
|
|
osm_id = models.IntegerField()
|
|
|
|
latitude = models.FloatField()
|
|
|
|
longitude = models.FloatField()
|
|
|
|
name = models.CharField(max_length=2000)
|
2024-03-18 07:26:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
class RescueOrganization(models.Model):
|
|
|
|
def __str__(self):
|
|
|
|
return f"{self.name}"
|
|
|
|
|
|
|
|
name = models.CharField(max_length=200)
|
2024-04-13 11:25:09 +00:00
|
|
|
trusted = models.BooleanField(default=False, verbose_name=_('Vertrauenswürdig'))
|
2024-05-31 08:58:57 +00:00
|
|
|
location_string = models.CharField(max_length=200, verbose_name=_("Ort der Organisation"))
|
2024-03-18 07:26:21 +00:00
|
|
|
location = models.ForeignKey(Location, on_delete=models.PROTECT)
|
2024-04-13 11:25:09 +00:00
|
|
|
instagram = models.URLField(null=True, blank=True, verbose_name=_('Instagram Profil'))
|
|
|
|
facebook = models.URLField(null=True, blank=True, verbose_name=_('Facebook Profil'))
|
|
|
|
fediverse_profile = models.URLField(null=True, blank=True, verbose_name=_('Fediverse Profil'))
|
2024-03-18 07:26:21 +00:00
|
|
|
website = models.URLField(null=True, blank=True, verbose_name=_('Website'))
|
|
|
|
|
|
|
|
|
2024-03-19 17:18:55 +00:00
|
|
|
class AdoptionNotice(models.Model):
|
2024-04-12 20:23:49 +00:00
|
|
|
class Meta:
|
|
|
|
permissions = [
|
|
|
|
("create_active_adoption_notice", "Can create an active adoption notice"),
|
|
|
|
]
|
|
|
|
|
2024-03-19 17:18:55 +00:00
|
|
|
def __str__(self):
|
|
|
|
return f"{self.name}"
|
|
|
|
|
2024-04-13 11:25:09 +00:00
|
|
|
created_at = models.DateField(verbose_name=_('Erstellt am'), default=datetime.now)
|
|
|
|
searching_since = models.DateField(verbose_name=_('Sucht nach einem Zuhause seit'))
|
2024-03-19 17:18:55 +00:00
|
|
|
name = models.CharField(max_length=200)
|
2024-04-13 11:25:09 +00:00
|
|
|
description = models.TextField(null=True, blank=True, verbose_name=_('Beschreibung'))
|
2024-03-19 17:18:55 +00:00
|
|
|
organization = models.ForeignKey(RescueOrganization, blank=True, null=True, on_delete=models.SET_NULL,
|
2024-04-13 11:25:09 +00:00
|
|
|
verbose_name=_('Organisation'))
|
|
|
|
further_information = models.URLField(null=True, blank=True, verbose_name=_('Link zu mehr Informationen'))
|
|
|
|
group_only = models.BooleanField(default=False, verbose_name=_('Ausschließlich Gruppenadoption'))
|
2024-03-19 17:18:55 +00:00
|
|
|
photos = models.ManyToManyField(Image, blank=True)
|
2024-05-31 08:58:57 +00:00
|
|
|
location_string = models.CharField(max_length=200, verbose_name=_("Ortsangabe"))
|
|
|
|
location = models.ForeignKey(Location, blank=True, null=True, on_delete=models.SET_NULL,)
|
2024-03-19 17:18:55 +00:00
|
|
|
|
|
|
|
@property
|
2024-03-20 09:35:40 +00:00
|
|
|
def animals(self):
|
|
|
|
return Animal.objects.filter(adoption_notice=self)
|
2024-03-19 17:18:55 +00:00
|
|
|
|
2024-05-30 11:58:24 +00:00
|
|
|
@property
|
|
|
|
def comments(self):
|
|
|
|
return Comment.objects.filter(adoption_notice=self)
|
|
|
|
|
2024-03-20 09:32:00 +00:00
|
|
|
def get_absolute_url(self):
|
|
|
|
"""Returns the url to access a detailed page for the animal."""
|
|
|
|
return reverse('adoption-notice-detail', args=[str(self.id)])
|
|
|
|
|
2024-03-22 11:53:39 +00:00
|
|
|
def get_report_url(self):
|
|
|
|
return reverse('report-adoption-notice', args=[str(self.id)])
|
|
|
|
|
2024-03-20 14:40:11 +00:00
|
|
|
def get_photos(self):
|
|
|
|
"""
|
|
|
|
First trys to get group photos that are attached to the adoption notice if there is none it trys to fetch
|
|
|
|
them from the animals
|
|
|
|
"""
|
|
|
|
group_photos = self.photos.all()
|
|
|
|
if len(group_photos) > 0:
|
|
|
|
return group_photos
|
|
|
|
else:
|
|
|
|
photos = []
|
|
|
|
for animal in self.animals:
|
|
|
|
photos.extend(animal.photos.all())
|
|
|
|
if len(photos) > 0:
|
|
|
|
return photos
|
|
|
|
|
|
|
|
def get_photo(self):
|
|
|
|
"""
|
|
|
|
Returns the first photo it finds.
|
|
|
|
First trys to get group photos that are attached to the adoption notice if there is none it trys to fetch
|
|
|
|
them from the animals
|
|
|
|
"""
|
|
|
|
group_photos = self.photos.all()
|
|
|
|
if len(group_photos) > 0:
|
|
|
|
return group_photos[0]
|
|
|
|
else:
|
|
|
|
photos = []
|
|
|
|
for animal in self.animals:
|
|
|
|
photos.extend(animal.photos.all())
|
|
|
|
if len(photos) > 0:
|
|
|
|
return photos[0]
|
|
|
|
|
2024-03-19 17:18:55 +00:00
|
|
|
|
2024-03-18 07:26:21 +00:00
|
|
|
class Animal(models.Model):
|
2024-03-19 05:15:38 +00:00
|
|
|
MALE_NEUTERED = "M_N"
|
|
|
|
MALE = "M"
|
|
|
|
FEMALE_NEUTERED = "F_N"
|
|
|
|
FEMALE = "F"
|
|
|
|
SEX_CHOICES = {
|
2024-03-20 15:40:52 +00:00
|
|
|
MALE_NEUTERED: "neutered male",
|
2024-03-19 05:15:38 +00:00
|
|
|
MALE: "male",
|
2024-03-20 15:40:52 +00:00
|
|
|
FEMALE_NEUTERED: "neutered female",
|
2024-03-19 05:15:38 +00:00
|
|
|
FEMALE: "female",
|
|
|
|
}
|
|
|
|
|
2024-04-13 11:25:09 +00:00
|
|
|
date_of_birth = models.DateField(verbose_name=_('Geburtsdatum'))
|
2024-03-19 05:15:38 +00:00
|
|
|
name = models.CharField(max_length=200)
|
2024-04-13 11:25:09 +00:00
|
|
|
description = models.TextField(null=True, blank=True, verbose_name=_('Beschreibung'))
|
2024-03-19 05:15:38 +00:00
|
|
|
species = models.ForeignKey(Species, on_delete=models.PROTECT)
|
|
|
|
photos = models.ManyToManyField(Image, blank=True)
|
|
|
|
sex = models.CharField(max_length=20, choices=SEX_CHOICES, )
|
2024-03-19 17:18:55 +00:00
|
|
|
adoption_notice = models.ForeignKey(AdoptionNotice, on_delete=models.CASCADE)
|
2024-03-19 05:15:38 +00:00
|
|
|
|
2024-03-18 07:26:21 +00:00
|
|
|
def __str__(self):
|
|
|
|
return f"{self.name}"
|
|
|
|
|
2024-03-19 05:15:38 +00:00
|
|
|
@property
|
|
|
|
def age(self):
|
|
|
|
return datetime.today().date() - self.date_of_birth
|
|
|
|
|
|
|
|
@property
|
|
|
|
def hr_age(self):
|
|
|
|
"""Returns a human-readable age based on the date of birth."""
|
|
|
|
return misc.age_as_hr_string(self.age)
|
|
|
|
|
2024-03-20 15:40:52 +00:00
|
|
|
def get_photo(self):
|
|
|
|
"""
|
|
|
|
Selects a random photo from the animal
|
|
|
|
"""
|
|
|
|
photos = self.photos.all()
|
|
|
|
if len(photos) > 0:
|
|
|
|
return photos[0]
|
|
|
|
|
|
|
|
def get_photos(self):
|
|
|
|
"""
|
|
|
|
Selects all photos from the animal
|
|
|
|
"""
|
2024-03-20 12:40:00 +00:00
|
|
|
return self.photos.all()
|
|
|
|
|
2024-03-18 21:50:39 +00:00
|
|
|
def get_absolute_url(self):
|
|
|
|
"""Returns the url to access a detailed page for the animal."""
|
|
|
|
return reverse('animal-detail', args=[str(self.id)])
|
|
|
|
|
2024-03-18 13:26:50 +00:00
|
|
|
|
2024-03-20 10:02:24 +00:00
|
|
|
class Rule(models.Model):
|
|
|
|
"""
|
|
|
|
Class to store rules
|
|
|
|
"""
|
|
|
|
title = models.CharField(max_length=200)
|
|
|
|
|
|
|
|
# Markdown is allowed in rule text
|
|
|
|
rule_text = models.TextField()
|
2024-04-14 13:56:42 +00:00
|
|
|
language = models.ForeignKey(Language, on_delete=models.PROTECT)
|
|
|
|
# Rule identifier allows to translate rules with the same identifier
|
|
|
|
rule_identifier = models.CharField(max_length=24)
|
2024-03-20 10:02:24 +00:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.title
|
|
|
|
|
2024-03-22 11:45:50 +00:00
|
|
|
|
|
|
|
class Report(models.Model):
|
2024-04-12 20:23:49 +00:00
|
|
|
class Meta:
|
|
|
|
permissions = []
|
|
|
|
|
2024-03-22 11:45:50 +00:00
|
|
|
ACTION_TAKEN = "action taken"
|
|
|
|
NO_ACTION_TAKEN = "no action taken"
|
|
|
|
WAITING = "waiting"
|
|
|
|
STATES = {
|
|
|
|
ACTION_TAKEN: "Action was taken",
|
|
|
|
NO_ACTION_TAKEN: "No action was taken",
|
|
|
|
WAITING: "Waiting for moderator action",
|
|
|
|
}
|
|
|
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text=_('ID dieses reports'),
|
|
|
|
verbose_name=_('ID'))
|
|
|
|
status = models.CharField(max_length=30, choices=STATES)
|
2024-05-30 13:46:51 +00:00
|
|
|
reported_broken_rules = models.ManyToManyField(Rule)
|
|
|
|
user_comment = models.TextField(blank=True)
|
2024-03-22 11:45:50 +00:00
|
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
|
|
|
|
def __str__(self):
|
2024-05-30 13:46:51 +00:00
|
|
|
return f"[{self.status}]: {self.user_comment:.20}"
|
2024-03-22 11:45:50 +00:00
|
|
|
|
2024-03-25 09:49:56 +00:00
|
|
|
def get_absolute_url(self):
|
|
|
|
"""Returns the url to access a detailed page for the report."""
|
|
|
|
return reverse('report-detail', args=[str(self.id)])
|
|
|
|
|
2024-03-22 11:45:50 +00:00
|
|
|
def get_reported_rules(self):
|
|
|
|
return self.reported_broken_rules.all()
|
|
|
|
|
|
|
|
def get_moderation_actions(self):
|
|
|
|
return ModerationAction.objects.filter(report=self)
|
|
|
|
|
|
|
|
|
2024-05-30 13:46:51 +00:00
|
|
|
class ReportAdoptionNotice(Report):
|
|
|
|
adoption_notice = models.ForeignKey("AdoptionNotice", on_delete=models.CASCADE)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def reported_content(self):
|
|
|
|
return self.adoption_notice
|
|
|
|
|
|
|
|
|
|
|
|
class ReportComment(Report):
|
|
|
|
reported_comment = models.ForeignKey("Comment", on_delete=models.CASCADE)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def reported_content(self):
|
|
|
|
return self.reported_comment
|
|
|
|
|
|
|
|
|
2024-03-22 11:45:50 +00:00
|
|
|
class ModerationAction(models.Model):
|
|
|
|
BAN = "user_banned"
|
|
|
|
DELETE = "content_deleted"
|
|
|
|
COMMENT = "comment"
|
|
|
|
OTHER = "other_action_taken"
|
|
|
|
NONE = "no_action_taken"
|
|
|
|
ACTIONS = {
|
|
|
|
BAN: "User was banned",
|
|
|
|
DELETE: "Content was deleted",
|
|
|
|
COMMENT: "Comment was added",
|
|
|
|
OTHER: "Other action was taken",
|
|
|
|
NONE: "No action was taken"
|
|
|
|
}
|
|
|
|
action = models.CharField(max_length=30, choices=ACTIONS.items())
|
|
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
public_comment = models.TextField(blank=True)
|
|
|
|
# Only visible to moderator
|
|
|
|
private_comment = models.TextField(blank=True)
|
|
|
|
report = models.ForeignKey(Report, on_delete=models.CASCADE)
|
|
|
|
|
|
|
|
# TODO: Needs field for moderator that performed the action
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return f"[{self.action}]: {self.public_comment}"
|
2024-03-23 21:20:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
Membership
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
2024-04-07 09:33:41 +00:00
|
|
|
class User(AbstractUser):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2024-03-23 21:20:31 +00:00
|
|
|
class Member(models.Model):
|
|
|
|
"""
|
|
|
|
Model that holds a user's profile, including the django user model
|
|
|
|
|
|
|
|
It is created upon creation of a new django user (see add_member)
|
|
|
|
The trust levels act as permission system and can be displayed as a badge for the user
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Admins can perform all actions and have the highest trust associated with them
|
|
|
|
# Moderators can make moderation decisions regarding the deletion of content
|
|
|
|
# Coordinators can create adoption notices without them being checked
|
|
|
|
# Members can create adoption notices that must be activated
|
|
|
|
ADMIN = "admin"
|
|
|
|
MODERATOR = "Moderator"
|
|
|
|
COORDINATOR = "Koordinator*in"
|
|
|
|
MEMBER = "Mitglied"
|
|
|
|
TRUES_LEVEL = {
|
|
|
|
ADMIN: "Administrator*in",
|
|
|
|
MODERATOR: "Moderator*in",
|
|
|
|
COORDINATOR: "Koordinator*in",
|
|
|
|
MEMBER: "Mitglied",
|
|
|
|
}
|
|
|
|
|
2024-04-13 11:25:09 +00:00
|
|
|
user = models.OneToOneField(User, on_delete=models.CASCADE, verbose_name=_('Nutzer*in'))
|
2024-03-23 21:20:31 +00:00
|
|
|
preferred_language = models.ForeignKey(Language, on_delete=models.PROTECT, null=True, blank=True,
|
2024-04-13 11:25:09 +00:00
|
|
|
verbose_name=_('Bevorzugte Sprache'))
|
2024-03-23 21:20:31 +00:00
|
|
|
trust_level = models.CharField(choices=TRUES_LEVEL, max_length=100, default=MEMBER)
|
|
|
|
|
|
|
|
class Meta:
|
2024-04-13 11:25:09 +00:00
|
|
|
verbose_name = _('Nutzer*in')
|
|
|
|
verbose_name_plural = _('Nutzer*innen')
|
2024-03-23 21:20:31 +00:00
|
|
|
|
|
|
|
@receiver(post_save, sender=User)
|
|
|
|
def add_member(sender, instance, created, raw, using, **kwargs):
|
|
|
|
if len(Member.objects.filter(user=instance)) != 1:
|
|
|
|
Member.objects.create(user=instance)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return str(self.user)
|
|
|
|
|
|
|
|
def get_absolute_url(self):
|
|
|
|
return reverse("member-detail", args=[str(self.user.id)])
|
2024-04-14 11:26:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Text(models.Model):
|
|
|
|
"""
|
|
|
|
Base class to store markdown content
|
|
|
|
"""
|
|
|
|
title = models.CharField(max_length=100)
|
2024-04-14 11:37:32 +00:00
|
|
|
content = models.TextField(verbose_name="Inhalt")
|
2024-04-14 12:14:35 +00:00
|
|
|
language = models.ForeignKey(Language, verbose_name="Sprache", on_delete=models.PROTECT)
|
2024-04-14 11:37:32 +00:00
|
|
|
text_code = models.CharField(max_length=24, verbose_name="Text code", blank=True)
|
2024-04-14 11:26:21 +00:00
|
|
|
|
|
|
|
class Meta:
|
2024-04-14 11:37:32 +00:00
|
|
|
verbose_name = "Text"
|
|
|
|
verbose_name_plural = "Texte"
|
2024-04-14 11:26:21 +00:00
|
|
|
|
|
|
|
def __str__(self):
|
2024-04-14 12:14:35 +00:00
|
|
|
return f"{self.title} ({self.language})"
|
2024-05-30 11:58:24 +00:00
|
|
|
|
|
|
|
|
2024-05-31 07:58:03 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-05-30 11:58:24 +00:00
|
|
|
class Comment(models.Model):
|
|
|
|
"""
|
|
|
|
Class to store comments in markdown content
|
|
|
|
"""
|
|
|
|
user = models.ForeignKey(Member, on_delete=models.CASCADE, verbose_name=_('Nutzer*in'))
|
|
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
adoption_notice = models.ForeignKey(AdoptionNotice, on_delete=models.CASCADE, verbose_name=_('AdoptionNotice'))
|
|
|
|
text = models.TextField(verbose_name="Inhalt")
|
|
|
|
reply_to = models.ForeignKey("self", verbose_name="Antwort auf", blank=True, null=True, on_delete=models.CASCADE)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return f"{self.user} at {self.created_at.strftime('%H:%M %d.%m.%y')}: {self.text:.10}"
|
2024-05-30 13:46:51 +00:00
|
|
|
|
|
|
|
def get_report_url(self):
|
|
|
|
return reverse('report-comment', args=[str(self.id)])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def get_absolute_url(self):
|
|
|
|
return self.adoption_notice.get_absolute_url()
|