Skip to content

Commit

Permalink
Use CharField & Remove "Banner" (#166)
Browse files Browse the repository at this point in the history
* fix(announcement): use char instead of integer for announcement_type

* lint: lol I didn't even have flake8 installed

* update Announcement serializer

* unpin black version + lint

* isort

---------

Co-authored-by: Rohan Moniz <rohan.moniz@gmail.com>
  • Loading branch information
esinx and rm03 authored Nov 2, 2023
1 parent faea762 commit 7014b93
Show file tree
Hide file tree
Showing 11 changed files with 579 additions and 544 deletions.
2 changes: 1 addition & 1 deletion backend/Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]
black = "==21.9b0"
black = "*"
unittest-xml-reporting = "*"
flake8 = "*"
flake8-isort = "*"
Expand Down
931 changes: 430 additions & 501 deletions backend/Pipfile.lock

Large diffs are not rendered by default.

9 changes: 1 addition & 8 deletions backend/announcements/admin.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
from django.conf import settings
from announcements.models import Announcement, Audience
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)
15 changes: 10 additions & 5 deletions backend/announcements/management/commands/populate_audiences.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
from django.core.management import BaseCommand

from announcements.models import Audience
from django.core.management import BaseCommand


class Command(BaseCommand):

def handle(self, *args, **options):
for x in ["MOBILE", "OHQ", "CLUBS", "COURSE_PLAN", "COURSE_REVIEW", "COURSE_ALERT"]:
Audience.objects.get_or_create(name=x)
for x in [
"MOBILE",
"OHQ",
"CLUBS",
"COURSE_PLAN",
"COURSE_REVIEW",
"COURSE_ALERT",
]:
Audience.objects.get_or_create(name=x)
65 changes: 51 additions & 14 deletions backend/announcements/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,67 @@


class Migration(migrations.Migration):

initial = True

dependencies = [
]
dependencies = []

operations = [
migrations.CreateModel(
name='Audience',
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)),
(
"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',
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')),
(
"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"
),
),
],
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 3.2.21 on 2023-10-26 18:28

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("announcements", "0001_initial"),
]

operations = [
migrations.AlterField(
model_name="announcement",
name="announcement_type",
field=models.CharField(
choices=[("NOTICE", "Notice"), ("ISSUE", "Issue")],
default="NOTICE",
max_length=20,
),
),
]
20 changes: 13 additions & 7 deletions backend/announcements/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.db import models


class Audience(models.Model):
"""
Represents a product that an announcement is intended for.
Expand All @@ -26,19 +27,18 @@ class Audience(models.Model):
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_NOTICE = "NOTICE"
ANNOUNCEMENT_ISSUE = "ISSUE"

ANNOUNCEMENT_CHOICES = [
(ANNOUNCEMENT_BANNER, "Banner"),
(ANNOUNCEMENT_ISSUE, "Issue"),
(ANNOUNCEMENT_NOTICE, "Notice"),
(ANNOUNCEMENT_ISSUE, "Issue"),
]

title = models.CharField(
Expand All @@ -47,7 +47,8 @@ class Announcement(models.Model):
null=True,
)
message = models.TextField()
announcement_type = models.IntegerField(
announcement_type = models.CharField(
max_length=20,
choices=ANNOUNCEMENT_CHOICES,
default=ANNOUNCEMENT_NOTICE,
)
Expand All @@ -56,4 +57,9 @@ class Announcement(models.Model):
end_time = models.DateTimeField(null=True, blank=True)

def __str__(self):
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}"
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}"""
3 changes: 2 additions & 1 deletion backend/announcements/permissions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from rest_framework import permissions


class IsSuperuser(permissions.BasePermission):
"""
Grants permission if the current user is a superuser.
Expand All @@ -9,4 +10,4 @@ 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
return request.user.is_authenticated and request.user.is_superuser
51 changes: 47 additions & 4 deletions backend/announcements/serializers.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,57 @@
from rest_framework import serializers
from announcements.models import Announcement, Audience
from rest_framework import serializers

class AudienceSerializer(serializers.ModelSerializer):

class AudienceSerializer(serializers.ModelSerializer):
class Meta:
model = Audience
fields = "__all__"
fields = ("name",)


class AnnouncementSerializer(serializers.ModelSerializer):
audiences = serializers.SlugRelatedField(
many=True, slug_field="name", queryset=Audience.objects.all()
)

class Meta:
model = Announcement
fields = "__all__"
fields = (
"id",
"title",
"message",
"announcement_type",
"release_time",
"end_time",
"audiences",
)

def to_representation(self, instance):
representation = super().to_representation(instance)
representation["audiences"] = [
audience.name for audience in instance.audiences.all()
]
return representation

def to_internal_value(self, data):
audiences = data.get("audiences")
if isinstance(audiences, list):
audience_objs = []
for audience_name in audiences:
audience = Audience.objects.filter(name=audience_name).first()
if audience:
audience_objs.append(audience)
data["audiences"] = audience_objs
return super().to_internal_value(data)

def create(self, validated_data):
audiences = validated_data.pop("audiences")
instance = Announcement.objects.create(**validated_data)
instance.audiences.set(audiences)
return instance

def update(self, instance, validated_data):
audiences = validated_data.pop("audiences", None)
super().update(instance, validated_data)
if audiences:
instance.audiences.set(audiences)
return instance
2 changes: 1 addition & 1 deletion backend/announcements/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.urls import path
from announcements.views import AnnouncementsViewSet
from rest_framework import routers


app_name = "announcements"
router = routers.SimpleRouter()
router.register("", AnnouncementsViewSet)
Expand Down
4 changes: 2 additions & 2 deletions backend/announcements/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from announcements.serializers import AnnouncementSerializer
from rest_framework import viewsets
from announcements.models import Announcement
from announcements.permissions import IsSuperuser
from announcements.serializers import AnnouncementSerializer
from rest_framework import viewsets


class AnnouncementsViewSet(viewsets.ModelViewSet):
Expand Down

0 comments on commit 7014b93

Please sign in to comment.