Refactoring

This commit is contained in:
Lorenz Padberg 2023-06-19 11:01:35 +02:00
parent 7ccc771ca7
commit 89ff4af3c8
19 changed files with 135 additions and 88 deletions

View File

@ -14,6 +14,4 @@ import { defineProps } from "vue";
const props = defineProps<{ const props = defineProps<{
dueDate: DueDate; dueDate: DueDate;
}>(); }>();
console.log(props.dueDate);
</script> </script>

View File

@ -23,8 +23,6 @@ const props = defineProps<{
}>(); }>();
const courseSession = useCurrentCourseSession(); const courseSession = useCurrentCourseSession();
console.log(props.maxCount);
const dueDates = courseSession.value.duedates.slice(0, props.maxCount); const dueDates = courseSession.value.duedates.slice(0, props.maxCount);
console.log(courseSession.value.duedates);
</script> </script>

View File

@ -1,9 +1,11 @@
import dayjs from "dayjs";
export const dueDatesTestData = () => { export const dueDatesTestData = () => {
return [ return [
{ {
id: 1, id: 1,
start: "2023-06-14T15:00:00+02:00", start: dayjs("2023-06-14T15:00:00+02:00"),
end: "2023-06-14T18:00:00+02:00", end: dayjs("2023-06-14T18:00:00+02:00"),
title: "Präsenzkurs Kickoff", title: "Präsenzkurs Kickoff",
url: "/course/überbetriebliche-kurse/learn/kickoff/präsenzkurs-kickoff", url: "/course/überbetriebliche-kurse/learn/kickoff/präsenzkurs-kickoff",
course_session: 2, course_session: 2,
@ -11,8 +13,8 @@ export const dueDatesTestData = () => {
}, },
{ {
id: 2, id: 2,
start: "2023-06-15T15:00:00+02:00", start: dayjs("2023-06-15T15:00:00+02:00"),
end: "2023-06-15T18:00:00+02:00", end: dayjs("2023-06-15T18:00:00+02:00"),
title: "Präsenzkurs Basis", title: "Präsenzkurs Basis",
url: "/course/überbetriebliche-kurse/learn/basis/präsenzkurs-basis", url: "/course/überbetriebliche-kurse/learn/basis/präsenzkurs-basis",
course_session: 2, course_session: 2,
@ -20,8 +22,8 @@ export const dueDatesTestData = () => {
}, },
{ {
id: 3, id: 3,
start: "2023-06-16T15:00:00+02:00", start: dayjs("2023-06-16T15:00:00+02:00"),
end: "2023-06-16T18:00:00+02:00", end: dayjs("2023-06-16T18:00:00+02:00"),
title: "Präsenzkurs Fahrzeug", title: "Präsenzkurs Fahrzeug",
url: "/course/überbetriebliche-kurse/learn/fahrzeug/präsenzkurs-fahrzeug", url: "/course/überbetriebliche-kurse/learn/fahrzeug/präsenzkurs-fahrzeug",
course_session: 2, course_session: 2,
@ -29,8 +31,8 @@ export const dueDatesTestData = () => {
}, },
{ {
id: 4, id: 4,
start: "2023-06-16T15:00:00+02:00", start: dayjs("2023-06-16T15:00:00+02:00"),
end: "2023-06-16T18:00:00+02:00", end: dayjs("2023-06-16T18:00:00+02:00"),
title: "Präsenzkurs Flugzeuge", title: "Präsenzkurs Flugzeuge",
url: "/course/überbetriebliche-kurse/learn/fahrzeug/präsenzkurs-fahrzeug", url: "/course/überbetriebliche-kurse/learn/fahrzeug/präsenzkurs-fahrzeug",
course_session: 2, course_session: 2,
@ -38,8 +40,8 @@ export const dueDatesTestData = () => {
}, },
{ {
id: 5, id: 5,
start: "2023-07-16T11:00:00+02:00", start: dayjs("2023-07-16T11:00:00+02:00"),
end: "2023-07-16T18:00:00+02:00", end: dayjs("2023-07-16T18:00:00+02:00"),
title: "Präsenzkurs Motorräder", title: "Präsenzkurs Motorräder",
url: "/course/überbetriebliche-kurse/learn/fahrzeug/präsenzkurs-fahrzeug", url: "/course/überbetriebliche-kurse/learn/fahrzeug/präsenzkurs-fahrzeug",
course_session: 2, course_session: 2,
@ -47,8 +49,8 @@ export const dueDatesTestData = () => {
}, },
{ {
id: 6, id: 6,
start: "2023-08-09T15:00:00+02:00", start: dayjs("2023-08-09T15:00:00+02:00"),
end: "2023-08-09T19:00:00+02:00", end: dayjs("2023-08-09T19:00:00+02:00"),
title: "Präsenzkurs Fahrräder", title: "Präsenzkurs Fahrräder",
url: "/course/überbetriebliche-kurse/learn/fahrzeug/präsenzkurs-fahrzeug", url: "/course/überbetriebliche-kurse/learn/fahrzeug/präsenzkurs-fahrzeug",
course_session: 2, course_session: 2,

View File

@ -1,7 +1,6 @@
export const formatDate = (start_str: string, end_str: string) => { import type { Dayjs } from "dayjs";
const start = new Date(start_str);
const end = new Date(end_str);
export const formatDate = (start: Dayjs, end: Dayjs) => {
const startDateString = getDateString(start); const startDateString = getDateString(start);
const endDateString = getDateString(end); const endDateString = getDateString(end);
@ -14,14 +13,10 @@ export const formatDate = (start_str: string, end_str: string) => {
)}`; )}`;
}; };
const getTimeString = (date: Date) => { const getTimeString = (date: Dayjs) => {
return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); return `${date.format("HH:mm")}`;
}; };
const getDateString = (date: Date) => { const getDateString = (date: Dayjs) => {
return date.toLocaleDateString([], { return `${date.format("DD MMM YYYY")}`;
day: "numeric",
month: "short",
year: "numeric",
});
}; };

View File

@ -1,6 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import * as d3 from "d3"; import * as d3 from "d3";
import * as log from "loglevel";
import { computed, onMounted } from "vue"; import { computed, onMounted } from "vue";
// @ts-ignore // @ts-ignore
@ -17,7 +16,6 @@ const props = defineProps<{
}>(); }>();
onMounted(async () => { onMounted(async () => {
log.debug("LearningPathCircle mounted");
render(); render();
}); });

View File

@ -10,6 +10,7 @@ import type {
} from "@/types"; } from "@/types";
import eventBus from "@/utils/eventBus"; import eventBus from "@/utils/eventBus";
import { useLocalStorage } from "@vueuse/core"; import { useLocalStorage } from "@vueuse/core";
import dayjs from "dayjs";
import uniqBy from "lodash/uniqBy"; import uniqBy from "lodash/uniqBy";
import log from "loglevel"; import log from "loglevel";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
@ -25,7 +26,6 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
async function loadCourseSessionsData(reload = false) { async function loadCourseSessionsData(reload = false) {
log.debug("loadCourseSessionsData called"); log.debug("loadCourseSessionsData called");
allCourseSessions.value = await itGetCached(`/api/course/sessions/`, { allCourseSessions.value = await itGetCached(`/api/course/sessions/`, {
reload: reload, reload: reload,
}); });
@ -39,6 +39,11 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
reload: reload, reload: reload,
})) as CourseSessionUser[]; })) as CourseSessionUser[];
cs.users = users; cs.users = users;
cs.duedates.forEach((dueDate) => {
dueDate.start = dayjs(dueDate.start);
dueDate.end = dayjs(dueDate.end);
console.log(dueDate);
});
}) })
); );

