diff --git a/backend/announcements/admin.py b/backend/announcements/admin.py new file mode 100644 index 0000000..ba0a287 --- /dev/null +++ b/backend/announcements/admin.py @@ -0,0 +1,13 @@ +from django.conf import settings +from django.contrib import admin +from django.contrib.auth.models import Permission +from django.shortcuts import redirect +from django.urls import reverse + +from announcements.models import ( + Audience, + Announcement +) + +admin.site.register(Audience) +admin.site.register(Announcement) diff --git a/backend/announcements/migrations/0001_initial.py b/backend/announcements/migrations/0001_initial.py new file mode 100644 index 0000000..a0bc9fc --- /dev/null +++ b/backend/announcements/migrations/0001_initial.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.5 on 2023-10-13 17:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Audience', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(choices=[('MOBILE', 'Penn Mobile'), ('OHQ', 'OHQ'), ('CLUBS', 'Penn Clubs'), ('COURSE_PLAN', 'Penn Course Plan'), ('COURSE_REVIEW', 'Penn Course Review'), ('COURSE_ALERT', 'Penn Course Alert')], max_length=20)), + ], + ), + migrations.CreateModel( + name='Announcement', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(blank=True, max_length=255, null=True)), + ('message', models.TextField()), + ('announcement_type', models.IntegerField(choices=[(1, 'Banner'), (2, 'Issue'), (3, 'Notice')], default=3)), + ('release_time', models.DateTimeField(auto_now_add=True)), + ('end_time', models.DateTimeField(blank=True, null=True)), + ('audiences', models.ManyToManyField(related_name='announcements', to='announcements.audience')), + ], + ), + ] diff --git a/backend/announcements/migrations/__init__.py b/backend/announcements/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/announcements/models.py b/backend/announcements/models.py index 3808ddf..6d19df5 100644 --- a/backend/announcements/models.py +++ b/backend/announcements/models.py @@ -1,10 +1,8 @@ from django.db import models -from django.contrib.postgres.fields import ArrayField - -class Announcement(models.Model): +class Audience(models.Model): """ - Represents an announcement in any of labs services. + Represents a product that an announcement is intended for. """ AUDIENCE_MOBILE = "MOBILE" @@ -23,9 +21,19 @@ class Announcement(models.Model): (AUDIENCE_COURSE_ALERT, "Penn Course Alert"), ] - ANNOUNCEMENT_BANNER = "BANNER" - ANNOUNCEMENT_ISSUE = "ISSUE" - ANNOUNCEMENT_NOTICE = "NOTICE" + name = models.CharField(choices=AUDIENCE_CHOICES, max_length=20) + + def __str__(self): + return self.name + +class Announcement(models.Model): + """ + Represents an announcement for any of the Penn Labs services. + """ + + ANNOUNCEMENT_BANNER = 1 + ANNOUNCEMENT_ISSUE = 2 + ANNOUNCEMENT_NOTICE = 3 ANNOUNCEMENT_CHOICES = [ (ANNOUNCEMENT_BANNER, "Banner"), @@ -39,20 +47,13 @@ class Announcement(models.Model): null=True, ) message = models.TextField() - announcement_type = models.CharField( - max_length=20, + announcement_type = models.IntegerField( choices=ANNOUNCEMENT_CHOICES, default=ANNOUNCEMENT_NOTICE, ) - audience = ArrayField( - models.CharField(max_length=20, choices=AUDIENCE_CHOICES), blank=True - ) - schedule = ArrayField( - ArrayField( - models.DateTimeField(), - size=2, - ), - ) + audiences = models.ManyToManyField("Audience", related_name="announcements") + release_time = models.DateTimeField(auto_now_add=True) + end_time = models.DateTimeField(null=True, blank=True) def __str__(self): - return f"[{self.announcement_type}: {self.audience}] @ {self.schedule} {self.title} - {self.message}" + return f"[{self.get_announcement_type_display()} for {','.join([audience.name for audience in self.audiences.all()])}] starting at {self.release_time.strftime('%m-%d-%Y %H:%M:%S')} {f'''to {self.end_time.strftime('%m-%d-%Y %H:%M:%S')}''' if self.end_time else ''} | {f'{self.title}: ' if self.title else ''} {self.message}" diff --git a/backend/announcements/permissions.py b/backend/announcements/permissions.py new file mode 100644 index 0000000..5a8be98 --- /dev/null +++ b/backend/announcements/permissions.py @@ -0,0 +1,12 @@ +from rest_framework import permissions + +class IsSuperuser(permissions.BasePermission): + """ + Grants permission if the current user is a superuser. + """ + + def has_object_permission(self, request, view, obj): + return request.user.is_authenticated and request.user.is_superuser + + def has_permission(self, request, view): + return request.user.is_authenticated and request.user.is_superuser \ No newline at end of file diff --git a/backend/announcements/serializers.py b/backend/announcements/serializers.py index 7913ed7..20c0630 100644 --- a/backend/announcements/serializers.py +++ b/backend/announcements/serializers.py @@ -1,8 +1,14 @@ from rest_framework import serializers -from announcements.models import Announcement +from announcements.models import Announcement, Audience +class AudienceSerializer(serializers.ModelSerializer): + + class Meta: + model = Audience + fields = "__all__" class AnnouncementSerializer(serializers.ModelSerializer): + class Meta: model = Announcement - fields = ("id", "title", "message", "announcement_type", "audience", "schedule") + fields = "__all__" diff --git a/backend/announcements/urls.py b/backend/announcements/urls.py index c09e230..c533b75 100644 --- a/backend/announcements/urls.py +++ b/backend/announcements/urls.py @@ -1,6 +1,8 @@ from django.urls import path -from announcements.views import AnnouncementsView +from announcements.views import AnnouncementsViewSet +from rest_framework import routers -app_name = "announcement" - -urlpatterns = [path("list", AnnouncementsView.as_view(), name="list")] +app_name = "announcements" +router = routers.SimpleRouter() +router.register("", AnnouncementsViewSet) +urlpatterns = router.urls diff --git a/backend/announcements/views.py b/backend/announcements/views.py index 47490f4..eeb41c2 100644 --- a/backend/announcements/views.py +++ b/backend/announcements/views.py @@ -1,12 +1,14 @@ from announcements.serializers import AnnouncementSerializer -from rest_framework import generics, mixins, status, viewsets +from rest_framework import viewsets from announcements.models import Announcement -from rest_framework.response import Response +from announcements.permissions import IsSuperuser -class AnnouncementsView(generics.ListAPIView): +class AnnouncementsViewSet(viewsets.ModelViewSet): serializer_class = AnnouncementSerializer + queryset = Announcement.objects.all() - def list(self, request): - announcement_list = Announcement.objects.all() - return Response(announcement_list) + def get_permissions(self): + if self.request.method != "GET": + self.permission_classes = [IsSuperuser] + return super(AnnouncementsViewSet, self).get_permissions()