Skip to content

Commit 3ba9b2b

Browse files
Merge pull request #128 from UNLV-CS472-672/calendar_events_model
4th PR: Calendar Events Model
2 parents 1849da0 + 52bc446 commit 3ba9b2b

File tree

14 files changed

+196
-0
lines changed

14 files changed

+196
-0
lines changed

backend/apps/planner/__init__.py

Whitespace-only changes.

backend/apps/planner/admin.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from django.contrib import admin
2+
from .models import CalendarEvent
3+
4+
@admin.register(CalendarEvent)
5+
class CalendarEventAdmin(admin.ModelAdmin):
6+
list_display = ("title", "date", "start_time", "end_time", "event_type", "related_task")
7+

backend/apps/planner/apps.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from django.apps import AppConfig
2+
3+
class PlannerConfig(AppConfig):
4+
default_auto_field = 'django.db.models.BigAutoField'
5+
name = 'apps.planner'

backend/apps/planner/managers.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from django.db import models
2+
from django.apps import apps
3+
from django.utils import timezone
4+
5+
class CalendarEventManager(models.Manager):
6+
def upcoming_events(self):
7+
"""Get events that occur in the future."""
8+
return self.filter(date__gte=timezone.now().date())
9+
10+
def past_events(self):
11+
"""Get events that have already occurred."""
12+
return self.filter(date__lt=timezone.now().date())
13+
14+
def events_for_date(self, date):
15+
"""Get events for a specific date."""
16+
return self.filter(date=date)
17+
18+
def events_by_type(self, event_type):
19+
"""Get events by event type."""
20+
return self.filter(event_type=event_type)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Generated by Django 5.1.6 on 2025-04-06 22:53
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
initial = True
10+
11+
dependencies = [
12+
('lessons', '0006_assignment_upload_record'),
13+
]
14+
15+
operations = [
16+
migrations.CreateModel(
17+
name='CalendarEvent',
18+
fields=[
19+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20+
('title', models.CharField(max_length=63)),
21+
('date', models.DateField()),
22+
('start_time', models.TimeField()),
23+
('end_time', models.TimeField()),
24+
('description', models.TextField(blank=True, null=True)),
25+
('event_type', models.CharField(blank=True, choices=[('lesson', 'Lesson'), ('meeting', 'Meeting'), ('reminder', 'Reminder'), ('practice', 'Practice')], default='lesson', max_length=20, null=True)),
26+
('related_task', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='lessons.assignment')),
27+
],
28+
options={
29+
'verbose_name': 'Calendar Event',
30+
'verbose_name_plural': 'Calendar Events',
31+
},
32+
),
33+
]
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Generated by Django 5.1.6 on 2025-04-09 05:40
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('planner', '0001_initial'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='calendarevent',
15+
name='location',
16+
field=models.TextField(blank=True, null=True),
17+
),
18+
migrations.AddField(
19+
model_name='calendarevent',
20+
name='virtual',
21+
field=models.BooleanField(default=False),
22+
),
23+
migrations.AlterField(
24+
model_name='calendarevent',
25+
name='event_type',
26+
field=models.CharField(choices=[('lesson', 'Lesson'), ('meeting', 'Meeting'), ('reminder', 'Reminder'), ('practice', 'Practice')], default='lesson', max_length=20),
27+
),
28+
]

backend/apps/planner/migrations/__init__.py

Whitespace-only changes.

backend/apps/planner/models.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from django.db import models
2+
from django.utils import timezone
3+
from django.core.exceptions import ValidationError
4+
from apps.planner.managers import CalendarEventManager
5+
6+
class CalendarEvent(models.Model):
7+
# Constants for event type choices
8+
LESSON = 'lesson'
9+
MEETING = 'meeting'
10+
REMINDER = 'reminder'
11+
PRACTICE = 'practice'
12+
13+
EVENT_TYPES = [
14+
(LESSON, 'Lesson'),
15+
(MEETING, 'Meeting'),
16+
(REMINDER, 'Reminder'),
17+
(PRACTICE, 'Practice')
18+
]
19+
20+
# Calendar event fields
21+
title = models.CharField(max_length=63)
22+
date = models.DateField(blank=False, null=False)
23+
start_time = models.TimeField(blank=False, null=False)
24+
end_time = models.TimeField(blank=False, null=False)
25+
description = models.TextField(blank=True, null=True)
26+
# related_unscheduled_task = models.ForeignKey(UnscheduledTask, on_delete=models.CASCADE, blank=True, null=True)
27+
related_task = models.ForeignKey(
28+
'lessons.Assignment',
29+
on_delete=models.CASCADE,
30+
blank=True,
31+
null=True
32+
)
33+
event_type = models.CharField(
34+
max_length=20,
35+
choices=EVENT_TYPES,
36+
default=LESSON
37+
)
38+
location = models.TextField(blank=True, null=True)
39+
virtual = models.BooleanField(default=False)
40+
41+
objects = CalendarEventManager()
42+
43+
class Meta:
44+
# For human readability and clarity
45+
verbose_name = "Calendar Event"
46+
verbose_name_plural = "Calendar Events"
47+
48+
def __str__(self):
49+
# To assist with debuging
50+
return (
51+
f"{self.title} ({self.get_event_type_display()}) "
52+
f"on {self.date} from {self.start_time} to {self.end_time}"
53+
)
54+
55+
def clean(self):
56+
# Ensure that the start_time is before the end_time
57+
if self.start_time and self.end_time: # Not null
58+
if self.start_time >= self.end_time:
59+
raise ValidationError("End time must be after the start time.")
60+
# Ensure that both start and end time are provided
61+
elif self.start_time or self.end_time:
62+
raise ValidationError("Both start time and end time must be provided.")
63+
64+
# Ensure the event is not scheduled in the past
65+
if self.start_time and self.end_time: # not null
66+
if self.date < timezone.now().date():
67+
raise ValidationError("The event date cannot be in the past.")
68+
elif self.date == timezone.now().date() and self.start_time < timezone.now().time():
69+
raise ValidationError("The event start time cannot be in the past.")
70+
elif self.start_time or self.end_time:
71+
raise ValidationError("Both start time and end time must be provided.")
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from rest_framework import serializers
2+
from .models import CalendarEvent
3+
4+
class CalendarEventSerializer(serializers.ModelSerializer):
5+
class Meta:
6+
model = CalendarEvent
7+
fields = '__all__'
8+

backend/apps/planner/tests.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from django.test import TestCase
2+
3+
# Create your tests here.

0 commit comments

Comments
 (0)