Repository: UNLV-CS472-672/2025-S-GROUP2-LessonConnect
Pull request #128: 4th PR: Calendar Events Model
Author: login: aviendha-andrus
Created: 2025-04-07T22:32:01Z | Merged: 2025-04-12T05:05:32Z

## **Calendar Events Model**  
_Added models and API endpoints for calendar events_

---

## **Overview**  
### **What does this PR do?**  
This PR introduces a fully functional Calendar model feature to LessonConnect, allowing users to create events. The calendar supports different types of events including lessons, meetingss, reminders, and practice/study sessions.

---

## **Key Features & Changes**  
### **What has been added or changed?**  

**Example:**  

- Introduced a Calendar events model that includes all neccisary components of an event including `title`, `date`, `start_time`, `end_time`, and `description`.
- Introduced a feature that allows events to be linked to assignments. 
- Insured workability using the admin panel to test the models and testing the endpoints using CURL

---

## **Why This Is Needed**  
### **What problem does this solve?**  
A flexible calendar is a neccisary component of LessonConnet, so that parents, students, and teachers can stay in the loop and on top of events and assignments. 

---

## **Related Issue**  
🔗 **This PR closes #100** 

---

## **Implementation Details**  
### **How was this feature developed?**  

- **Django REST Framework**: Implemented ViewSets for all CRUD operations with proper authentication and permissions

---

## **Design Documentation (If Applicable)**  
**Diagrams & Visuals**  
-  Rough Model Diagram 
	- https://drive.google.com/file/d/1xrgO12CFhIuQuG7R9_UsYOoXX24B8RNH/view?usp=sharing
<img width="315" alt="Screenshot 2025-04-11 at 9 31 46 PM" src="https://github.com/user-attachments/assets/b5b718ac-4256-45ab-b6e5-e9e2e155e4f7" />

---

## **How to Test This PR**  
### **Step-by-step testing guide**  
1️⃣ **Acquire an access token**: To do this, migrate and run the server like usual (python manage runserver). Then go to http://127.0.0.1:8000/api/token/ via the browser. Sign in with your superuser account info and copy your access token.  
2️⃣ Keep the server running in one terminal.  
3️⃣ Note this must be done in another separate (not in the virtual environment) terminal. Paste **access token** into curl commands that test lesson-related API.  
4️⃣ Fill in any desired API endpoint Fields, some being required while others not (and some being avoided more will be listed out in follow-up PR).  
5️⃣ Outputs will be in the second (not in the virtual environment) terminal.  


---

## **Files Added/Modified**  
📂 **List of important files changed in this PR**  

| **File Name**      | **Description** |
|--------------------|----------------|
| `models.py`    | Calendar related models |
| `serializers.py`    | REST API serializers |
| `views.py`        | (views) API endpoints |
| `urls.py`        | URL routing for API endpoints |
| `backend/urls.py`  | Added lessons URL routing for new endpoints|
| `admin.py`        | to use the admin panel |
| `managers.py`        | to implement endpoints |

---

## **AI Code Usage**  
🤖 **Was AI-generated code used in this PR?**  

- [x] Yes 
- [ ] No  

---

## **Checklist for Reviewers**  
**Things to check before approving this PR:**  
- [x] Does the feature work as expected?  
- [x] Are the UI and animations smooth?  
- [x] Do the session buttons and controls function properly?  
- [x] Is the code **clean, modular, and well-documented**?  
- [x] Does the feature work on **different screen sizes** (desktop, mobile, tablet)?  

---

## **Next Steps & Future Enhancements**  
### **What improvements can be made after merging?**  
- Implement tests for the models and endpoints in tests.py in a future PR

---

### **Final Notes**  
**Thank you for reviewing!** Please leave feedback if you have any suggestions or concerns. Let’s make this feature even better!  


Linked issue #100: Task: Calendar Events Model
### **Task: Calendar Events Model**

🚀 _Create a calendar events model that allows users to store, create, and manage calendar events linked to their tasks or assignments._
#### **Is your task related to existing implementation?**

🔹 _Yes. This issue is related to Issues #98 and #99 but not dependent. Please create a new app by going to backend/apps/ and doing `python manage.py startapp calendar` if one does not already exist._

#### **❗ Design Philosophy**
<details> <summary>Click for more details</summary>

🔸 _Fat Model, Skinny View Approach:_
-   **Fat Model**:
    -   The **model** in our application acts as the "brain" and is responsible for all business logic and operations. This includes tasks such as calculations, data transformations, and handling complex queries. All logic related to how data should behave (e.g., filtering, sorting, validations) should be placed here.
    - Initially, business logic is written directly in the model. But it should be later refactored (see section: Refactoring Business Logic Through Managers for more details).
