From e365d255b96f7de761b4db59289afb36eb83edca Mon Sep 17 00:00:00 2001 From: AlexandreDoneux <94830560+AlexandreDoneux@users.noreply.github.com> Date: Tue, 12 Dec 2023 11:31:01 +0100 Subject: [PATCH] [frontend] Changing accessible and registration structure when importing legacy courses + moving dict_data_str_to_datetime() --- inginious/frontend/course_factory.py | 83 +++++++++++++++++++ .../frontend/pages/course_admin/task_list.py | 2 - .../frontend/pages/taskset_admin/template.py | 2 - inginious/frontend/pages/utils.py | 20 ----- 4 files changed, 83 insertions(+), 24 deletions(-) diff --git a/inginious/frontend/course_factory.py b/inginious/frontend/course_factory.py index 05e66d329..201fcc9cf 100644 --- a/inginious/frontend/course_factory.py +++ b/inginious/frontend/course_factory.py @@ -6,6 +6,7 @@ """ Factory for loading courses from disk """ from pymongo import ReturnDocument +from datetime import datetime from inginious.frontend.log import get_course_logger @@ -13,6 +14,72 @@ from inginious.frontend.courses import Course +# remove from pages/utils.py because only used here for the moment +def dict_data_str_to_datetimes(data): + if isinstance(data, dict): + for key, value in data.items(): + if isinstance(value, str): + try: + if value == "1-01-01 00:00:00": + data[key] = datetime.min + elif value == "9999-12-31 23:59:59": + data[key] = datetime.max + else: + data[key] = datetime.strptime(value, '%Y-%m-%d %H:%M:%S') if (value != "") else None + except ValueError: + pass # If it's not a valid date string, continue without converting + else: + dict_data_str_to_datetimes(value) + elif isinstance(data, list): + for index, item in enumerate(data): + dict_data_str_to_datetimes(item) + return data + + +def change_access_structure(access_data, needs_soft_end=False): + """ + Transforms old access structure (course access and registration, task access) into new structure. + ex: "accessible" can be a boolean or a concatenation of start and end dates ("start/end"). + It will be transformed to have this structure: + "accessible": {"start": ..., "end": ...} + "registration": {"start": ..., "end": ...} + "accessibility": {"start": ...,"soft_end": ..., "end": ...} + When one of the dates is not given in a custom access or always/never accessible, it will be set to a max or min date. + examples: + "registration": {"start": "2023-11-24 16:44:56", "end": "2023-11-24 16:44:56"} + "accessible": {"start": "2023-11-24 16:44:56", "end": "2023-11-24 16:44:56"} + :param access_data: dict, old access structure + :param needs_soft_end: bool, True if the new structure needs a soft_end date in the structure + :return: dict, new access structure + """ + + new_access_data = {"start": None, "end": None} + # PK pas des objets datetime ? Les datetime seraint manipulés à l'écriture en YAML (et en DB si cette fonction est appelée à l'écriture en DB) + + + if isinstance(access_data, bool): + new_access_data["end"] = "9999-12-31 23:59:59" + if needs_soft_end: + new_access_data["soft_end"] = "9999-12-31 23:59:59" + if access_data: + new_access_data["start"] = "0001-01-01 00:00:00" + else: + new_access_data["start"] = "9999-12-31 23:59:59" + + + elif isinstance(access_data, str) and access_data != "": + dates = access_data.split("/") + if needs_soft_end: + new_access_data["start"] = dates[0] + new_access_data["soft_end"] = dates[1] + new_access_data["end"] = dates[2] + else: + new_access_data["start"] = dates[0] + new_access_data["end"] = dates[1] + + return new_access_data + + class CourseFactory(object): """ Load courses from disk """ @@ -98,6 +165,11 @@ def _migrate_legacy_courses(self): if taskset.is_legacy() and not self._database.courses.find_one({"_id": tasksetid}): courseids.append(tasksetid) + # look here for bad accessibilitY structure ??? -> _migrate_legacy_courses detect the courses whithout taskset (taskset id in DB and correct taskset yaml file) + # I can maybe also checko here if the taskset file has the right structure (task accessibilities). + + # where to check for DB structure ? + for courseid in courseids: get_course_logger(courseid).warning("Trying to migrate legacy course {}.".format(courseid)) @@ -113,7 +185,18 @@ def _migrate_legacy_courses(self): cleaned_taskset_descriptor["dispenser_data"] = taskset_descriptor.get("dispenser_data", {}) taskset_descriptor["tasksetid"] = courseid taskset_descriptor["admins"] = taskset_descriptor.get("admins", []) + taskset_descriptor.get("tutors", []) + if "accessible" in taskset_descriptor: + taskset_descriptor["accessible"] = change_access_structure(taskset_descriptor["accessible"]) + if "registration" in taskset_descriptor: + taskset_descriptor["registration"] = change_access_structure(taskset_descriptor["registration"]) + + # here transform task accessibilities ? -> no, it will be done during the migration of the taskset (import_legacy_tasks) + # task accessibilities are not in the course descriptor, but in the tasks descriptors (task.yaml) + + taskset_descriptor = dict_data_str_to_datetimes(taskset_descriptor) + #cleaned_taskset_descriptor = dict_data_str_to_datetimes(cleaned_taskset_descriptor) self._database.courses.update_one({"_id": courseid}, {"$set": taskset_descriptor}, upsert=True) + # why not set cleaned_taskset_descriptor ? -> parce qu'on transmet le taskset_descriptor pour l'enregistrer en DB self._taskset_factory.update_taskset_descriptor_content(courseid, cleaned_taskset_descriptor) except TasksetNotFoundException as e: get_course_logger(courseid).warning("No migration from taskset possible for courseid {}.".format(courseid)) diff --git a/inginious/frontend/pages/course_admin/task_list.py b/inginious/frontend/pages/course_admin/task_list.py index daddc296b..e06f462ca 100644 --- a/inginious/frontend/pages/course_admin/task_list.py +++ b/inginious/frontend/pages/course_admin/task_list.py @@ -8,10 +8,8 @@ import flask from collections import OrderedDict from natsort import natsorted -from datetime import datetime from inginious.frontend.pages.course_admin.utils import INGIniousAdminPage -from inginious.frontend.pages.utils import dict_data_str_to_datetimes class CourseTaskListPage(INGIniousAdminPage): """ List informations about all tasks """ diff --git a/inginious/frontend/pages/taskset_admin/template.py b/inginious/frontend/pages/taskset_admin/template.py index 1b934de5b..3b04b2082 100644 --- a/inginious/frontend/pages/taskset_admin/template.py +++ b/inginious/frontend/pages/taskset_admin/template.py @@ -8,10 +8,8 @@ import flask from collections import OrderedDict from natsort import natsorted -from datetime import datetime from inginious.frontend.pages.taskset_admin.utils import INGIniousAdminPage -from inginious.frontend.pages.utils import dict_data_str_to_datetimes diff --git a/inginious/frontend/pages/utils.py b/inginious/frontend/pages/utils.py index ce9d4058b..5c4007551 100644 --- a/inginious/frontend/pages/utils.py +++ b/inginious/frontend/pages/utils.py @@ -366,23 +366,3 @@ def register_utils(database, user_manager, template_helper: TemplateHelper): single) ) - -def dict_data_str_to_datetimes(data): - if isinstance(data, dict): - for key, value in data.items(): - if isinstance(value, str): - try: - if value == "1-01-01 00:00:00": - data[key] = datetime.min - elif value == "9999-12-31 23:59:59": - data[key] = datetime.max - else: - data[key] = datetime.strptime(value, '%Y-%m-%d %H:%M:%S') if (value != "") else None - except ValueError: - pass # If it's not a valid date string, continue without converting - else: - dict_data_str_to_datetimes(value) - elif isinstance(data, list): - for index, item in enumerate(data): - dict_data_str_to_datetimes(item) - return data \ No newline at end of file