Skip to content
Snippets Groups Projects
Verified Commit 79060a31 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Add model and managers for school years

parent e5e98490
No related branches found
No related tags found
1 merge request!299Resolve "Link data to school term"
from datetime import date
from typing import Union
from django.contrib.sites.managers import CurrentSiteManager as _CurrentSiteManager
from django.db.models import QuerySet
from calendarweek import CalendarWeek
class CurrentSiteManagerWithoutMigrations(_CurrentSiteManager):
"""CurrentSiteManager for auto-generating managers just by query sets."""
use_in_migrations = False
class DateRangeQuerySetMixin:
"""QuerySet with custom query methods for models with date ranges.
Filterable fields: date_start, date_end
"""
def within_dates(self, start: date, end: date):
"""Filter for all objects within a date range."""
return self.filter(date_start__lte=end, date_end__gte=start)
def in_week(self, wanted_week: CalendarWeek):
"""Filter for all objects within a calendar week."""
return self.within_dates(wanted_week[0], wanted_week[6])
def on_day(self, day: date):
"""Filter for all objects on a certain day."""
return self.within_dates(day, day)
class SchoolYearQuerySet(QuerySet, DateRangeQuerySetMixin):
"""Custom query set for school years."""
class SchoolYearRelatedQuerySet(QuerySet):
"""Custom query set for all models related to school years."""
def within_dates(self, start: date, end: date) -> "SchoolYearRelatedQuerySet":
"""Filter for all objects within a date range."""
return self.filter(school_year__date_start__lte=end, school_year__date_end__gte=start)
def in_week(self, wanted_week: CalendarWeek) -> "SchoolYearRelatedQuerySet":
"""Filter for all objects within a calendar week."""
return self.within_dates(wanted_week[0], wanted_week[6])
def on_day(self, day: date) -> "SchoolYearRelatedQuerySet":
"""Filter for all objects on a certain day."""
return self.within_dates(day, day)
def for_school_year(self, school_year: "SchoolYear") -> "SchoolYearRelatedQuerySet":
return self.filter(school_year=school_year)
def for_current_school_year_or_all(self) -> "SchoolYearRelatedQuerySet":
"""Get all objects related to current school year.
If there is no current school year, it will return all objects.
"""
from aleksis.core.models import SchoolYear
current_school_year = SchoolYear.current
if current_school_year:
return self.for_school_year(current_school_year)
else:
return self
def for_current_school_year_or_none(self) -> Union["SchoolYearRelatedQuerySet", None]:
"""Get all objects related to current school year.
If there is no current school year, it will return `None`.
"""
from aleksis.core.models import SchoolYear
current_school_year = SchoolYear.current
if current_school_year:
return self.for_school_year(current_school_year)
else:
return None
# Generated by Django 3.0.6 on 2020-06-04 09:50
import django.contrib.postgres.fields.jsonb
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('sites', '0002_alter_domain_unique'),
('core', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='SchoolYear',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)),
('name', models.CharField(max_length=255, unique=True, verbose_name='Name')),
('date_start', models.DateField(verbose_name='Start date')),
('date_end', models.DateField(verbose_name='End date')),
('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
],
options={
'verbose_name': 'School year',
'verbose_name_plural': 'School years',
},
),
]
......@@ -8,11 +8,13 @@ from django.contrib.auth.models import Group as DjangoGroup
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import QuerySet
from django.forms.widgets import Media
from django.urls import reverse
from django.utils import timezone
from django.utils.decorators import classproperty
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _
......@@ -22,7 +24,8 @@ from image_cropping import ImageCropField, ImageRatioField
from phonenumber_field.modelfields import PhoneNumberField
from polymorphic.models import PolymorphicModel
from .mixins import ExtensibleModel, PureDjangoModel
from .managers import CurrentSiteManagerWithoutMigrations, SchoolYearQuerySet
from .mixins import ExtensibleModel, PureDjangoModel, SchoolYearRelatedExtensibleModel
from .tasks import send_notification
from .util.core_helpers import get_site_preferences, now_tomorrow
from .util.model_helpers import ICONS
......@@ -43,6 +46,53 @@ FIELD_CHOICES = (
)
class SchoolYear(ExtensibleModel):
"""School year model.
This is used to manage start and end times of a school year and link data to it.
"""
objects = CurrentSiteManagerWithoutMigrations.from_queryset(SchoolYearQuerySet)()
name = models.CharField(verbose_name=_("Name"), max_length=255, unique=True)
date_start = models.DateField(verbose_name=_("Start date"))
date_end = models.DateField(verbose_name=_("End date"))
@classmethod
def get_current(cls, day: Optional[date] = None):
if not day:
day = timezone.now().date()
try:
return cls.objects.on_day(day).first()
except SchoolYear.DoesNotExist:
return None
@classproperty
def current(cls):
return cls.get_current()
def clean(self):
"""Ensure there is only one school year of each point of time."""
if self.date_end < self.date_start:
raise ValidationError(_("The start date must be earlier than the end date."))
qs = SchoolYear.objects.within_dates(self.date_start, self.date_end)
if self.pk:
qs.exclude(pk=self.pk)
if qs.exists():
raise ValidationError(
_("There is already a school year for this time or a part of this time.")
)
def __str__(self):
return self.name
class Meta:
verbose_name = _("School year")
verbose_name_plural = _("School years")
class Person(ExtensibleModel):
"""Person model.
......@@ -272,7 +322,7 @@ class Group(ExtensibleModel):
name = models.CharField(verbose_name=_("Long name"), max_length=255, unique=True)
short_name = models.CharField(
verbose_name=_("Short name"), max_length=255, unique=True, blank=True, null=True # noqa
verbose_name=_("Short name"), max_length=255, blank=True, null=True # noqa
)
members = models.ManyToManyField(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment