Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions backend/apps/lessons/migrations/0008_assignment_reminder_sent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1.6 on 2025-04-20 04:48

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('lessons', '0006_assignment_upload_record'),
]

operations = [
migrations.AddField(
model_name='assignment',
name='reminder_sent',
field=models.BooleanField(default=False),
),
]
14 changes: 14 additions & 0 deletions backend/apps/lessons/migrations/0009_merge_20250419_2347.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Django 5.1.6 on 2025-04-20 06:47

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('lessons', '0007_alter_solution_choices_and_more'),
('lessons', '0008_assignment_reminder_sent'),
]

operations = [
]
2 changes: 2 additions & 0 deletions backend/apps/lessons/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class Assignment(models.Model): # Assignments: Represents assignments given to

deadline = models.DateTimeField(null=True, blank=True) # deadline is optional for extra credit/optional assignments

reminder_sent = models.BooleanField(default=False)

def __str__(self):
return self.title

Expand Down
3 changes: 2 additions & 1 deletion backend/apps/lessons/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class Meta:
"student",
"student_username",
"upload_record",
"upload_record_id"
"upload_record_id",
"reminder_sent"
]


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1.6 on 2025-04-20 03:22

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('notifications', '0003_alter_notification_info_category_and_more'),
]

operations = [
migrations.AddField(
model_name='notification',
name='priority',
field=models.CharField(choices=[('low', 'Low'), ('medium', 'Medium'), ('high', 'High')], default='medium', max_length=10),
),
]
14 changes: 14 additions & 0 deletions backend/apps/notifications/migrations/0006_merge_20250419_2347.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Django 5.1.6 on 2025-04-20 06:47

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('notifications', '0004_notification_priority'),
('notifications', '0005_notification_sender'),
]

operations = [
]
19 changes: 19 additions & 0 deletions backend/apps/notifications/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ class Notification(models.Model):
INFO_GENERAL = 'general'
INFO_SYSTEM = 'system'

# constants for priority types for scheduled notifications
LOW = 'low'
MEDIUM = 'medium'
HIGH = 'high'

# choices for the notification_type field. the elements in the list is
# a tuple of (value, display_name)
NOTIFICATION_TYPES = [
Expand All @@ -29,6 +34,13 @@ class Notification(models.Model):
(INFO_SYSTEM, 'System Update')
]

# choices for priority types
PRIORITY_CHOICES = [
(LOW, 'Low'),
(MEDIUM, 'Medium'),
(HIGH, 'High')
]

# links to the user who will receive notifications
# on_delete=CASCADE means that if the user is deleted, their notifs will be deleted too
# related_name='notifications' gets all notifs for a user
Expand Down Expand Up @@ -62,6 +74,13 @@ class Notification(models.Model):
null=True
)

# priority field
priority = models.CharField(
max_length=10,
choices=PRIORITY_CHOICES,
default=MEDIUM
)

# when notification was created
sent_at = models.DateTimeField(default=timezone.now)

Expand Down
4 changes: 1 addition & 3 deletions backend/apps/notifications/serializers.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
from rest_framework import serializers
from .models import Notification


# for serializing notification data to JSON
class NotificationSerializer(serializers.ModelSerializer):
sender_username = serializers.CharField(source='sender.username', read_only=True)

class Meta:
model = Notification
fields = [
Expand All @@ -18,6 +15,7 @@ class Meta:
'sent_at',
'scheduled_time',
'is_read',
'priority',
'sender_username'
]
read_only_fields = ['id', 'sent_at']
1 change: 1 addition & 0 deletions backend/apps/notifications/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from django.utils import timezone
from .models import Notification

# TODO: this is where I'll create the celery tasks for the session and assignment reminders (6th PR)

@shared_task
def process_scheduled_notifications():
Expand Down
9 changes: 3 additions & 6 deletions backend/apps/notifications/utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
from .models import Notification

'''Helper function(s) for notifications/models.py and notifications/tasks.py '''


