add models

This commit is contained in:
imperosol committed 2026-10-02 15:35:29 +02:00
1 parent f211d0ff85
commit 7044a06546
6 files changed
+267 -5

No files matched your search

@@ -0,0 +1,22 @@
# Generated by Django 5.2.17 on 2026-09-24 14:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("core", "0050_alter_sithfile_moderator")]
operations = [
migrations.AddField(
model_name="preferences",
name="show_my_timetable",
field=models.BooleanField(
default=True,
help_text=(
"Allow subscribers to see your timetable and "
"to compare it to theirs (if you have filled it)"
),
verbose_name="show your timetable to others",
),
),
]
+8
View File
@@ -799,6 +799,14 @@ class Preferences(models.Model):
),
default=False,
)
show_my_timetable = models.BooleanField(
_("show your timetable to others"),
help_text=_(
"Allow subscribers to see your timetable and "
"to compare it to theirs (if you have filled it)"
),
default=True,
)
notify_on_click = models.BooleanField(
_("get a notification for every click"), default=False
)
+19 -5
View File
@@ -2,6 +2,7 @@ import type TomSelect from "tom-select";
import type { TomOption } from "tom-select/src/types";
import type { UserAjaxSelect } from "#core:core/components/ajax-select-index";
import { paginated } from "#core:utils/api";
import { History } from "#core:utils/history";
import {
type IdentifiedUserSchema,
type ModerationRequestSchema,
@@ -227,6 +228,11 @@ document.addEventListener("alpine:init", () => {
* Error message when a moderation operation fails
**/
moderationError: "",
/**
* Method of pushing new url to the browser history
* Used by popstate event and always reset to it's default value when used
**/
pushstate: History.Push,
async init() {
this.pictures = (
@@ -261,10 +267,12 @@ document.addEventListener("alpine:init", () => {
if (!event.state || event.state.sasPictureId === undefined) {
return;
}
this.pushstate = History.Replace;
this.currentPicture = this.pictures.find(
(i: PictureSchema) => i.id === Number.parseInt(event.state.sasPictureId, 10),
) as PictureWithIdentifications;
});
this.pushstate = History.Replace; /* Avoid first url push */
await this.updatePicture();
},
@@ -277,11 +285,17 @@ document.addEventListener("alpine:init", () => {
* the list of identified users are updated.
*/
async updatePicture(): Promise<void> {
window.history.replaceState(
{ sasPictureId: this.currentPicture.id },
"",
this.currentPicture.sas_url,
);
const updateArgs = {
data: { sasPictureId: this.currentPicture.id },
unused: "",
url: this.currentPicture.sas_url,
};
if (this.pushstate === History.Replace) {
window.history.replaceState(updateArgs.data, updateArgs.unused, updateArgs.url);
this.pushstate = History.Push;
} else {
window.history.pushState(updateArgs.data, updateArgs.unused, updateArgs.url);
}
this.moderationError = "";
const index: number = this.pictures.indexOf(this.currentPicture);
+16
View File
@@ -1 +1,17 @@
# Register your models here.
from django.contrib import admin
from timetable.models import Timetable, TimetableSlot
class TimetableSlotInline(admin.TabularInline):
model = TimetableSlot
@admin.register(Timetable)
class TimetableAdmin(admin.ModelAdmin):
list_display = ("user", "semester")
search_fields = ("user__nick_name", "user__first_name", "user__last_name")
autocomplete_fields = ("user",)
inlines = (TimetableSlotInline,)
list_select_related = ("user",)
+121
View File
@@ -0,0 +1,121 @@
# Generated by Django 5.2.17 on 2026-09-24 21:03
import django.core.validators
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
import core.utils
class Migration(migrations.Migration):
initial = True
dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]
operations = [
migrations.CreateModel(
name="Timetable",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"semester",
models.CharField(
blank=True,
default=core.utils.get_semester_code,
validators=[
django.core.validators.RegexValidator("(A|P)\\d{2}")
],
verbose_name="semester",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
verbose_name="user",
),
),
],
options={"verbose_name": "timetable", "verbose_name_plural": "timetables"},
),
migrations.CreateModel(
name="TimetableSlot",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"weekday",
models.SmallIntegerField(
choices=[
(1, "Monday"),
(2, "Tuesday"),
(3, "Wednesday"),
(4, "Thursday"),
(5, "Friday"),
(6, "Saturday"),
(7, "Sunday"),
],
verbose_name="weekday",
),
),
(
"start_at",
models.PositiveSmallIntegerField(
validators=[django.core.validators.MaxValueValidator(95)],
verbose_name="start at",
),
),
(
"end_at",
models.PositiveSmallIntegerField(
validators=[django.core.validators.MaxValueValidator(95)],
verbose_name="end at",
),
),
(
"timetable",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="slots",
to="timetable.timetable",
verbose_name="timetable",
),
),
],
options={
"verbose_name": "timetable slot",
"verbose_name_plural": "timetable slots",
"permissions": [("add_self_timetable", "Can add its own timetable")],
},
),
migrations.AddConstraint(
model_name="timetable",
constraint=models.UniqueConstraint(
fields=("user", "semester"), name="timetable_unique_user_semester"
),
),
migrations.AddConstraint(
model_name="timetableslot",
constraint=models.CheckConstraint(
condition=models.Q(("end_at__gt", models.F("start_at"))),
name="timetable_slot_end_after_start",
),
),
]
+81
View File
@@ -1 +1,82 @@
# Create your models here.
from datetime import timedelta
from django.core.validators import MaxValueValidator, RegexValidator
from django.db import models
from django.db.models import F, Q
from django.utils.translation import gettext_lazy as _
from core.models import User
from core.utils import get_semester_code
class Timetable(models.Model):
user = models.ForeignKey(User, verbose_name=_("user"), on_delete=models.CASCADE)
semester = models.CharField(
_("semester"),
default=get_semester_code,
validators=[RegexValidator(r"(A|P)\d{2}")],
blank=True,
)
class Meta:
verbose_name = _("timetable")
verbose_name_plural = _("timetables")
constraints = [
models.UniqueConstraint(
fields=("user", "semester"), name="timetable_unique_user_semester"
)
]
permissions = [("add_self_timetable", "Can add its own timetable")]
def __str__(self) -> str:
return f"{self.user} {self.semester}"
class TimetableSlot(models.Model):
MINUTES_PER_SLOT = 15
class WeekDay(models.IntegerChoices):
MONDAY = 1
TUESDAY = 2
WEDNESDAY = 3
THURSDAY = 4
FRIDAY = 5
SATURDAY = 6
SUNDAY = 7
timetable = models.ForeignKey(
Timetable,
verbose_name=_("timetable"),
related_name="slots",
on_delete=models.CASCADE,
)
weekday = models.SmallIntegerField(_("weekday"), choices=WeekDay)
# there are 4 * 24 = 96 quarters in a day, and we start counting at 0
start_at = models.PositiveSmallIntegerField(
_("start at"), validators=[MaxValueValidator(95)]
)
end_at = models.PositiveSmallIntegerField(
_("end at"), validators=[MaxValueValidator(95)]
)
class Meta:
verbose_name = _("timetable slot")
verbose_name_plural = _("timetable slots")
constraints = [
models.CheckConstraint(
condition=Q(end_at__gt=F("start_at")),
name="timetable_slot_end_after_start",
)
]
def __str__(self) -> str:
return f"{self.start_hour} - {self.end_hour} - {self.get_weekday_display()}"
@property
def start_hour(self):
return timedelta(minutes=self.start_at * self.MINUTES_PER_SLOT)
@property
def end_hour(self):
return timedelta(minutes=self.end_at * self.MINUTES_PER_SLOT)