diff --git a/api/views/default.py b/api/views/default.py index fce7f72db..ab25c48c4 100644 --- a/api/views/default.py +++ b/api/views/default.py @@ -381,6 +381,7 @@ def class_detail_list(request): "timeslot": clazz.timeslot, "code": clazz.code, "subject_abbr": clazz.subject.abbr, + "room": clazz.room.code if clazz.room else None, "csv_link": reverse("download_csv_per_class", kwargs={"class_id": clazz.pk}), "assignments": assignments, "quizzes": quizzes, diff --git a/common/admin.py b/common/admin.py index bbd219f59..668cb2598 100644 --- a/common/admin.py +++ b/common/admin.py @@ -1,3 +1,4 @@ +from django import forms from django.contrib import admin from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User @@ -138,6 +139,20 @@ class SubmitAdmin(admin.ModelAdmin): autocomplete_fields = ["assignment"] +class RoomForm(forms.ModelForm): + class Meta: + model = models.Room + fields = "__all__" + labels = { + "inbus_room_id": "INBUS room_id", + } + + +@admin.register(models.Room) +class RoomAdmin(admin.ModelAdmin): + form = RoomForm + + admin.site.register(models.Task, TaskAdmin) admin.site.register(models.Class, ClassAdmin) admin.site.register(models.Submit, SubmitAdmin) diff --git a/common/migrations/0031_room_class_room.py b/common/migrations/0031_room_class_room.py new file mode 100644 index 000000000..d55e154b3 --- /dev/null +++ b/common/migrations/0031_room_class_room.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.16 on 2025-12-09 12:45 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('common', '0030_llmreviewprompt_suggestedcomment'), + ] + + operations = [ + migrations.CreateModel( + name='Room', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('code', models.CharField(max_length=30)), + ('inbus_room_id', models.IntegerField()), + ('capacity', models.IntegerField()), + ], + ), + migrations.AddField( + model_name='class', + name='room', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.room'), + ), + ] diff --git a/common/models.py b/common/models.py index 1c525ffe1..d58b79545 100644 --- a/common/models.py +++ b/common/models.py @@ -126,6 +126,17 @@ def markdown(self): return "" +class Room(models.Model): + code = models.CharField( + max_length=30 + ) # This is actually a string rather than a strict number itself + inbus_room_id = models.IntegerField() + capacity = models.IntegerField() + + def __str__(self): + return f"{self.code} - {self.capacity} seats" + + class Class(models.Model): class Day(models.TextChoices): MONDAY = "PO", "Monday" @@ -153,6 +164,10 @@ class Day(models.TextChoices): day = models.CharField(max_length=5, choices=Day.choices) time = models.TimeField() + # Use SET_NULL so that if a room is deleted, the class remains but its room assignment is cleared. + # This is preferred over CASCADE or PROTECT since room assignments may change or be removed. + room = models.ForeignKey(Room, on_delete=models.SET_NULL, null=True, blank=True) + objects = ClassManager() def __str__(self): diff --git a/frontend/src/ClassDetail.svelte b/frontend/src/ClassDetail.svelte index b5218749c..be6b0f309 100644 --- a/frontend/src/ClassDetail.svelte +++ b/frontend/src/ClassDetail.svelte @@ -167,6 +167,7 @@ let showSummary = false;