View File

@ -1,5 +1,6 @@
import type { AssignmentCompletionStatus as AssignmentCompletionStatusGenerated } from "@/gql/graphql"; import type { AssignmentCompletionStatus as AssignmentCompletionStatusGenerated } from "@/gql/graphql";
import type { Circle } from "@/services/circle"; import type { Circle } from "@/services/circle";
import { Dayjs } from "dayjs";
import type { Component } from "vue"; import type { Component } from "vue";
export type LoginMethod = "local" | "sso"; export type LoginMethod = "local" | "sso";
@ -560,8 +561,8 @@ export interface UserAssignmentCompletionStatus {
export type DueDate = { export type DueDate = {
id: number; id: number;
start: string; start: Dayjs;
end: string; end: Dayjs;
title: string; title: string;
url: string; url: string;
course_session: number | null; course_session: number | null;

View File

@ -102,7 +102,7 @@ class CourseSessionSerializer(serializers.ModelSerializer):
"media_library_url", "media_library_url",
"course_url", "course_url",
"documents", "documents",
"duedates" "duedates",
] ]

View File

@ -12,13 +12,7 @@ from vbv_lernwelt.learnpath.models import (
@admin.register(DueDate) @admin.register(DueDate)
class DueDateAdmin(admin.ModelAdmin): class DueDateAdmin(admin.ModelAdmin):
date_hierarchy = "end" date_hierarchy = "end"
list_display = [ list_display = ["title", "course_session", "start", "end", "unset"]
"title",
"course_session",
"start",
"end",
"unset"
]
list_filter = ["course_session"] list_filter = ["course_session"]
def formfield_for_foreignkey(self, db_field, request, **kwargs): def formfield_for_foreignkey(self, db_field, request, **kwargs):

View File

@ -4,16 +4,20 @@ import structlog
from django.utils import timezone from django.utils import timezone
from factory.django import DjangoModelFactory from factory.django import DjangoModelFactory
from .models import DueDate
from ..assignment.models import Assignment from ..assignment.models import Assignment
from ..course.models import CourseSession from ..course.models import CourseSession
from ..learnpath.models import LearningContentAttendanceCourse from ..learnpath.models import LearningContentAttendanceCourse
from .models import DueDate
logger = structlog.get_logger(__name__) logger = structlog.get_logger(__name__)
def get_date(date_string): def get_date(date_string):
return datetime.datetime.strptime(date_string, '%b %d %Y', ).astimezone(timezone.get_current_timezone()) return datetime.datetime.strptime(
date_string,
"%b %d %Y",
).astimezone(timezone.get_current_timezone())
class DueDateFactory(DjangoModelFactory): class DueDateFactory(DjangoModelFactory):
@ -26,14 +30,18 @@ class DueDateFactory(DjangoModelFactory):
def generate_events(start=timezone.now()): def generate_events(start=timezone.now()):
for i in range(20): for i in range(20):
DueDateFactory(title=f"{i}", start=start + datetime.timedelta(days=i), DueDateFactory(
end=start + datetime.timedelta(days=i, hours=1)) title=f"{i}",
start=start + datetime.timedelta(days=i),
end=start + datetime.timedelta(days=i, hours=1),
)
def hour_rounder(t): def hour_rounder(t):
# Rounds to nearest hour by adding a timedelta hour if minute >= 30 # Rounds to nearest hour by adding a timedelta hour if minute >= 30
return (t.replace(second=0, microsecond=0, minute=0, hour=t.hour) return t.replace(
+ datetime.timedelta(hours=t.minute // 30)) second=0, microsecond=0, minute=0, hour=t.hour
) + datetime.timedelta(hours=t.minute // 30)
def create_duedates_for_all_course_sessions(): def create_duedates_for_all_course_sessions():
@ -45,22 +53,33 @@ def create_duedates_for_all_course_sessions():
def create_duedates_for_course_session(course_session: CourseSession): def create_duedates_for_course_session(course_session: CourseSession):
course = course_session.course course = course_session.course
attendance_courses = list(LearningContentAttendanceCourse.objects.descendant_of(course.get_learning_path())) attendance_courses = list(
LearningContentAttendanceCourse.objects.descendant_of(
course.get_learning_path()
)
)
assignments = list(Assignment.objects.descendant_of(course.get_learning_path())) assignments = list(Assignment.objects.descendant_of(course.get_learning_path()))
contents = attendance_courses + assignments contents = attendance_courses + assignments
for content in contents: for content in contents:
if callable(getattr(content, 'get_frontend_url', None)): if callable(getattr(content, "get_frontend_url", None)):
url = content.get_frontend_url() url = content.get_frontend_url()
duedate, created = DueDate.objects.get_or_create(course_session=course_session, duedate, created = DueDate.objects.get_or_create(
course_session=course_session,
title=content.title, title=content.title,
url=url, url=url,
page=content.specific) page=content.specific,
)
if created: if created:
pass pass
logger.info("Created duedate ", duedate=duedate, course_session=course_session, content=content) logger.info(
"Created duedate ",
duedate=duedate,
course_session=course_session,
content=content,
)
def set_default_times_for_duedates(): def set_default_times_for_duedates():

View File

@ -1,6 +1,9 @@
import djclick as click import djclick as click
from vbv_lernwelt.duedate.factories import create_duedates_for_all_course_sessions, set_default_times_for_duedates from vbv_lernwelt.duedate.factories import (
create_duedates_for_all_course_sessions,
set_default_times_for_duedates,
)
@click.command() @click.command()

View File

@ -1,7 +1,7 @@
# Generated by Django 3.2.13 on 2023-06-14 09:29 # Generated by Django 3.2.13 on 2023-06-14 09:29
from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -9,20 +9,40 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('course', '0004_import_fields'), ("course", "0004_import_fields"),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='DueDate', name="DueDate",
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), (
('start', models.DateTimeField(db_index=True, null=True)), "id",
('end', models.DateTimeField(db_index=True, null=True)), models.BigAutoField(
('title', models.CharField(default='Termin', max_length=1024)), auto_created=True,
('url', models.URLField(blank=True, max_length=1024, null=True)), primary_key=True,
('learning_content_id', models.CharField(blank=True, max_length=255, null=True)), serialize=False,
('course_session', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='events', to='course.coursesession')), verbose_name="ID",
),
),
("start", models.DateTimeField(db_index=True, null=True)),
("end", models.DateTimeField(db_index=True, null=True)),
("title", models.CharField(default="Termin", max_length=1024)),
("url", models.URLField(blank=True, max_length=1024, null=True)),
(
"learning_content_id",
models.CharField(blank=True, max_length=255, null=True),
),
(
"course_session",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="events",
to="course.coursesession",
),
),
], ],
), ),
] ]

