Source code for intranet.apps.announcements.models

from datetime import datetime

from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group as DjangoGroup
from django.db import models
from django.db.models import Manager, Q
from django.utils import timezone
from simple_history.models import HistoricalRecords

from ...utils.date import get_date_range_this_year, is_current_year
from ...utils.deletion import set_historical_user
from ...utils.html import nullify_links
from ..eighth.models import EighthActivity, EighthSponsor


[docs]class AnnouncementManager(Manager):
[docs] def visible_to_user(self, user): """Get a list of visible announcements for a given user (usually request.user). These visible announcements will be those that either have no groups assigned to them (and are therefore public) or those in which the user is a member. Apparently this .filter() call occasionally returns duplicates, hence the .distinct()... """ if user.is_restricted: # Restricted users are not authorized to view announcements return Announcement.objects.none() return Announcement.objects.filter( Q(user=user) | Q(groups__isnull=True) | Q(groups__user=user) | Q(announcementrequest__teachers_requested=user) | Q(announcementrequest__user=user) ).distinct()
[docs] def hidden_announcements(self, user): """Get a list of announcements marked as hidden for a given user (usually request.user). These are all announcements visible to the user -- they have just decided to hide them. """ ids = user.announcements_hidden.all().values_list("announcement__id") return Announcement.objects.filter(id__in=ids)
[docs] def this_year(self): """Get AnnouncementRequests from this school year only.""" start_date, end_date = get_date_range_this_year() return Announcement.objects.filter(added__gte=start_date, added__lte=end_date)
[docs]class AnnouncementUserMap(models.Model): """Represents mapping fields between announcements and users. These attributes would be a part of the Announcement model, but if they are, the last updated date is changed whenever a student sees or hides an announcement. Access these through announcement.user_map If you are checking to see whether a user has hidden an announcement, use: Announcement.objects.hidden_announcements(user) Attributes: announcement The one-to-one mapping between this object and the Announcement it is for users_hidden A many-to-many field of Users who have hidden this announcement users_seen A many-to-many field of Users who have seen this announcement """ announcement = models.OneToOneField("Announcement", related_name="_user_map", on_delete=models.CASCADE) users_hidden = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name="announcements_hidden") users_seen = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name="announcements_seen") def __str__(self): return f"UserMap: {self.announcement.title}"
[docs]class Announcement(models.Model): """Represents an announcement. Attributes: title The title of the announcement content The HTML content of the news post author The name of the author added The date the announcement was added updated The most recent date the announcement was updated user_map An attribute corresponding with an AnnouncementUserMap object. A new object is automatically created if it does not exist. """ objects = AnnouncementManager() title = models.CharField(max_length=127) content = models.TextField() author = models.CharField(max_length=63, blank=True) user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=set_historical_user) added = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) groups = models.ManyToManyField(DjangoGroup, blank=True) activity = models.ForeignKey(EighthActivity, null=True, blank=True, on_delete=models.CASCADE) expiration_date = models.DateTimeField(auto_now=False, default=timezone.make_aware(datetime(3000, 1, 1))) notify_post = models.BooleanField(default=True) notify_email_all = models.BooleanField(default=False) pinned = models.BooleanField(default=False) history = HistoricalRecords()
[docs] def get_author(self) -> str: """Returns 'author' if it is set. Otherwise, returns the name of the user who created the announcement. Returns: The name of the author as it should be displayed with the announcement. """ return self.author if self.author else self.user.full_name_nick if self.user else None
def __str__(self): return self.title @property def user_map(self): try: return self._user_map # pylint: disable=no-member; Defined via a related_name in AnnouncementUserMap except AnnouncementUserMap.DoesNotExist: return AnnouncementUserMap.objects.create(announcement=self) @property def is_this_year(self): """Return whether the announcement was created after July 1st of this school year.""" return is_current_year(self.added) @property def is_club_announcement(self): return self.activity is not None
[docs] def is_visible(self, user): return self in Announcement.objects.visible_to_user(user)
[docs] def can_modify(self, user): return ( user.is_announcements_admin or self.is_club_announcement and ( user in self.activity.officers.all() or user in self.activity.club_sponsors.all() or EighthSponsor.objects.filter(user=user).exists() and user.sponsor_obj in self.activity.sponsors.all() ) )
# False, not None. This can be None if no AnnouncementRequest exists for this Announcement, # and we should not reevaluate in that case. _announcementrequest = False # type: AnnouncementRequest @property def announcementrequest(self): if self._announcementrequest is False: self._announcementrequest = self.announcementrequest_set.first() return self._announcementrequest
[docs] def is_visible_requester(self, user): try: return self.announcementrequest_set.filter(teachers_requested=user).exists() except get_user_model().DoesNotExist: return False
[docs] def is_visible_submitter(self, user): try: return self.user == user or self.announcementrequest and user == self.announcementrequest.user except get_user_model().DoesNotExist: return False
@property def dashboard_type(self): return "announcement" @property def content_no_links(self) -> str: """Returns the content of this announcement with all links nullified. Returns: The content of this announcement with all links nullified. """ return nullify_links(self.content) class Meta: ordering = ["-pinned", "-added"]
[docs]class AnnouncementRequestQuerySet(models.query.QuerySet):
[docs] def this_year(self): """Get AnnouncementRequests from this school year only.""" start_date, end_date = get_date_range_this_year() return self.filter(added__gte=start_date, added__lte=end_date)
[docs]class AnnouncementRequestManager(Manager):
[docs] def get_queryset(self): return AnnouncementRequestQuerySet(self.model, using=self._db)
[docs]class AnnouncementRequest(models.Model): """Represents a request for an announcement. Attributes: title The title of the announcement content The HTML content of the news post notes Notes for the person who approves the announcement added The date the request was added updated The most recent date the request was updated user The user who submitted the request teachers_requested The teachers requested to approve the request teachers_approved The teachers who have approved the request posted ForeignKey to Announcement if posted posted_by The user (administrator) that approved the request rejected Boolean describing whether the post was rejected by an administrator. This will hide it. admin_email_sent Boolean describing whether an email was sent to an Intranet administrator to post the announcement. """ objects = AnnouncementRequestManager() title = models.CharField(max_length=127) content = models.TextField() author = models.CharField(max_length=63, blank=True) expiration_date = models.DateTimeField(auto_now=False, default=timezone.make_aware(datetime(3000, 1, 1))) notes = models.TextField(blank=True) added = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, related_name="user", on_delete=set_historical_user) teachers_requested = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=False, related_name="teachers_requested") teachers_approved = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name="teachers_approved") posted = models.ForeignKey(Announcement, null=True, blank=True, on_delete=models.CASCADE) posted_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, related_name="posted_by", on_delete=set_historical_user) rejected = models.BooleanField(default=False) rejected_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, related_name="rejected_by", on_delete=set_historical_user) admin_email_sent = models.BooleanField(default=False) def __str__(self): return self.title class Meta: ordering = ["-added"]
[docs]class WarningAnnouncement(models.Model): """Warning in yellow to show on Ion""" title = models.CharField(max_length=127) content = models.TextField(help_text="Content of the warning. You can use HTML here.") type = models.CharField( max_length=127, choices=[ ("dashboard", "Dashboard Warning (displays on dashboard)"), ("login", "Login Warning (displays on login page)"), ("dashboard_login", "Dashboard and Login Warning (displays on dashboard and login pages)"), ("global", "Global Warning (displays on all pages)"), ], default="dashboard", ) active = models.BooleanField(default=True, help_text="Whether or not to show the warning.") added = models.DateTimeField(auto_now_add=True) history = HistoricalRecords() @property def show_on_dashboard(self): return self.type in ("dashboard", "dashboard_login") # global is not included. It will show on all pages and this logic isn't needed. @property def show_on_login(self): return self.type in ("login", "dashboard_login") def __str__(self): return self.title class Meta: ordering = ["-added"]