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

Merge branch 'master' into 41-create-match-failures-as-data-checks

parents c917e415 40a2a7b8
No related branches found
No related tags found
1 merge request!120Resolve "Create match failures as data checks"
......@@ -9,11 +9,28 @@ and this project adheres to `Semantic Versioning`_.
Unreleased
----------
Added
~~~~~
* Add fuzzy matching mode for searching course groups: If no 100 % match is found,
the importer will search a match by a subset of parent groups.
Changed
~~~~~~~
* Let untis_import_mysql management command default to ``current`` instead of all
to prevent accidental imports of old plans
* Use new change tracker from Chronos to trigger notifications
Fixed
~~~~~
* Search course groups not only by parent groups and subject, but also take
the teachers (group owners) into account
* Don't recreate lesson periods if they change, but just update them.
`2.1.3`_ - 2022-02-06
---------------------
Fixed
~~~~~
......@@ -182,3 +199,4 @@ Fixed
.. _2.1: https://edugit.org/Teckids/AlekSIS/AlekSIS-App-Untis/-/tags/2.1
.. _2.1.1: https://edugit.org/Teckids/AlekSIS/AlekSIS-App-Untis/-/tags/2.1.1
.. _2.1.2: https://edugit.org/Teckids/AlekSIS/AlekSIS-App-Untis/-/tags/2.1.2
.. _2.1.3: https://edugit.org/Teckids/AlekSIS/AlekSIS-App-Untis/-/tags/2.1.3
......@@ -106,6 +106,15 @@ class DataCheckNotFoundCourseGroups(BooleanPreference):
verbose_name = _("Register a data problem if a course group has been not found.")
@site_preferences_registry.register
class CourseGroupsFuzzyMatching(BooleanPreference):
section = untis_mysql
name = "course_groups_fuzzy_matching"
default = False
verbose_name = _("Match course groups by a subset of parent groups if no 100% match is found")
help_text = _("Works only if 'Use course groups' is activated.")
@site_preferences_registry.register
class IgnoreIncompleteSubstitutions(BooleanPreference):
section = untis_mysql
......
......@@ -126,21 +126,54 @@ def import_lessons(
# Negative import_ref denotes a course group
group_import_ref = -int("{}{}".format(lesson_id, i))
# Search by parent groups and subject
# Search by parent groups, teachers/owners and subject
qs = core_models.Group.objects.filter(
parent_groups__in=[c.id for c in course_classes],
subject_id=subject.id,
owners__in=[t.id for t in teachers],
).filter(Q(school_term__isnull=True) | Q(school_term=validity_range.school_term))
# Check if found groups match
course_group = None
for found_group in qs:
if compare_m2m(course_classes, found_group.parent_groups.all()):
if compare_m2m(course_classes, found_group.parent_groups.all()) and compare_m2m(
teachers, found_group.owners.all()
):
match = True
course_group = found_group
logger.info(
" Course group found by searching by parent groups and subject"
" Course group found by searching by parent groups, "
"teachers (owners) and subject"
)
if (
not match
and get_site_preferences()["untis_mysql__course_groups_fuzzy_matching"]
):
if qs.count() != 1:
logger.warning(
" Course group not found by searching by parent groups, "
"teachers (owners) and subject (fuzzy matching mode)"
)
else:
for found_group in qs:
if compare_m2m(teachers, found_group.owners.all()):
if match:
logger.warning(
" More than one course group found "
"by searching by parent groups, "
"teachers (owners) and subject (fuzzy matching mode)"
)
match = False
course_group = None
else:
match = True
course_group = found_group
logger.info(
" Course group found by searching by parent groups, "
"teachers (owners) and subject (fuzzy matching mode)"
)
changed = False
register_data_check = get_site_preferences()[
"untis_mysql__data_check_for_not_found_course_groups"
......@@ -257,10 +290,7 @@ def import_lessons(
# All times for this course
old_lesson_periods_qs = chronos_models.LessonPeriod.objects.filter(lesson=lesson)
# If length has changed, delete all lesson periods
if old_lesson_periods_qs.count() != len(time_periods):
old_lesson_periods_qs.delete()
logger.info(" Lesson periods deleted")
existing_lesson_period_pks = []
# Sync time periods
for j, time_period in enumerate(time_periods):
......@@ -274,24 +304,29 @@ def import_lessons(
room = None
# Check if an old lesson period is provided
old_lesson_period_qs = old_lesson_periods_qs.filter(element_id_untis=j)
old_lesson_period_qs = old_lesson_periods_qs.filter(period=time_period)
if old_lesson_period_qs.exists():
# Update old lesson period
old_lesson_period = old_lesson_period_qs[0]
if old_lesson_period.period != time_period or old_lesson_period.room != room:
old_lesson_period.period = time_period
old_lesson_period.room = room
old_lesson_period.save()
logger.info(" Time period and room updated")
lesson_period = old_lesson_period_qs[0]
if lesson_period.room != room or lesson_period.element_id_untis != j:
lesson_period.element_id_untis = j
lesson_period.room = room
lesson_period.save()
logger.info(" Untis reference and room updated")
else:
# Create new lesson period
chronos_models.LessonPeriod.objects.create(
lesson_period = chronos_models.LessonPeriod.objects.create(
lesson=lesson, period=time_period, room=room, element_id_untis=j
)
logger.info(" New time period added")
logger.info(" New lesson period added")
existing_lesson_period_pks.append(lesson_period.pk)
# delete all no-longer existing lesson periods
old_lesson_periods_qs.exclude(pk__in=existing_lesson_period_pks).delete()
logger.info(" Old lesson periods deleted")
current_teacher_id = teacher_id
for lesson in chronos_models.Lesson.objects.filter(validity=validity_range):
......
......@@ -5,6 +5,7 @@ from django.db.models import QuerySet
import reversion
from tqdm import tqdm
from aleksis.apps.chronos.util.change_tracker import TimetableDataChangeTracker
from aleksis.apps.untis.util.mysql.importers.terms import import_terms
from aleksis.apps.untis.util.mysql.util import TQDM_DEFAULTS
......@@ -35,6 +36,8 @@ def untis_import_mysql(terms: Optional[QuerySet] = None, version: Optional[int]
with reversion.create_revision(atomic=True):
reversion.set_comment(f"Untis import for validity range {validity_range}")
tracker = TimetableDataChangeTracker()
# Common data for Chronos
subjects_ref = import_subjects(validity_range)
rooms_ref = import_rooms(validity_range)
......
......@@ -31,7 +31,7 @@ author = "The AlekSIS Team"
# The short X.Y version
version = "2.1"
# The full version, including alpha/beta/rc tags
release = "2.1.3.dev0"
release = "2.1.4.dev0"
# -- General configuration ---------------------------------------------------
......
[tool.poetry]
name = "AlekSIS-App-Untis"
version = "2.1.3.dev0"
version = "2.1.4.dev0"
packages = [
{ include = "aleksis" }
]
......@@ -41,11 +41,11 @@ python = "^3.9"
mysqlclient = "^2.0.0"
tqdm = "^4.44.1"
defusedxml = "^0.7.0"
aleksis-core = "^2.0"
aleksis-app-chronos = "^2.0"
aleksis-core = "^2.8"
aleksis-app-chronos = "^2.3"
[tool.poetry.dev-dependencies]
aleksis-builddeps = "^6"
aleksis-builddeps = "*"
[tool.poetry.plugins."aleksis.app"]
untis = "aleksis.apps.untis.apps:UntisConfig"
......
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