View File

@ -1,24 +1,29 @@
# Generated by Django 3.2.13 on 2023-06-14 13:00 # Generated by Django 3.2.13 on 2023-06-14 13:00
from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('wagtailcore', '0083_workflowcontenttype'), ("wagtailcore", "0083_workflowcontenttype"),
('duedate', '0001_initial'), ("duedate", "0001_initial"),
] ]
operations = [ operations = [
migrations.RemoveField( migrations.RemoveField(
model_name='duedate', model_name="duedate",
name='learning_content_id', name="learning_content_id",
), ),
migrations.AddField( migrations.AddField(
model_name='duedate', model_name="duedate",
name='page', name="page",
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wagtailcore.page'), field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="wagtailcore.page",
),
), ),
] ]

View File

@ -12,12 +12,12 @@ from vbv_lernwelt.course.models import CourseSession
class DueDate(models.Model): class DueDate(models.Model):
start = models.DateTimeField(null=True, db_index=True) start = models.DateTimeField(null=True, db_index=True)
end = models.DateTimeField(db_index=True, null=True) end = models.DateTimeField(db_index=True, null=True)
title = models.CharField(default=_('Termin'), max_length=1024) title = models.CharField(default=_("Termin"), max_length=1024)
url = models.URLField(null=True, blank=True, max_length=1024) url = models.URLField(null=True, blank=True, max_length=1024)
course_session = models.ForeignKey( course_session = models.ForeignKey(
'course.CourseSession', "course.CourseSession",
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='duedates', related_name="duedates",
null=True, null=True,
blank=True, blank=True,
) )
@ -25,12 +25,12 @@ class DueDate(models.Model):
page = models.ForeignKey(Page, on_delete=models.SET_NULL, null=True, blank=True) page = models.ForeignKey(Page, on_delete=models.SET_NULL, null=True, blank=True)
def Meta(self): def Meta(self):
ordering = ['start', 'end'] ordering = ["start", "end"]
verbose_name = _("Termin") verbose_name = _("Termin")
def __str__(self): def __str__(self):
start_str = self.start.strftime('%Y-%m-%d %H:%M') if self.start else '' start_str = self.start.strftime("%Y-%m-%d %H:%M") if self.start else ""
end_str = self.end.strftime('%Y-%m-%d %H:%M') if self.end else '' end_str = self.end.strftime("%Y-%m-%d %H:%M") if self.end else ""
return f"DueDate: {self.title} {start_str} to {end_str}" return f"DueDate: {self.title} {start_str} to {end_str}"
@property @property
@ -44,7 +44,9 @@ class DueDate(models.Model):
return self.end - self.start return self.end - self.start
@classmethod @classmethod
def get_users_next_events_qs(cls, user: User, course_session: CourseSession = None, limit=10): def get_users_next_events_qs(
cls, user: User, course_session: CourseSession = None, limit=10
):
""" """
Returns a queryset of all duedates that are relevant for the given user. Ordered nearest start date first. Returns a queryset of all duedates that are relevant for the given user. Ordered nearest start date first.
If course_session is given, only duedates for that course_session are returned. If course_session is given, only duedates for that course_session are returned.
@ -54,11 +56,14 @@ class DueDate(models.Model):
qs = cls.get_next_duedates_qs() qs = cls.get_next_duedates_qs()
if course_session: if course_session:
qs = qs.filter(course_session=course_session, course_session__course_assignment__user=user) qs = qs.filter(
course_session=course_session,
course_session__course_assignment__user=user,
)
else: else:
qs = qs.filter(course_session__course_assignment__user=user) qs = qs.filter(course_session__course_assignment__user=user)
qs = qs.order_by('start')[:limit] qs = qs.order_by("start")[:limit]
return qs return qs