-   **Skinny View**:
    -   The **view** is kept lightweight and focused on its main responsibility: handling the HTTP request and preparing the data for display. The view acts as the "muscle" that collects the required data from the model and returns it to the frontend (ReactJS)  via **REST API** endpoints.
    -   Views should **never** contain business logic or complex data manipulation. Instead, they should call predefined methods from the model to gather the necessary information and pass it to the frontend.
  
🔸 _Refactoring Business Logic Through Managers_

To ensure clean, maintainable code, we follow a process where we initially write business logic directly within the model.

1.  **Write Business Logic in the Model**:
    
    -   Start by writing the necessary logic directly in the model. This is where we define fields, methods, and the core business logic related to the model’s data.
    -   While this helps quickly implement functionality, it can cause models to become bloated if the logic is too complex.
2.  **Refactor into Managers**:
    
    -   Once the logic is functional, it’s refactored into **managers.py**. The manager organizes common queries and operations, providing reusable methods for interacting with the model’s data.
    -   Instead of having complex queries or business logic directly in the model, the manager handles the intricacies, making the model file more readable and focused on the data structure.
    - **model.py** uses the refactored logic from **managers.py**
3.  **Use Querysets for Complex Queries (Optional)**:
    
    -   For complex queries or reusable logic (such as filtering, ordering, and complex database joins), custom **querysets** are defined in **querysets.py**.
    -   This separation helps keep the model clean, while the manager and queryset handle the more intricate operations.
    
</details>

#### **Describe the solution you'd like**

✅ _Store events with attributes like title, date, start_time, end_time, description, and related_task:_

1.  **CalendarEvent**: Stores events with important details like the task it relates to.
    
    -   Example Fields: `title` (CharField), `date` (DateField), `start_time` (TimeField), `end_time` (TimeField), `description` (TextField), `related_task` (ForeignKey to `UnscheduledTask` or `Assignment`, nullable), `event_type` (choices)
        

✅ _API endpoints to create, update, and delete calendar events:_

1.  **POST /api/calendar-events/**: Create a new calendar event.
    
2.  **PATCH /api/calendar-events/{id}/**: Update an existing calendar event.
    
3.  **DELETE /api/calendar-events/{id}/**: Delete a calendar event.
    

#### **Tasks Breakdown**

📋 _If this feature involves multiple steps, list them here:_

-   Step 1
    
    -   Create the `CalendarEvent` model with required fields.
        
-   Step 2
    
    -   Create API views to handle calendar event creation, updating, and deletion.
        
-   Step 3
    
    -   Implement and test the API endpoints for managing calendar events.
        
-   Step 4
    
    -   Write tests to ensure calendar events are properly created, updated, and deleted.
        
#### **Additional Context**

📌 _Include relevant information such as:_

-  Rough Model Diagram (Not 100% accurate but provides general idea)
	- https://drive.google.com/file/d/1xrgO12CFhIuQuG7R9_UsYOoXX24B8RNH/view?usp=sharing

🔗 _Links to related documentation, GitHub discussions, or external references._
- Django Documentation for Models
  - https://docs.djangoproject.com/en/5.1/topics/db/models/
- Example of API Endpoints Setup (following our design philosophy)
	- https://docs.google.com/document/d/1vu4TH9xP4gTEoAWPA_vYh6cCqVa55taehhVv-di8bqw/edit?usp=sharing
- Django Documentation for Handling HTTP Responses
	- https://docs.djangoproject.com/en/5.1/topics/http/
- Example of API Endpoints being set up
	- https://medium.com/@umarsunny2011/integrate-reactjs-and-django-using-django-rest-framework-and-axios-166223e0860b
- Overview of CRUD
	- https://www.crowdstrike.com/en-us/cybersecurity-101/next-gen-siem/crud-vs-rest/

#### **Priority & Impact**

⚡ _How urgent or impactful is this feature?_

-   High
Linked issue comments: user: login: aviendha-andrus
created_at: 2025-04-12T04:38:01Z
body: Hi @ashley-arellano ! I’ve assigned myself this issue awhile ago and as an update I completed my 4th PR based around this issue! I’m thinking it would be best to make a testing issue around this so I’ll go ahead and do that as my next step. What do you think about that?
author_association: CONTRIBUTOR
user: login: ashley-arellano
created_at: 2025-04-12T05:34:31Z
body: @aviendha-andrus Yes, that would be a great idea! We need to keep our test coverage 90% and up so that will help alot with our project : D
author_association: CONTRIBUTOR

Diff:
diff --git a/backend/apps/planner/__init__.py b/backend/apps/planner/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/backend/apps/planner/admin.py b/backend/apps/planner/admin.py
new file mode 100644
index 00000000..a779abff
--- /dev/null
+++ b/backend/apps/planner/admin.py
@@ -0,0 +1,7 @@
+from django.contrib import admin
+from .models import CalendarEvent
+
+@admin.register(CalendarEvent)
+class CalendarEventAdmin(admin.ModelAdmin):
+    list_display = ("title", "date", "start_time", "end_time", "event_type", "related_task")
+    
\ No newline at end of file
diff --git a/backend/apps/planner/apps.py b/backend/apps/planner/apps.py
new file mode 100644
index 00000000..4b36edee
--- /dev/null
+++ b/backend/apps/planner/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+class PlannerConfig(AppConfig):
+    default_auto_field = 'django.db.models.BigAutoField'
+    name = 'apps.planner'
diff --git a/backend/apps/planner/managers.py b/backend/apps/planner/managers.py
new file mode 100644
index 00000000..cd514bce
--- /dev/null
+++ b/backend/apps/planner/managers.py
@@ -0,0 +1,20 @@
+from django.db import models
+from django.apps import apps
+from django.utils import timezone
+
+class CalendarEventManager(models.Manager):
+    def upcoming_events(self):
+        """Get events that occur in the future."""
+        return self.filter(date__gte=timezone.now().date())
+
+    def past_events(self):
+        """Get events that have already occurred."""
+        return self.filter(date__lt=timezone.now().date())
+
+    def events_for_date(self, date):
+        """Get events for a specific date."""
+        return self.filter(date=date)
+
+    def events_by_type(self, event_type):
+        """Get events by event type."""
+        return self.filter(event_type=event_type)
\ No newline at end of file
diff --git a/backend/apps/planner/migrations/0001_initial.py b/backend/apps/planner/migrations/0001_initial.py
new file mode 100644
index 00000000..1ab4ea2a
--- /dev/null
+++ b/backend/apps/planner/migrations/0001_initial.py
@@ -0,0 +1,33 @@
+# Generated by Django 5.1.6 on 2025-04-06 22:53
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('lessons', '0006_assignment_upload_record'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='CalendarEvent',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(max_length=63)),
+                ('date', models.DateField()),
+                ('start_time', models.TimeField()),
+                ('end_time', models.TimeField()),
+                ('description', models.TextField(blank=True, null=True)),
+                ('event_type', models.CharField(blank=True, choices=[('lesson', 'Lesson'), ('meeting', 'Meeting'), ('reminder', 'Reminder'), ('practice', 'Practice')], default='lesson', max_length=20, null=True)),
+                ('related_task', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='lessons.assignment')),
+            ],
+            options={
+                'verbose_name': 'Calendar Event',
+                'verbose_name_plural': 'Calendar Events',
+            },
+        ),
+    ]
diff --git a/backend/apps/planner/migrations/0002_calendarevent_location_calendarevent_virtual_and_more.py b/backend/apps/planner/migrations/0002_calendarevent_location_calendarevent_virtual_and_more.py
new file mode 100644
index 00000000..87454a51
--- /dev/null
+++ b/backend/apps/planner/migrations/0002_calendarevent_location_calendarevent_virtual_and_more.py
@@ -0,0 +1,28 @@
+# Generated by Django 5.1.6 on 2025-04-09 05:40
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('planner', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='calendarevent',
+            name='location',
+            field=models.TextField(blank=True, null=True),
+        ),
+        migrations.AddField(
+            model_name='calendarevent',
+            name='virtual',
+            field=models.BooleanField(default=False),
+        ),
+        migrations.AlterField(
+            model_name='calendarevent',
+            name='event_type',
+            field=models.CharField(choices=[('lesson', 'Lesson'), ('meeting', 'Meeting'), ('reminder', 'Reminder'), ('practice', 'Practice')], default='lesson', max_length=20),
+        ),
+    ]
diff --git a/backend/apps/planner/migrations/__init__.py b/backend/apps/planner/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/backend/apps/planner/models.py b/backend/apps/planner/models.py
new file mode 100644
index 00000000..947801ec
--- /dev/null
+++ b/backend/apps/planner/models.py
@@ -0,0 +1,71 @@
+from django.db import models
+from django.utils import timezone
+from django.core.exceptions import ValidationError
+from apps.planner.managers import CalendarEventManager
+
+class CalendarEvent(models.Model):
+    # Constants for event type choices
+    LESSON = 'lesson'
+    MEETING = 'meeting'
+    REMINDER = 'reminder'
+    PRACTICE = 'practice'
+
+    EVENT_TYPES = [
+        (LESSON, 'Lesson'),
+        (MEETING, 'Meeting'),
+        (REMINDER, 'Reminder'),
+        (PRACTICE, 'Practice')
+    ]
+
+    # Calendar event fields 
+    title = models.CharField(max_length=63)
+    date = models.DateField(blank=False, null=False)
+    start_time = models.TimeField(blank=False, null=False)
+    end_time = models.TimeField(blank=False, null=False)
+    description = models.TextField(blank=True, null=True) 
+    # related_unscheduled_task = models.ForeignKey(UnscheduledTask, on_delete=models.CASCADE, blank=True, null=True)
+    related_task = models.ForeignKey(
+        'lessons.Assignment', 
+        on_delete=models.CASCADE, 
+        blank=True,
+        null=True
+    )
+    event_type = models.CharField(
+        max_length=20,
+        choices=EVENT_TYPES,
+        default=LESSON
+    )
+    location = models.TextField(blank=True, null=True) 
+    virtual = models.BooleanField(default=False)
+
+    objects = CalendarEventManager()
+
+    class Meta:
+        # For human readability and clarity
+        verbose_name = "Calendar Event"
+        verbose_name_plural = "Calendar Events"
+    
+    def __str__(self):
+        # To assist with debuging 
+        return (
+            f"{self.title} ({self.get_event_type_display()}) "
+            f"on {self.date} from {self.start_time} to {self.end_time}"
+        )
+
+    def clean(self):
+        # Ensure that the start_time is before the end_time
+        if self.start_time and self.end_time: # Not null 
+            if self.start_time >= self.end_time:
+                raise ValidationError("End time must be after the start time.")
+        # Ensure that both start and end time are provided
+        elif self.start_time or self.end_time:
+            raise ValidationError("Both start time and end time must be provided.")
+        
+        # Ensure the event is not scheduled in the past
+        if self.start_time and self.end_time: # not null 
+            if self.date < timezone.now().date():
+                raise ValidationError("The event date cannot be in the past.")
+            elif self.date == timezone.now().date() and self.start_time < timezone.now().time():
+                raise ValidationError("The event start time cannot be in the past.")
+        elif self.start_time or self.end_time:
+            raise ValidationError("Both start time and end time must be provided.")
\ No newline at end of file
diff --git a/backend/apps/planner/serializers.py b/backend/apps/planner/serializers.py
new file mode 100644
index 00000000..b2e8dd94
--- /dev/null
+++ b/backend/apps/planner/serializers.py
@@ -0,0 +1,8 @@
+from rest_framework import serializers
+from .models import CalendarEvent
+
+class CalendarEventSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = CalendarEvent
+        fields = '__all__'
+
diff --git a/backend/apps/planner/tests.py b/backend/apps/planner/tests.py
new file mode 100644
index 00000000..7ce503c2
--- /dev/null
+++ b/backend/apps/planner/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/backend/apps/planner/urls.py b/backend/apps/planner/urls.py
new file mode 100644
index 00000000..cac76adc
--- /dev/null
+++ b/backend/apps/planner/urls.py
@@ -0,0 +1,11 @@
+from django.urls import path, include
+from rest_framework.routers import DefaultRouter
+from .views import CalendarEventViewSet
+
+# Create a router and register viewsets
+router = DefaultRouter()
+router.register(r'calendar', CalendarEventViewSet)
+
+urlpatterns = [
+    path('', include(router.urls)),
+]
diff --git a/backend/apps/planner/views.py b/backend/apps/planner/views.py
new file mode 100644
index 00000000..569bcf8b
--- /dev/null
+++ b/backend/apps/planner/views.py
@@ -0,0 +1,8 @@
+from django.shortcuts import render
+from rest_framework import viewsets
+from .models import CalendarEvent
+from .serializers import CalendarEventSerializer
+
+class CalendarEventViewSet(viewsets.ModelViewSet):
+    queryset = CalendarEvent.objects.all()
+    serializer_class = CalendarEventSerializer
\ No newline at end of file
diff --git a/backend/backend/settings.py b/backend/backend/settings.py
index 5da2ecec..619f944e 100644
--- a/backend/backend/settings.py
+++ b/backend/backend/settings.py
@@ -72,6 +72,7 @@
     "apps.uploads",       # our uploads app
     "apps.search",        # our search app
     "apps.whiteboard",      # our whiteboard app
+    "apps.planner",       # our calendar app
     "rest_framework",     # rest framework
     'rest_framework_simplejwt.token_blacklist', # for logout functionality
     "channels",           # Django channels
diff --git a/backend/backend/urls.py b/backend/backend/urls.py
index d552fe07..74249194 100644
--- a/backend/backend/urls.py
+++ b/backend/backend/urls.py
@@ -31,5 +31,6 @@
     path('lessons/', include('apps.lessons.urls')),
     path('submissions/', include('apps.submissions.urls')),
     path('pomodoro/', include('apps.pomodoro.urls')),
+    path('planner/', include('apps.planner.urls')),
 ]
 


Reviews:
user: login: ChrisCRL
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T03:21:55Z
user: login: ChrisCRL
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T03:29:53Z
user: login: ChrisCRL
body: ### 🔍 Peer Review: Calendar Event Model Implementation
Hey Aviendha👋 — just finished reviewing your PR. Here's my feedback:
---
## ✅ What Looks Great
- The model structure has been implemented cleanly.
- Your event type choices are well-organized using class constants.
---
## 💡 Suggestions for Improvement (if any)
- Consider adding date validation in the clean method to make sure events aren't scheduled in the past.
-  Maybe add a location field. And a field to see if it's a virtual meeting or not.
- For the `event_type` field, consider whether it should really allow null/blank values when it has a default.
---
## 🧪 Tested This Feature
I ran the following test steps:
- ✅ Verified model structure and field definitions.
- ✅ Confirmed relationship with Assignment model.
---
### 🚀 Final Thoughts
Besides the suggestions I made, lgtm 👍 approved :)
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T03:32:39Z
user: login: aviendha-andrus
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T03:40:31Z
user: login: aviendha-andrus
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T03:40:48Z
user: login: ChrisCRL
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T06:37:38Z
user: login: omgdory
body: ## ✅ Approval Overview
Managers added and serve to be really useful. Migrations created. Models make sense, serializers added for data formatting, and URL successfully handled. Views are very succinct and utilize pre-existing Django viewsets, particularly ModelViewSet, meaning that functionality aligns with existing Django methods and documentation. Settings updated to register this as an app. Overall, looks good! All that remains are tests.

## 🧠 Details
The introduction of the planner application, along with its models and associated views, enriches the functionality of the LessonConnect. The models are well-structured, capturing essential data (titles, dates, times, etc.). This lays the groundwork for a robust event management system within the application.

The addition of CRUD API endpoints demonstrates a clear understanding of RESTful principles and enhances the application's interactivity.

The implementation of features such as virtual event handling and the inclusion of a clean method to validate event data helps ensure data integrity. These features improve the user experience and contribute to the maintainability of the source code.

## 🎯 Next Steps
Although manual checking via the admin panel and using CURL work, it is important to add automatic tests to ensure that the CI/CD pipeline updates relative to these additional models. As discussed, these tests can be created in a separate PR. As it stands, this looks good to merge, and pre-existing functionality does not seem to have been affected. Great work!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T19:48:11Z
user: login: ChrisCRL
body: Thanks for adding my suggestions! Everything looks good to me. Approved 👍 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-12T00:13:48Z

Review comments:
diff_hunk: @@ -0,0 +1,62 @@
+from django.db import models
+from django.utils import timezone
+from django.core.exceptions import ValidationError
+from apps.planner.managers import CalendarEventManager
+
+class CalendarEvent(models.Model):
+    # Constants for event type choices
+    LESSON = 'lesson'
+    MEETING = 'meeting'
+    REMINDER = 'reminder'
+    PRACTICE = 'practice'
+
+    EVENT_TYPES = [
+        (LESSON, 'Lesson'),
+        (MEETING, 'Meeting'),
+        (REMINDER, 'Reminder'),
+        (PRACTICE, 'Practice')
+    ]
+
+    # Calendar event fields 
path: backend/apps/planner/models.py
user: login: ChrisCRL
body: Maybe we can add a location field too? And a field to see if it's a virtual meeting or not.
created_at: 2025-04-08T03:21:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,62 @@
+from django.db import models
+from django.utils import timezone
+from django.core.exceptions import ValidationError
+from apps.planner.managers import CalendarEventManager
+
+class CalendarEvent(models.Model):
+    # Constants for event type choices
+    LESSON = 'lesson'
+    MEETING = 'meeting'
+    REMINDER = 'reminder'
+    PRACTICE = 'practice'
+
+    EVENT_TYPES = [
+        (LESSON, 'Lesson'),
+        (MEETING, 'Meeting'),
+        (REMINDER, 'Reminder'),
+        (PRACTICE, 'Practice')
+    ]
+
+    # Calendar event fields 
+    title = models.CharField(max_length=63)
+    date = models.DateField(blank=False, null=False)
+    start_time = models.TimeField(blank=False, null=False)
+    end_time = models.TimeField(blank=False, null=False)
+    description = models.TextField(blank=True, null=True) 
+    # related_unscheduled_task = models.ForeignKey(UnscheduledTask, on_delete=models.CASCADE, blank=True, null=True)
+    related_task = models.ForeignKey(
+        'lessons.Assignment', 
+        on_delete=models.CASCADE, 
+        blank=True,
+        null=True
+    )
+    event_type = models.CharField(
+        max_length=20,
+        choices=EVENT_TYPES,
+        default=LESSON, 
+        blank=True,
+        null=True, 
+    )
+
+    objects = CalendarEventManager()
+
+    class Meta:
+        # For human readability and clarity
+        verbose_name = "Calendar Event"
+        verbose_name_plural = "Calendar Events"
+    
+    def __str__(self):
+        # To assist with debuging 
+        return (
+            f"{self.title} ({self.get_event_type_display()}) "
+            f"on {self.date} from {self.start_time} to {self.end_time}"
+        )
+
+    def clean(self):
path: backend/apps/planner/models.py
user: login: ChrisCRL
body: Consider adding date validation in the clean method to make sure events aren't scheduled in the past?
created_at: 2025-04-08T03:29:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,62 @@
+from django.db import models
+from django.utils import timezone
+from django.core.exceptions import ValidationError
+from apps.planner.managers import CalendarEventManager
+
+class CalendarEvent(models.Model):
+    # Constants for event type choices
+    LESSON = 'lesson'
+    MEETING = 'meeting'
+    REMINDER = 'reminder'
+    PRACTICE = 'practice'
+
+    EVENT_TYPES = [
+        (LESSON, 'Lesson'),
+        (MEETING, 'Meeting'),
+        (REMINDER, 'Reminder'),
+        (PRACTICE, 'Practice')
+    ]
+
+    # Calendar event fields 
+    title = models.CharField(max_length=63)
+    date = models.DateField(blank=False, null=False)
+    start_time = models.TimeField(blank=False, null=False)
+    end_time = models.TimeField(blank=False, null=False)
+    description = models.TextField(blank=True, null=True) 
+    # related_unscheduled_task = models.ForeignKey(UnscheduledTask, on_delete=models.CASCADE, blank=True, null=True)
+    related_task = models.ForeignKey(
+        'lessons.Assignment', 
+        on_delete=models.CASCADE, 
+        blank=True,
+        null=True
+    )
+    event_type = models.CharField(
+        max_length=20,
+        choices=EVENT_TYPES,
+        default=LESSON, 
+        blank=True,
+        null=True, 
+    )
+
+    objects = CalendarEventManager()
+
+    class Meta:
+        # For human readability and clarity
+        verbose_name = "Calendar Event"
+        verbose_name_plural = "Calendar Events"
+    
+    def __str__(self):
+        # To assist with debuging 
+        return (
+            f"{self.title} ({self.get_event_type_display()}) "
+            f"on {self.date} from {self.start_time} to {self.end_time}"
+        )
+
+    def clean(self):
path: backend/apps/planner/models.py
user: login: aviendha-andrus
body: I’m glad you said this! I was thinking of doing this but then realized I often make calendar events for myself set in the past as a reminder/note. In your opinion should I add this method? 
created_at: 2025-04-08T03:40:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,62 @@
+from django.db import models
+from django.utils import timezone
+from django.core.exceptions import ValidationError
+from apps.planner.managers import CalendarEventManager
+
+class CalendarEvent(models.Model):
+    # Constants for event type choices
+    LESSON = 'lesson'
+    MEETING = 'meeting'
+    REMINDER = 'reminder'
+    PRACTICE = 'practice'
+
+    EVENT_TYPES = [
+        (LESSON, 'Lesson'),
+        (MEETING, 'Meeting'),
+        (REMINDER, 'Reminder'),
+        (PRACTICE, 'Practice')
+    ]
+
+    # Calendar event fields 
path: backend/apps/planner/models.py
user: login: aviendha-andrus
body: I like this idea! Will do.
created_at: 2025-04-08T03:40:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,62 @@
+from django.db import models
+from django.utils import timezone
+from django.core.exceptions import ValidationError
+from apps.planner.managers import CalendarEventManager
+
+class CalendarEvent(models.Model):
+    # Constants for event type choices
+    LESSON = 'lesson'
+    MEETING = 'meeting'
+    REMINDER = 'reminder'
+    PRACTICE = 'practice'
+
+    EVENT_TYPES = [
+        (LESSON, 'Lesson'),
+        (MEETING, 'Meeting'),
+        (REMINDER, 'Reminder'),
+        (PRACTICE, 'Practice')
+    ]
+
+    # Calendar event fields 
+    title = models.CharField(max_length=63)
+    date = models.DateField(blank=False, null=False)
+    start_time = models.TimeField(blank=False, null=False)
+    end_time = models.TimeField(blank=False, null=False)
+    description = models.TextField(blank=True, null=True) 
+    # related_unscheduled_task = models.ForeignKey(UnscheduledTask, on_delete=models.CASCADE, blank=True, null=True)
+    related_task = models.ForeignKey(
+        'lessons.Assignment', 
+        on_delete=models.CASCADE, 
+        blank=True,
+        null=True
+    )
+    event_type = models.CharField(
+        max_length=20,
+        choices=EVENT_TYPES,
+        default=LESSON, 
+        blank=True,
+        null=True, 
+    )
+
+    objects = CalendarEventManager()
+
+    class Meta:
+        # For human readability and clarity
+        verbose_name = "Calendar Event"
+        verbose_name_plural = "Calendar Events"
+    
+    def __str__(self):
+        # To assist with debuging 
+        return (
+            f"{self.title} ({self.get_event_type_display()}) "
+            f"on {self.date} from {self.start_time} to {self.end_time}"
+        )
+
+    def clean(self):
path: backend/apps/planner/models.py
user: login: ChrisCRL
body: hmmmm true. But I am working on an issue that deals with reminders at the moment. So, in my opinion, this check should be included in the method.
created_at: 2025-04-08T06:37:38Z
author_association: CONTRIBUTOR

PR comments:
user: login: aviendha-andrus
created_at: 2025-04-09T06:33:55Z
body: Thank you for your input! I added the clean method, and the location and virtual fields as suggested. 
author_association: CONTRIBUTOR
user: login: FrankFurter123
created_at: 2025-04-12T01:35:38Z
body: Hey @aviendha-andrus 

Great work, just what we needed for our calendar to function properly! I know Abdul has already designed a good-looking calendar on the frontend, and I love the fact that you’ve provided a clear step-by-step guide on how test your work. It’s super helpful for integration.

I also appreciate that @AbdulAlharbi has already structured his code to expect where this should connect, so no need to worry about the design being affected. When it’s time to hook everything up, this should align perfectly with our MVP goals and be a strong addition for the presentation.

Amazing job, @aviendha-andrus ! If you plan on connecting this to the backend in your upcoming PRs, make sure to reach out to @AbdulAlharbi —he can definitely help smooth out the integration process. 

- Franklin
author_association: CONTRIBUTOR
user: login: AbdulAlharbi
created_at: 2025-04-12T02:11:54Z
body: Hey @aviendha-andrus 

Just wanted to say how proud I am of the work you’ve done here. This backend implementation is exactly the kind of thoughtful, flexible design we need for LessonConnect to grow into a truly functional platform. You’ve covered both the essentials and future considerations beautifully.

I especially appreciate how clearly you outlined the setup steps and testing approach—it’s going to make front-end integration smooth sailing. As _PM_, it's always a joy to see this level of clarity, coordination, and quality in our core features. This lays a rock-solid foundation for what’s coming next.

Excited to see this come together with the frontend!  Amazing job!

— _Abdul_
author_association: CONTRIBUTOR
user: login: ashley-arellano
created_at: 2025-04-12T02:28:13Z
body: Hi @aviendha-andrus

This is a great addition to LessonConnect. Thank you for completing your task and getting this implementation done : D

This model is very important towards making progress on the calendar functionality. The instructions you provided on how to test your model is clear and easy to follow. Furthermore, I love how well you documented everything about your implementation so that in the future it'll be easy for us to use in case we want to add more to it.

Looking forward to seeing how this continues to evolve, especially once tests are added in the follow-up. Great work!!
author_association: CONTRIBUTOR
user: login: aviendha-andrus
created_at: 2025-04-12T03:09:13Z
body: > Hey @aviendha-andrus
> 
> Great work, just what we needed for our calendar to function properly! I know Abdul has already designed a good-looking calendar on the frontend, and I love the fact that you’ve provided a clear step-by-step guide on how test your work. It’s super helpful for integration.
> 
> I also appreciate that @AbdulAlharbi has already structured his code to expect where this should connect, so no need to worry about the design being affected. When it’s time to hook everything up, this should align perfectly with our MVP goals and be a strong addition for the presentation.
> 
> Amazing job, @aviendha-andrus ! If you plan on connecting this to the backend in your upcoming PRs, make sure to reach out to @AbdulAlharbi —he can definitely help smooth out the integration process.
> 
> * Franklin

Hi @FrankFurter123! 
Thank you for looking over this! I'm definitely planning to connect this to the backend in my upcoming PRs, and I’ll be sure to reach out to @AbdulAlharbi to help with the integration. I'm really happy to be working on something so fundamental to LessonConnect, and it's exciting to see how it’s all coming together. Looking forward to contributing more as we keep building toward the MVP!
author_association: CONTRIBUTOR
user: login: aviendha-andrus
created_at: 2025-04-12T03:12:23Z
body: > Hey @aviendha-andrus
> 
> Just wanted to say how proud I am of the work you’ve done here. This backend implementation is exactly the kind of thoughtful, flexible design we need for LessonConnect to grow into a truly functional platform. You’ve covered both the essentials and future considerations beautifully.
> 
> I especially appreciate how clearly you outlined the setup steps and testing approach—it’s going to make front-end integration smooth sailing. As _PM_, it's always a joy to see this level of clarity, coordination, and quality in our core features. This lays a rock-solid foundation for what’s coming next.
> 
> Excited to see this come together with the frontend! Amazing job!
> 
> — _Abdul_

Hi @AbdulAlharbi 
Thank you so much! I’m really grateful for your implementation of the frontend calendar, it looks really good! I’m looking forward to seeing this feature come together through integration. I’ll definitely reach out as I get started on hooking things up. It’s awesome to be working on something so central to LessonConnect’s purpose. Super excited for what’s ahead!
author_association: CONTRIBUTOR
user: login: aviendha-andrus
created_at: 2025-04-12T03:14:42Z
body: > Hi @aviendha-andrus
> 
> This is a great addition to LessonConnect. Thank you for completing your task and getting this implementation done : D
> 
> This model is very important towards making progress on the calendar functionality. The instructions you provided on how to test your model is clear and easy to follow. Furthermore, I love how well you documented everything about your implementation so that in the future it'll be easy for us to use in case we want to add more to it.
> 
> Looking forward to seeing how this continues to evolve, especially once tests are added in the follow-up. Great work!!

Hi @ashley-arellano, 
Thank you for creating the issue that this PR was based off of! I’m happy that this PR fits in with the vision you had for this issue. The feedback means a lot, especially considering how fundamental the calendar feature is and how many other people will be working on this feature. I’m glad to see that my code is readable for my teammates. 
author_association: CONTRIBUTOR
user: login: EthanZ23
created_at: 2025-04-12T04:55:57Z
body: Hello @aviendha-andrus,

Since I'm currently working on two calendar-related tasks myself, I was eager to familiarize myself early with your work. Also, I am more than glad to read through this PR. After reviewing the thoughtful discussions you've had with Chris and Dorian, and closely examining your calendar model, I wanted to highlight and express my appreciation!

---

**Initial Impression:**
I genuinely appreciate how organized and thorough your implementation is, especially how clearly you've structured the Calendar events model. The integration with assignments is particularly beneficial and practical for our calendar app functionality.

---

Great job, and I'll definitely reach out if I need any advice or further clarification while developing my own calendar features. Keep up the awesome work!

— Ethan
author_association: CONTRIBUTOR
user: login: aviendha-andrus
created_at: 2025-04-12T05:04:46Z
body: > Hello @aviendha-andrus,
> 
> Since I'm currently working on two calendar-related tasks myself, I was eager to familiarize myself early with your work. Also, I am more than glad to read through this PR. After reviewing the thoughtful discussions you've had with Chris and Dorian, and closely examining your calendar model, I wanted to highlight and express my appreciation!
> 
> **Initial Impression:** I genuinely appreciate how organized and thorough your implementation is, especially how clearly you've structured the Calendar events model. The integration with assignments is particularly beneficial and practical for our calendar app functionality.
> 
> Great job, and I'll definitely reach out if I need any advice or further clarification while developing my own calendar features. Keep up the awesome work!
> 
> — Ethan

I’m so glad to hear your feedback in this! Because we are working on the same topic its important to keep each other updated so I’m grateful you saw my work and like what you see. I’ll keep an eye out for your work too and make sure I stay updated with it. 
author_association: CONTRIBUTOR