def update_task_descriptor_content(self, courseid, taskid, content, force_extension=None): """ Update the task descriptor with the dict in content :param course: the course id of the course :param taskid: the task id of the task :param content: the content to put in the task file :param force_extension: If None, save it the same format. Else, save with the given extension :raise InvalidNameException, TaskNotFoundException, TaskUnreadableException """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) if force_extension is None: path_to_descriptor, _, descriptor_manager = self._get_task_descriptor_info( courseid, taskid) elif force_extension in self.get_available_task_file_extensions(): path_to_descriptor = os.path.join(self._tasks_directory, courseid, taskid, "task." + force_extension) descriptor_manager = self._task_file_managers[force_extension] else: raise TaskReaderNotFoundException() try: with codecs.open(path_to_descriptor, 'w', 'utf-8') as fd: fd.write(descriptor_manager.dump(content)) except: raise TaskNotFoundException()
def delete_all_possible_task_files(self, courseid, taskid): """ Deletes all possibles task files in directory, to allow to change the format """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) task_fs = self.get_task_fs(courseid, taskid) for ext in self.get_available_task_file_extensions(): try: task_fs.delete("task." + ext) except: pass
def get_task_fs(self, courseid, taskid): """ :param courseid: the course id of the course :param taskid: the task id of the task :raise InvalidNameException :return: A FileSystemProvider to the folder containing the task files """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) return self._filesystem.from_subfolder(courseid).from_subfolder(taskid)
def get_directory_path(self, courseid, taskid): """ :param courseid: the course id of the course :param taskid: the task id of the task :raise InvalidNameException :return: The path to the directory that contains the files related to the task """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) return os.path.join(self._tasks_directory, courseid, taskid)
def get_task_descriptor_extension(self, courseid, taskid): """ :param courseid: the course id of the course :param taskid: the task id of the task :raise InvalidNameException, TaskNotFoundException :return: the current extension of the task descriptor """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) descriptor_path = self._get_task_descriptor_info(courseid, taskid)[0] return splitext(descriptor_path)[1]
def _update_cache(self, course, taskid): """ Updates the cache :param course: a Course object :param taskid: a (valid) task id :raise InvalidNameException, TaskNotFoundException, TaskUnreadableException """ if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) task_fs = self.get_task_fs(course.get_id(), taskid) descriptor_name, descriptor_reader = self._get_task_descriptor_info(course.get_id(), taskid) try: task_content = descriptor_reader.load(task_fs.get(descriptor_name)) except Exception as e: raise TaskUnreadableException(str(e)) last_modif = {descriptor_name: task_fs.get_last_modification_time(descriptor_name)} translations_fs = task_fs.from_subfolder("$i18n") if translations_fs.exists(): for f in translations_fs.list(folders=False, files=True, recursive=False): lang = f[0:len(f) - 3] if translations_fs.exists(lang + ".mo"): last_modif["$i18n/" + lang + ".mo"] = translations_fs.get_last_modification_time(lang + ".mo") self._cache[(course.get_id(), taskid)] = ( self._task_class(course, taskid, task_content, task_fs, self._hook_manager, self._task_problem_types), last_modif )
def _cache_update_needed(self, course, taskid): """ :param course: a Course object :param taskid: a (valid) task id :raise InvalidNameException, TaskNotFoundException :return: True if an update of the cache is needed, False else """ if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) task_fs = self.get_task_fs(course.get_id(), taskid) if (course.get_id(), taskid) not in self._cache: return True try: descriptor_name = self._get_task_descriptor_info(course.get_id(), taskid)[0] last_update = {descriptor_name: task_fs.get_last_modification_time(descriptor_name)} translations_fs = task_fs.from_subfolder("$i18n") if translations_fs.exists(): for f in translations_fs.list(folders=False, files=True, recursive=False): lang = f[0:len(f) - 3] if translations_fs.exists(lang + ".mo"): last_update["$i18n/" + lang + ".mo"] = translations_fs.get_last_modification_time(lang + ".mo") except: raise TaskNotFoundException() last_modif = self._cache[(course.get_id(), taskid)][1] for filename, mftime in last_update.items(): if filename not in last_modif or last_modif[filename] < mftime: return True return False
def create_course(self, courseid, init_content): """ :param courseid: the course id of the course :param init_content: initial descriptor content :raise InvalidNameException or CourseAlreadyExistsException Create a new course folder and set initial descriptor content, folder can already exist """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) course_fs = self.get_course_fs(courseid) course_fs.ensure_exists() if course_fs.exists("course.yaml") or course_fs.exists("course.json"): raise CourseAlreadyExistsException("Course with id " + courseid + " already exists.") else: course_fs.put("course.yaml", get_json_or_yaml("course.yaml", init_content)) get_course_logger(courseid).info("Course %s created in the factory.", courseid) self._hook_manager.call_hook('course_created', courseid=courseid, new_content=init_content)
def create_course(self, courseid, init_content): """ :param courseid: the course id of the course :param init_content: initial descriptor content :raise InvalidNameException or CourseAlreadyExistsException Create a new course folder and set initial descriptor content, folder can already exist """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) course_directory = os.path.join(self._tasks_directory, courseid) if not os.path.exists(course_directory): os.mkdir(course_directory) base_file = os.path.join(course_directory, "course") if not os.path.isfile(base_file + ".yaml") and not os.path.isfile(base_file + ".json"): write_yaml(os.path.join(course_directory, "course.yaml"), init_content) else: raise CourseAlreadyExistsException("Course with id " + courseid + " already exists.") get_course_logger(courseid).info("Course %s created in the factory.", courseid)
def delete_task(self, courseid, taskid): """ Erase the content of the task folder :param courseid: the course id of the course :param taskid: the task id of the task :raise: InvalidNameException or CourseNotFoundException """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) task_fs = self.get_task_fs(courseid, taskid) if task_fs.exists(): task_fs.delete() get_course_logger(courseid).info("Task %s erased from the factory.", taskid)
def update_temporal_task_file(self, course, taskid, data): """ :param course: a Course object :param taskid: the task id of the task :param data: a Dict with the temporary data of the task to be stored :raise InvalidNameException, TaskReaderNotFoundException, TaskNotFoundException Create or Update a temporary task file that is used to store the task data that is required for some plugins """ if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) task_fs = self.get_task_fs(course.get_id(), taskid) task_file_manager = None try: for file_extension, file_manager in self._task_file_managers.items( ): if file_extension is "yaml": task_file_manager = file_manager except: raise TaskReaderNotFoundException() if task_file_manager: temporal_task_file_content = task_file_manager.dump(data) try: task_fs.put("task_temp.yaml", temporal_task_file_content) except: raise TaskNotFoundException()
def _cache_update_needed(self, course, taskid): """ :param course: a Course object :param taskid: a (valid) task id :raise InvalidNameException, TaskNotFoundException :return: True if an update of the cache is needed, False else """ if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) task_fs = self.get_task_fs(course.get_id(), taskid) if (course.get_id(), taskid) not in self._cache: return True try: last_update, __, __ = self._get_last_updates( course, taskid, task_fs, False) except: raise TaskNotFoundException() last_modif = self._cache[(course.get_id(), taskid)][1] for filename, mftime in last_update.items(): if filename not in last_modif or last_modif[filename] < mftime: return True return False
def get_temporal_task_file_content(self, course, taskid): """ :param course: a Course object :param taskid: the task id of the task :raise InvalidNameException, TaskUnreadableException :return: the content of the temporary task file """ if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) task_file_manager = None task_fs = self.get_task_fs(course.get_id(), taskid) for file_extension, file_manager in self._task_file_managers.items(): if task_fs.get("task_temp." + file_extension): task_file_manager = file_manager temporal_task_file_content = {} try: temporal_task_file_content["data"] = task_file_manager.load( task_fs.get("task_temp.yaml")) except Exception as e: raise TaskUnreadableException(str(e)) return temporal_task_file_content
def get_course_fs(self, courseid): """ :param courseid: :return: a FileSystemProvider pointing to the directory of the course """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) return self._filesystem.from_subfolder(courseid)
def get_task_descriptor_content(self, courseid, taskid): """ :param courseid: the course id of the course :param taskid: the task id of the task :raise InvalidNameException, TaskNotFoundException, TaskUnreadableException :return: the content of the task descriptor, as a dict """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) descriptor_path, descriptor_manager = self._get_task_descriptor_info(courseid, taskid) try: task_content = descriptor_manager.load(self.get_task_fs(courseid, taskid).get(descriptor_path)) except Exception as e: raise TaskUnreadableException(str(e)) return task_content
def get_task_descriptor_content(self, courseid, taskid): """ :param courseid: the course id of the course :param taskid: the task id of the task :raise InvalidNameException, TaskNotFoundException, TaskUnreadableException :return: the content of the task descriptor, as a dict """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) path_to_descriptor, _, descriptor_manager = self._get_task_descriptor_info(courseid, taskid) try: with codecs.open(path_to_descriptor, 'r', 'utf-8') as fd: task_content = descriptor_manager.load(fd.read()) except Exception as e: raise TaskUnreadableException(str(e)) return task_content
def _get_task_descriptor_info(self, courseid, taskid): """ :param courseid: the course id of the course :param taskid: the task id of the task :raise InvalidNameException, TaskNotFoundException :return: a tuple, containing: (descriptor filename, task file manager for the descriptor) """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) task_fs = self.get_task_fs(courseid, taskid) for ext, task_file_manager in self._task_file_managers.items(): if task_fs.exists("task." + ext): return "task." + ext, task_file_manager raise TaskNotFoundException()
def delete_task(self, courseid, taskid): """ :param courseid: the course id of the course :param taskid: the task id of the task :raise InvalidNameException or CourseNotFoundException Erase the content of the task folder """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) task_directory = os.path.join(self._tasks_directory, courseid, taskid) if not os.path.exists(task_directory): raise TaskNotFoundException() shutil.rmtree(task_directory) get_course_logger(courseid).info("Task %s erased from the factory.", taskid)
def get_course(self, courseid): """ :param courseid: the course id of the course :raise InvalidNameException, CourseNotFoundException, CourseUnreadableException :return: an object representing the course, of the type given in the constructor """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) if self._cache_update_needed(courseid): self._update_cache(courseid) return self._cache[courseid][0]
def _get_task_descriptor_info(self, courseid, taskid): """ :param courseid: the course id of the course :param taskid: the task id of the task :raise InvalidNameException, TaskNotFoundException :return: a tuple, containing: (the path to the descriptor of the task, extension of the descriptor, task file manager for the descriptor) """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) base_file = os.path.join(self._tasks_directory, courseid, taskid, "task") for ext, task_file_manager in self._task_file_managers.items(): if os.path.isfile(base_file + "." + ext): return base_file + "." + ext, ext, task_file_manager raise TaskNotFoundException()
def get_task(self, course, taskid): """ :param course: a Course object :param taskid: the task id of the task :raise InvalidNameException, TaskNotFoundException, TaskUnreadableException :return: an object representing the task, of the type given in the constructor """ if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) if self._cache_update_needed(course, taskid): self._update_cache(course, taskid) return self._cache[(course.get_id(), taskid)][0]
def update_task_descriptor_content(self, courseid, taskid, content, force_extension=None): """ Update the task descriptor with the dict in content :param courseid: the course id of the course :param taskid: the task id of the task :param content: the content to put in the task file :param force_extension: If None, save it the same format. Else, save with the given extension :raise InvalidNameException, TaskNotFoundException, TaskUnreadableException """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) if force_extension is None: path_to_descriptor, descriptor_manager = self._get_task_descriptor_info( courseid, taskid) elif force_extension in self.get_available_task_file_extensions(): path_to_descriptor = "task." + force_extension descriptor_manager = self._task_file_managers[force_extension] else: raise TaskReaderNotFoundException() try: self.get_task_fs(courseid, taskid).put(path_to_descriptor, descriptor_manager.dump(content)) except: raise TaskNotFoundException() self._hook_manager.call_hook('task_updated', courseid=courseid, taskid=taskid, new_content=content)
def _get_course_descriptor_path(self, courseid): """ :param courseid: the course id of the course :raise InvalidNameException, CourseNotFoundException :return: the path to the descriptor of the course """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) course_fs = self.get_course_fs(courseid) if course_fs.exists("course.yaml"): return courseid + "/course.yaml" if course_fs.exists("course.json"): return courseid + "/course.json" raise CourseNotFoundException()
def _get_template_descriptor_path(self, courseid): """ [Source code integration]: generalise with get_course_descriptor_path :param courseid: the course id of the course :raise InvalidNameException, CourseNotFoundException :return: the path to the description of the template """ if not id_checker(courseid): raise InvalidNameException("Template with invalid name: " + courseid) course_fs = self.get_course_fs(courseid) if course_fs.exists("template.yaml"): return courseid + "/template.yaml" raise CourseNotFoundException()
def delete_task(self, courseid, taskid): """ :param courseid: the course id of the course :param taskid: the task id of the task :raise InvalidNameException or CourseNotFoundException Erase the content of the task folder """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) task_fs = self.get_task_fs(courseid, taskid) if not task_fs.exists(): raise TaskNotFoundException() task_fs.delete() get_course_logger(courseid).info("Task %s erased from the factory.", taskid) self._hook_manager.call_hook('task_deleted', courseid=courseid, taskid=taskid)
def update_toc_content(course_factory, courseid, content): """ Updates the content of the structure that describes the course :param course_factory: the course factory :param courseid: the course id of the course :param content: the new structure that replaces the old content :raise InvalidNameException, CourseNotFoundException """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) get_sections_ids_and_make_unique(content) course_factory.get_course_fs(courseid).put( "toc.yaml", get_json_or_yaml("toc.yaml", content))
def _get_course_descriptor_path(self, courseid): """ :param courseid: the course id of the course :raise InvalidNameException, CourseNotFoundException :return: the path to the descriptor of the course """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) base_file = os.path.join(self._tasks_directory, courseid, "course") if os.path.isfile(base_file + ".yaml"): return base_file + ".yaml" elif os.path.isfile(base_file + ".json"): return base_file + ".json" else: raise CourseNotFoundException()
def get_temporal_task_file(self, course, taskid): """ :param course: a Course object :param taskid: the task id of the task :raise InvalidNameException :return: the temporary task file in the task folder """ if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) task_fs = self.get_task_fs(course.get_id(), taskid) try: temporal_task_file = task_fs.get("task_temp.yaml") except: temporal_task_file = None return temporal_task_file
def _update_cache(self, course, taskid): """ Updates the cache :param course: a Course object :param taskid: a (valid) task id :raise InvalidNameException, TaskNotFoundException, TaskUnreadableException """ if not id_checker(taskid): raise InvalidNameException("Task with invalid name: " + taskid) task_fs = self.get_task_fs(course.get_id(), taskid) last_modif, translation_fs, task_content = self._get_last_updates( course, taskid, task_fs, True) self._cache[(course.get_id(), taskid)] = (self._task_class( course, taskid, task_content, task_fs, translation_fs, self._hook_manager, self._task_problem_types), last_modif)
def delete_course(self, courseid): """ :param courseid: the course id of the course :raise InvalidNameException or CourseNotFoundException Erase the content of the course folder """ if not id_checker(courseid): raise InvalidNameException("Course with invalid name: " + courseid) course_fs = self.get_course_fs(courseid) if not course_fs.exists(): raise CourseNotFoundException() course_fs.delete() get_course_logger(courseid).info("Course %s erased from the factory.", courseid)