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-06-08 09:44:21 +00:00
from . tools import misc , geo
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-06-08 07:31:23 +00:00
class User ( AbstractUser ) :
"""
Model that holds a user ' s profile, including the django user model
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 "
2024-06-08 10:32:57 +00:00
TRUST_LEVEL = {
ADMIN : 4 ,
MODERATOR : 3 ,
COORDINATOR : 2 ,
MEMBER : 1 ,
2024-06-08 07:31:23 +00:00
}
preferred_language = models . ForeignKey ( Language , on_delete = models . PROTECT , null = True , blank = True ,
verbose_name = _ ( ' Bevorzugte Sprache ' ) )
2024-06-08 10:32:57 +00:00
trust_level = models . IntegerField ( choices = TRUST_LEVEL , default = TRUST_LEVEL [ MEMBER ] )
2024-06-08 07:31:23 +00:00
class Meta :
verbose_name = _ ( ' Nutzer*in ' )
verbose_name_plural = _ ( ' Nutzer*innen ' )
def get_absolute_url ( self ) :
return reverse ( " user-detail " , args = [ str ( self . pk ) ] )
2024-08-02 17:11:55 +00:00
def get_notifications_url ( self ) :
return self . get_absolute_url ( )
2024-08-03 06:24:40 +00:00
def get_num_unread_notifications ( self ) :
return BaseNotification . objects . filter ( user = self , read = False ) . count ( )
2024-08-06 17:28:03 +00:00
@property
def owner ( self ) :
return self
2024-06-08 07:31:23 +00:00
2024-03-18 16:10:48 +00:00
class Image ( models . Model ) :
image = models . ImageField ( upload_to = ' images ' )
alt_text = models . TextField ( max_length = 2000 )
2024-08-06 17:28:03 +00:00
owner = models . ForeignKey ( User , on_delete = models . CASCADE )
2024-03-18 16:10:48 +00:00
def __str__ ( self ) :
2024-05-31 11:51:14 +00:00
return self . alt_text
2024-03-18 16:10:48 +00:00
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 ) :
2024-05-31 08:58:57 +00:00
place_id = models . IntegerField ( )
latitude = models . FloatField ( )
longitude = models . FloatField ( )
name = models . CharField ( max_length = 2000 )
2024-03-18 07:26:21 +00:00
2024-05-31 10:17:53 +00:00
def __str__ ( self ) :
return f " { self . name } ( { self . latitude : .5 } , { self . longitude : .5 } ) "
@staticmethod
def get_location_from_string ( location_string ) :
geo_api = geo . GeoAPI ( )
2024-05-31 11:51:14 +00:00
geojson = geo_api . get_geojson_for_query ( location_string )
if geojson is None :
return None
result = geojson [ 0 ]
2024-05-31 10:17:53 +00:00
if " name " in result :
name = result [ " name " ]
else :
name = result [ " display_name " ]
location = Location . objects . create (
place_id = result [ " place_id " ] ,
latitude = result [ " lat " ] ,
longitude = result [ " lon " ] ,
name = name ,
)
return location
2024-03-18 07:26:21 +00:00
class RescueOrganization ( models . Model ) :
def __str__ ( self ) :
return f " { self . name } "
2024-09-28 21:02:13 +00:00
USE_MATERIALS_ALLOWED = " allowed "
USE_MATERIALS_REQUESTED = " requested "
USE_MATERIALS_DENIED = " denied "
USE_MATERIALS_OTHER = " other "
USE_MATERIALS_NOT_ASKED = " not_asked "
ALLOW_USE_MATERIALS_CHOICE = {
USE_MATERIALS_ALLOWED : " Usage allowed " ,
USE_MATERIALS_REQUESTED : " Usage requested " ,
USE_MATERIALS_DENIED : " Usage denied " ,
USE_MATERIALS_OTHER : " It ' s complicated " ,
USE_MATERIALS_NOT_ASKED : " Not asked "
}
2024-03-18 07:26:21 +00:00
name = models . CharField ( max_length = 200 )
2024-04-13 11:25:09 +00:00
trusted = models . BooleanField ( default = False , verbose_name = _ ( ' Vertrauenswürdig ' ) )
2024-09-28 21:02:13 +00:00
allows_using_materials = models . CharField ( max_length = 200 , default = ALLOW_USE_MATERIALS_CHOICE [ USE_MATERIALS_NOT_ASKED ] , choices = ALLOW_USE_MATERIALS_CHOICE , verbose_name = _ ( ' Erlaubt Nutzung von Inhalten ' ) )
2024-05-31 08:58:57 +00:00
location_string = models . CharField ( max_length = 200 , verbose_name = _ ( " Ort der Organisation " ) )
2024-09-28 21:02:13 +00:00
location = models . ForeignKey ( Location , on_delete = models . PROTECT , blank = True , null = True )
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 " ) )
2024-05-31 13:57:38 +00:00
location = models . ForeignKey ( Location , blank = True , null = True , on_delete = models . SET_NULL , )
2024-08-06 17:28:03 +00:00
owner = models . ForeignKey ( User , on_delete = models . CASCADE , verbose_name = _ ( ' Creator ' ) )
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-05-31 10:52:57 +00:00
@property
def position ( self ) :
if self . location is None :
return None
else :
2024-05-31 11:48:22 +00:00
return self . location . latitude , self . location . longitude
2024-05-31 10:52:57 +00:00
2024-09-27 14:34:44 +00:00
@property
def description_short ( self ) :
2024-09-27 22:41:51 +00:00
if self . description is None :
return " "
2024-09-27 14:34:44 +00:00
if len ( self . description ) > 200 :
return self . description [ : 200 ] + f " ... [weiterlesen]( { self . get_absolute_url ( ) } ) "
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-08-02 18:03:25 +00:00
def get_subscriptions ( self ) :
return Subscriptions . objects . filter ( adoption_notice = self )
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-05-31 10:53:06 +00:00
def in_distance ( self , position , max_distance , unknown_true = True ) :
"""
Returns a boolean indicating if the Location of the adoption notice is within a given distance to the position
If the location is none , we by default return that the location is within the given distance
"""
if unknown_true and self . position is None :
return True
distance = geo . calculate_distance_between_coordinates ( self . position , position )
return distance < max_distance
2024-05-31 13:47:22 +00:00
@property
def link_to_more_information ( self ) :
from urllib . parse import urlparse
domain = urlparse ( self . further_information ) . netloc
return f " <a href= ' { self . further_information } ' > { domain } </a> "
2024-05-31 14:00:52 +00:00
@property
def is_active ( self ) :
2024-05-31 14:25:50 +00:00
if not hasattr ( self , ' adoptionnoticestatus ' ) :
return False
return self . adoptionnoticestatus . is_active
class AdoptionNoticeStatus ( models . Model ) :
"""
The major status indicates a general state of an adoption notice
whereas the minor status is used for reporting
"""
ACTIVE = " active "
2024-06-08 10:32:57 +00:00
AWAITING_ACTION = " awaiting_action "
2024-05-31 14:25:50 +00:00
CLOSED = " closed "
DISABLED = " disabled "
MAJOR_STATUS_CHOICES = {
ACTIVE : " active " ,
2024-06-08 10:32:57 +00:00
AWAITING_ACTION : " in review " ,
2024-05-31 14:25:50 +00:00
CLOSED : " closed " ,
DISABLED : " disabled " ,
}
MINOR_STATUS_CHOICES = {
ACTIVE : {
" searching " : " searching " ,
" interested " : " interested " ,
} ,
2024-06-08 10:32:57 +00:00
AWAITING_ACTION : {
2024-05-31 14:25:50 +00:00
" waiting_for_review " : " waiting_for_review " ,
2024-06-08 10:32:57 +00:00
" needs_additional_info " : " needs_additional_info " ,
2024-05-31 14:25:50 +00:00
} ,
CLOSED : {
" successful_with_notfellchen " : " successful_with_notfellchen " ,
" successful_without_notfellchen " : " successful_without_notfellchen " ,
" animal_died " : " animal_died " ,
" closed_for_other_adoption_notice " : " closed_for_other_adoption_notice " ,
" not_open_for_adoption_anymore " : " not_open_for_adoption_anymore " ,
" other " : " other "
} ,
DISABLED : {
" against_the_rules " : " against_the_rules " ,
" missing_information " : " missing_information " ,
" technical_error " : " technical_error " ,
" other " : " other "
}
}
major_status = models . CharField ( choices = MAJOR_STATUS_CHOICES , max_length = 200 )
minor_choices = { }
for key in MINOR_STATUS_CHOICES :
minor_choices . update ( MINOR_STATUS_CHOICES [ key ] )
minor_status = models . CharField ( choices = minor_choices , max_length = 200 )
adoption_notice = models . OneToOneField ( AdoptionNotice , on_delete = models . CASCADE )
def __str__ ( self ) :
return f " { self . adoption_notice } : { self . major_status } , { self . minor_status } "
@property
def is_active ( self ) :
return self . major_status == self . ACTIVE
@staticmethod
def get_minor_choices ( major_status ) :
return AdoptionNoticeStatus . MINOR_STATUS_CHOICES [ major_status ]
2024-05-31 14:00:52 +00:00
2024-05-31 10:53:06 +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-08-06 17:28:03 +00:00
owner = models . ForeignKey ( User , 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-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
"""
2024-06-08 07:31:23 +00:00
user = models . ForeignKey ( User , on_delete = models . CASCADE , verbose_name = _ ( ' Nutzer*in ' ) )
2024-05-30 11:58:24 +00:00
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 ( )
2024-08-02 17:11:55 +00:00
class BaseNotification ( models . Model ) :
created_at = models . DateTimeField ( auto_now_add = True )
title = models . CharField ( max_length = 100 )
text = models . TextField ( verbose_name = " Inhalt " )
user = models . ForeignKey ( User , on_delete = models . CASCADE , verbose_name = _ ( ' Nutzer*in ' ) )
read = models . BooleanField ( default = False )
def __str__ ( self ) :
return f " [ { self . user } ] { self . title } ( { self . created_at } ) "
def get_absolute_url ( self ) :
self . user . get_notifications_url ( )
class CommentNotification ( BaseNotification ) :
comment = models . ForeignKey ( Comment , on_delete = models . CASCADE , verbose_name = _ ( ' Antwort ' ) )
2024-08-02 17:31:32 +00:00
2024-08-03 07:02:09 +00:00
@property
def url ( self ) :
print ( f " URL: self.comment.get_absolute_url() " )
return self . comment . get_absolute_url
2024-08-02 17:31:32 +00:00
class Subscriptions ( models . Model ) :
2024-08-06 17:28:03 +00:00
owner = models . ForeignKey ( User , on_delete = models . CASCADE , verbose_name = _ ( ' Nutzer*in ' ) )
2024-08-02 17:31:32 +00:00
adoption_notice = models . ForeignKey ( AdoptionNotice , on_delete = models . CASCADE , verbose_name = _ ( ' AdoptionNotice ' ) )
2024-08-02 18:03:25 +00:00
created_at = models . DateTimeField ( auto_now_add = True )