View File

@ -6,4 +6,4 @@ from vbv_lernwelt.duedate.models import DueDate
class DueDateSerializer(serializers.ModelSerializer): class DueDateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = DueDate model = DueDate
fields = '__all__' fields = "__all__"

View File

@ -34,5 +34,8 @@ class TesDueDatetModel(TestCase):
def generate_duedates(start=timezone.now()): def generate_duedates(start=timezone.now()):
for i in range(20): for i in range(20):
DueDateFactory(title=f"{i}", start=start + datetime.timedelta(days=i), DueDateFactory(
end=start + datetime.timedelta(days=i, hours=1)) title=f"{i}",
start=start + datetime.timedelta(days=i),
end=start + datetime.timedelta(days=i, hours=1),
)

View File

@ -12,4 +12,4 @@ class TestDueDatetSerializer(TestCase):
duedates = DueDate.objects.all() duedates = DueDate.objects.all()
result = DueDateSerializer(duedates, many=True).data result = DueDateSerializer(duedates, many=True).data
assert result[0]['title'] == 'Prüfung Versicherungsvermittler/-in' assert result[0]["title"] == "Prüfung Versicherungsvermittler/-in"

View File

@ -282,6 +282,7 @@ class LearningContentAttendanceCourse(LearningContent):
""" """
Präsenzkurs Präsenzkurs
""" """
parent_page_types = ["learnpath.Circle"] parent_page_types = ["learnpath.Circle"]
subpage_types = [] subpage_types = []