def create_notification(user, title, message, notification_type='INFO', info_category=None, scheduled_time=None, sender=None):
def create_notification(user, title, message, notification_type='INFO', info_category=None, scheduled_time=None, priority='medium', sender=None):
"""
Args:
user: User object who will receive the notification
Expand All @@ -12,8 +9,8 @@ def create_notification(user, title, message, notification_type='INFO', info_cat
notification_type: Type of notification ('INFO', 'SUCCESS', 'WARNING', 'ERROR')
info_category: Category for INFO notifications
scheduled_time: When the notification should be sent (if scheduled)
priority: Priority level ('low', 'medium', 'high')
sender: User object who sent the notification (can be None for system notifications)

Returns:
Notification object
"""
Expand All @@ -24,9 +21,9 @@ def create_notification(user, title, message, notification_type='INFO', info_cat
notification_type=notification_type.lower(), # ensure lowercase to match model choices
info_category=info_category,
scheduled_time=scheduled_time,
priority=priority.lower(), # ensure lowercase to match model choices
sender=sender
)

return notification


Expand Down
4 changes: 4 additions & 0 deletions backend/apps/sessions/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from django.contrib import admin
from .models import Session

admin.site.register(Session)
7 changes: 7 additions & 0 deletions backend/apps/sessions/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.apps import AppConfig

class SessionsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.sessions'
label = 'custom_sessions'

38 changes: 38 additions & 0 deletions backend/apps/sessions/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 5.1.6 on 2025-04-20 04:51

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
('users', '0009_alter_tutorprofile_rating'),
]

operations = [
migrations.CreateModel(
name='Session',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=100)),
('description', models.TextField(blank=True, null=True)),
('start_time', models.DateTimeField()),
('end_time', models.DateTimeField()),
('location', models.CharField(blank=True, max_length=200, null=True)),
('is_virtual', models.BooleanField(default=True)),
('meeting_link', models.URLField(blank=True, null=True)),
('status', models.CharField(choices=[('scheduled', 'Scheduled'), ('completed', 'Completed'), ('canceled', 'Canceled')], default='scheduled', max_length=20)),
('reminder_sent', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('student', models.ForeignKey(limit_choices_to={'role': 3}, on_delete=django.db.models.deletion.CASCADE, related_name='student_sessions', to='users.profile')),
('tutor', models.ForeignKey(limit_choices_to={'role': 1}, on_delete=django.db.models.deletion.CASCADE, related_name='tutor_sessions', to='users.profile')),
],
options={
'ordering': ['-start_time'],
},
),
]
Empty file.
57 changes: 57 additions & 0 deletions backend/apps/sessions/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from django.db import models
from django.utils import timezone
from apps.users.models import Profile


class Session(models.Model):
# session participants
student = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='student_sessions',
limit_choices_to={'role': Profile.STUDENT})
tutor = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='tutor_sessions',
limit_choices_to={'role': Profile.TUTOR})

# session details
title = models.CharField(max_length=100)
description = models.TextField(blank=True, null=True)
start_time = models.DateTimeField()
end_time = models.DateTimeField()

# location details
location = models.CharField(max_length=200, blank=True, null=True)
is_virtual = models.BooleanField(default=True)
meeting_link = models.URLField(blank=True, null=True)

# session status
SCHEDULED = 'scheduled'
COMPLETED = 'completed'
CANCELED = 'canceled'

STATUS_CHOICES = [
(SCHEDULED, 'Scheduled'),
(COMPLETED, 'Completed'),
(CANCELED, 'Canceled')
]

status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=SCHEDULED)

# for reminders
reminder_sent = models.BooleanField(default=False)

# for tracking
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

def __str__(self):
return f"{self.title} - {self.start_time.strftime('%Y-%m-%d %H:%M')}"

def is_upcoming(self):
"""check if session is in the future"""
return self.start_time > timezone.now()

def duration_minutes(self):
"""calculate session duration in minutes"""
delta = self.end_time - self.start_time
return delta.total_seconds() // 60

class Meta:
ordering = ['-start_time']
1 change: 1 addition & 0 deletions backend/backend/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"apps.uploads", # our uploads app
"apps.search", # our search app
"apps.whiteboard", # our whiteboard app
"apps.sessions.apps.SessionsConfig", # our sessions app
"apps.planner", # our calendar app
"rest_framework", # rest framework
'rest_framework_simplejwt.token_blacklist', # for logout functionality
Expand Down