def test_write_string(self): d = OrderedDict([("the", "a"), ("order", "z"), ("is", "b"), ("important", "y")]) string = yaml.dump(d) loaded = yaml.load(string) assert type(loaded) == OrderedDict assert loaded.keys() == ["the", "order", "is", "important"]
def show_page(self, page): static_directory = self.app.static_directory language = self.user_manager.session_language() # Check for the file filename = None filepaths = [os.path.join(static_directory, page + ".yaml"), os.path.join(static_directory, page + "." + language + ".yaml")] for filepath in filepaths: if os.path.exists(filepath): filename = filepath mtime = os.stat(filepath).st_mtime if not filename: raise web.notfound() # Check and update cache if INGIniousStaticPage.cache.get(filepath, (0, None))[0] < mtime: with open(filename, "r") as f: INGIniousStaticPage.cache[filepath] = mtime, custom_yaml.load(f) filecontent = INGIniousStaticPage.cache[filepath][1] title = filecontent["title"] content = ParsableText.rst(filecontent["content"], initial_header_level=2) return self.template_helper.get_renderer().static(title, content)
def test_write_ordereddict(self): d = OrderedDict([("the", "a"), ("order", "z"), ("is", "b"), ("important", "y")]) yaml.dump(d, open(os.path.join(self.dir_path, "output.yaml"), "w")) loaded = yaml.load(open(os.path.join(self.dir_path, "output.yaml"), "r")) assert type(loaded) == OrderedDict assert loaded.keys() == ["the", "order", "is", "important"]
def POST_AUTH(self, courseid, classroomid): # pylint: disable=arguments-differ """ Edit a classroom """ course, __ = self.get_course_and_check_rights(courseid, allow_all_staff=True) if course.is_lti(): raise web.notfound() error = False data = web.input(tutors=[], groups=[], classroomfile={}) if "delete" in data: # Get the classroom classroom = self.database.classrooms.find_one({"_id": ObjectId(classroomid), "courseid": courseid}) if classroom is None: msg = _("Classroom not found.") error = True elif classroom['default']: msg = _("You can't remove your default classroom.") error = True else: self.database.classrooms.find_one_and_update({"courseid": courseid, "default": True}, {"$push": { "students": {"$each": classroom["students"]} }}) self.database.classrooms.delete_one({"_id": ObjectId(classroomid)}) raise web.seeother(self.app.get_homepath() + "/admin/" + courseid + "/classrooms") else: try: if "upload" in data: new_data = custom_yaml.load(data["classroomfile"].file) else: # Prepare classroom-like data structure from input new_data = {"description": data["description"], "tutors": data["tutors"], "students": [], "groups": []} for index, groupstr in enumerate(data["groups"]): group = json.loads(groupstr) new_data["students"].extend(group["students"]) if index != 0: new_data["groups"].append(group) classroom, errored_students = self.update_classroom(course, classroomid, new_data) student_list, tutor_list, other_students, users_info = self.get_user_lists(course, classroom["_id"]) if len(errored_students) > 0: msg = _("Changes couldn't be applied for following students :") + "<ul>" for student in errored_students: msg += "<li>" + student + "</li>" msg += "</ul>" error = True else: msg = _("Classroom updated.") except: classroom = self.database.classrooms.find_one({"_id": ObjectId(classroomid), "courseid": courseid}) student_list, tutor_list, other_students, users_info = self.get_user_lists(course, classroom["_id"]) msg = _('An error occurred while parsing the data.') error = True return self.template_helper.get_renderer().course_admin.edit_classroom(course, student_list, tutor_list, other_students, users_info, classroom, msg, error)
def test_load_string(self): loaded = yaml.load(""" the: a order: z of: b the_: y keys: c is: x important: d """) assert type(loaded) == OrderedDict assert loaded.keys() == ["the", "order", "of", "the_", "keys", "is", "important"]
def test_load_ordereddict(self): open(os.path.join(self.dir_path, "input.yaml"), "w").write(""" the: a order: z of: b the_: y keys: c is: x important: d """) loaded = yaml.load(open(os.path.join(self.dir_path, "input.yaml"), "r")) assert type(loaded) == OrderedDict assert loaded.keys() == ["the", "order", "of", "the_", "keys", "is", "important"]
def extract_yaml(fileobj, keywords, comment_tags, options): source = fileobj.read().decode(options.get('encoding', 'utf-8')) content = custom_yaml.load(source) if "task.yaml" in fileobj.name: keys = ["author", "context", "name"] for key in keys: yield 0, "", content.get(key, ""), [key] for problem_id, problem_content in content.get("problems").items(): task_problem_types = {"code": CodeProblem, "code_single_line": CodeSingleLineProblem, "file": FileProblem, "multiple_choice": MultipleChoiceProblem, "match": MatchProblem} fields = task_problem_types.get(problem_content.get('type', "")).get_text_fields() for string, strkey in get_strings(content.get("problems").get(problem_id), fields): yield 0, "", string, [key + ", " + problem_id + ", " + strkey] elif "course.yaml" in fileobj.name: yield 0, "", content.get("name", ""), ["name"]
def post_groups(self, course, data, active_tab, msg, error): if course.is_lti(): return active_tab audience_list = self.user_manager.get_course_audiences(course) audience_students = {} for audience in audience_list: for stud in audience["students"]: audience_students.setdefault(stud, []).append(audience["_id"]) errored_students = [] if len(data["delete"]): for classid in data["delete"]: # Get the group group = self.database.groups.find_one( { "_id": ObjectId(classid), "courseid": course.get_id() }) if ObjectId.is_valid(classid) else None if group is None: msg["groups"] = ( "group with id {} not found.").format(classid) error["groups"] = True else: self.database.groups.find_one_and_update( {"courseid": course.get_id()}, {"$push": { "students": { "$each": group["students"] } }}) self.database.groups.delete_one({"_id": ObjectId(classid)}) msg["groups"] = _("Audience updated.") active_tab = "tab_groups" if "upload_groups" in data or "groups" in data: try: if "upload_groups" in data: self.database.groups.delete_many( {"courseid": course.get_id()}) groups = custom_yaml.load(data["groupfile"].file) else: groups = json.loads(data["groups"]) for index, new_group in enumerate(groups): # In case of file upload, no id specified new_group['_id'] = new_group[ '_id'] if '_id' in new_group else 'None' # Update the group group, errors = self.update_group(course, new_group['_id'], new_group, audience_students) errored_students += errors if len(errored_students) > 0: msg["groups"] = _( "Changes couldn't be applied for following students :" ) + "<ul>" for student in errored_students: msg["groups"] += "<li>" + student + "</li>" msg["groups"] += "</ul>" error["groups"] = True elif not error: msg["groups"] = _("Groups updated.") except: msg["groups"] = _('An error occurred while parsing the data.') error["groups"] = True active_tab = "tab_groups" return active_tab
def POST_AUTH(self, courseid, audienceid=''): # pylint: disable=arguments-differ """ Edit a audience """ course, __ = self.get_course_and_check_rights(courseid, allow_all_staff=True) msg = '' error = False errored_students = [] data = web.input(delete=[], tutors=[], audiencefile={}) if len(data["delete"]): for classid in data["delete"]: # Get the audience audience = self.database.audiences.find_one( { "_id": ObjectId(classid), "courseid": courseid }) if ObjectId.is_valid(classid) else None if audience is None: msg = _("Audience with id {} not found.").format(classid) error = True else: self.database.audiences.find_one_and_update( {"courseid": courseid}, { "$push": { "students": { "$each": audience["students"] } } }) self.database.audiences.delete_one( {"_id": ObjectId(classid)}) msg = _("Audience updated.") if audienceid and audienceid in data["delete"]: raise web.seeother(self.app.get_homepath() + "/admin/" + courseid + "/audiences") try: if "upload" in data: self.database.audiences.delete_many( {"courseid": course.get_id()}) audiences = custom_yaml.load(data["audiencefile"].file) else: audiences = json.loads(data["audiences"]) for index, new_audience in enumerate(audiences): # In case of file upload, no id specified new_audience['_id'] = new_audience[ '_id'] if '_id' in new_audience else 'None' # Update the audience audience, errors = self.update_audience( course, new_audience['_id'], new_audience) # If file upload was done, get the default audience id audienceid = audience['_id'] errored_students += errors if len(errored_students) > 0: msg = _("Changes couldn't be applied for following students :" ) + "<ul>" for student in errored_students: msg += "<li>" + student + "</li>" msg += "</ul>" error = True elif not error: msg = _("Audience updated.") except: msg = _('An error occurred while parsing the data.') error = True # Display the page return self.display_page(course, audienceid, msg, error)
def POST_AUTH(self, courseid, aggregationid=''): # pylint: disable=arguments-differ """ Edit a aggregation """ course, _ = self.get_course_and_check_rights(courseid, allow_all_staff=True) if course.is_lti(): raise web.notfound() msg = '' error = False errored_students = [] data = web.input(delete=[], tutors=[], groups=[], aggregationfile={}) if len(data["delete"]): for classid in data["delete"]: # Get the aggregation aggregation = self.database.aggregations.find_one( { "_id": ObjectId(classid), "courseid": courseid }) if ObjectId.is_valid(classid) else None if aggregation is None: msg = "Classroom" if course.use_classrooms( ) else "Team" + " with id " + classid + "not found." error = True elif aggregation['default'] and aggregationid: msg = "You can't remove your default classroom." error = True else: self.database.aggregations.find_one_and_update( { "courseid": courseid, "default": True }, { "$push": { "students": { "$each": aggregation["students"] } } }) self.database.aggregations.delete_one( {"_id": ObjectId(classid)}) msg = "Classroom updated." if aggregationid and aggregationid in data["delete"]: raise web.seeother(self.app.get_homepath() + "/admin/" + courseid + "/aggregations") try: if "upload" in data: self.database.aggregations.delete_many( {"courseid": course.get_id()}) aggregations = custom_yaml.load(data["aggregationfile"].file) else: aggregations = json.loads(data["aggregations"]) for index, new_aggregation in enumerate(aggregations): # In case of file upload, no id specified new_aggregation['_id'] = new_aggregation[ '_id'] if '_id' in new_aggregation else 'None' # In case of no aggregation usage, set the first entry default if not aggregationid and index == 0: new_aggregation["default"] = True # If no groups field set, create group from class students if in groups only mode if "groups" not in new_aggregation: new_aggregation["groups"] = [] if aggregationid else [{ 'size': len(new_aggregation['students']), 'students': new_aggregation['students'] }] # Update the aggregation aggregation, errors = self.update_aggregation( course, new_aggregation['_id'], new_aggregation) # If file upload was done, get the default aggregation id if course.use_classrooms() and aggregation['default']: aggregationid = aggregation['_id'] errored_students += errors if len(errored_students) > 0: msg = "Changes couldn't be applied for following students : <ul>" for student in errored_students: msg += "<li>" + student + "</li>" msg += "</ul>" error = True elif not error: msg = "Classroom updated." if course.use_classrooms( ) else "Teams updated." except: msg = 'An error occurred while parsing the data.' error = True # Display the page return self.display_page(course, aggregationid, msg, error)
def post_audiences(self, course, data, active_tab, msg, error): try: if 'audience' in data: if self.user_manager.has_admin_rights_on_course(course): self.database.audiences.insert({ "courseid": course.get_id(), "students": [], "tutors": [], "description": data['audience'] }) msg["audiences"] = _("New audience created.") else: msg["audiences"] = _( "You have no rights to add/change audiences") error["audiences"] = True active_tab = "tab_audiences" except: msg["audiences"] = _('User returned an invalid form.') error["audiences"] = True active_tab = "tab_audiences" try: if "upload_audiences" in data or "audiences" in data: errored_students = [] if "upload_audiences" in data: self.database.audiences.delete_many( {"courseid": course.get_id()}) audiences = custom_yaml.load(data["audiencefile"].file) else: audiences = json.loads(data["audiences"]) for index, new_audience in enumerate(audiences): # In case of file upload, no id specified new_audience['_id'] = new_audience[ '_id'] if '_id' in new_audience else 'None' # Update the audience audience, errors = self.update_audience( course, new_audience['_id'], new_audience) # If file upload was done, get the default audience id audienceid = audience['_id'] errored_students += errors if len(errored_students) > 0: msg["audiences"] = _( "Changes couldn't be applied for following students :" ) + "<ul>" for student in errored_students: msg["audiences"] += "<li>" + student + "</li>" msg["audiences"] += "</ul>" error["audiences"] = True elif not error: msg["audiences"] = _("Audience updated.") active_tab = "tab_audiences" except Exception: msg["audiences"] = _('An error occurred while parsing the data.') error["audiences"] = True active_tab = "tab_audiences" return active_tab
def POST_AUTH(self, courseid, classroomid): # pylint: disable=arguments-differ """ Edit a classroom """ course, _ = self.get_course_and_check_rights(courseid, allow_all_staff=True) error = False data = web.input(tutors=[], groups=[], classroomfile={}) if "delete" in data: # Get the classroom classroom = self.database.classrooms.find_one({ "_id": ObjectId(classroomid), "courseid": courseid }) if classroom is None: msg = "Classroom not found." error = True elif classroom['default']: msg = "You can't remove your default classroom." error = True else: self.database.classrooms.find_one_and_update( { "courseid": courseid, "default": True }, {"$push": { "students": { "$each": classroom["students"] } }}) self.database.classrooms.delete_one( {"_id": ObjectId(classroomid)}) raise web.seeother("/admin/" + courseid + "/classrooms") else: try: if "upload" in data: new_data = custom_yaml.load(data["classroomfile"].file) else: # Prepare classroom-like data structure from input new_data = { "description": data["description"], "tutors": data["tutors"], "students": [], "groups": [] } for index, groupstr in enumerate(data["groups"]): group = json.loads(groupstr) new_data["students"].extend(group["students"]) if index != 0: new_data["groups"].append(group) classroom, errored_students = self.update_classroom( course, classroomid, new_data) student_list, tutor_list, other_students, users_info = self.get_user_lists( course, classroom["_id"]) if len(errored_students) > 0: msg = "Changes couldn't be applied for following students : <ul>" for student in errored_students: msg += "<li>" + student + "</li>" msg += "</ul>" error = True else: msg = "Classroom updated." except: classroom = self.database.classrooms.find_one({ "_id": ObjectId(classroomid), "courseid": courseid }) student_list, tutor_list, other_students, users_info = self.get_user_lists( course, classroom["_id"]) msg = 'An error occurred while parsing the data.' error = True return self.template_helper.get_renderer().course_admin.edit_classroom( course, student_list, tutor_list, other_students, users_info, classroom, msg, error)
def POST_AUTH(self, courseid, groupid=''): # pylint: disable=arguments-differ """ Edit a group """ course, __ = self.get_course_and_check_rights(courseid, allow_all_staff=True) if course.is_lti(): raise web.notfound() audience_list = self.user_manager.get_course_audiences(course) audience_students = {} for audience in audience_list: for stud in audience["students"]: audience_students.setdefault(stud, []).append(audience["_id"]) msg = '' error = False errored_students = [] data = web.input(delete=[], groupfile={}) if len(data["delete"]): for classid in data["delete"]: # Get the group group = self.database.groups.find_one({ "_id": ObjectId(classid), "courseid": courseid }) if ObjectId.is_valid(classid) else None if group is None: msg = ("group with id {} not found.").format(classid) error = True else: self.database.groups.find_one_and_update( {"courseid": courseid}, {"$push": { "students": { "$each": group["students"] } }}) self.database.groups.delete_one({"_id": ObjectId(classid)}) msg = _("Audience updated.") if groupid and groupid in data["delete"]: raise web.seeother(self.app.get_homepath() + "/admin/" + courseid + "/groups") try: if "upload" in data: self.database.groups.delete_many({"courseid": course.get_id()}) groups = custom_yaml.load(data["groupfile"].file) else: groups = json.loads(data["groups"]) for index, new_group in enumerate(groups): # In case of file upload, no id specified new_group[ '_id'] = new_group['_id'] if '_id' in new_group else 'None' # Update the group group, errors = self.update_group(course, new_group['_id'], new_group, audience_students) errored_students += errors if len(errored_students) > 0: msg = _("Changes couldn't be applied for following students :" ) + "<ul>" for student in errored_students: msg += "<li>" + student + "</li>" msg += "</ul>" error = True elif not error: msg = _("Groups updated.") except: raise msg = _('An error occurred while parsing the data.') error = True # Display the page return self.display_page(course, msg, error)
def import_elements(self, course, data): imported_tasks, renamed_tasks = [], {} errors = [] try: with tarfile.open(fileobj=data["archive_file"].file) as tar: elements = tar.getnames() if "import_inginious_file" in data: # import tasks for taskid in get_tasks_id(elements): try: # get files task_files = get_task_files(taskid, elements) tar_members = [ tar.getmember(task_file) for task_file in task_files ] # avoid tasks with same id new_taskid = make_id_unique( taskid, course.get_tasks()) if taskid != new_taskid: renamed_tasks[taskid] = new_taskid for tar_member in tar_members: tar_member.name = new_taskid + tar_member.name.lstrip( taskid) # extract files tar.extractall(course.get_fs().prefix, tar_members) # check task descriptor is in inginious format and remove tags task_descriptor = self.task_factory.get_task_descriptor_content( course.get_id(), new_taskid) if not check_task_descriptor(task_descriptor): raise TaskUnreadableException( "Invalid task config format") task_descriptor["categories"] = [] self.task_factory.update_task_descriptor_content( course.get_id(), taskid, task_descriptor) imported_tasks.append(new_taskid) except: errors.append( _("Invalid format for task ") + taskid) if new_taskid in course.get_tasks(): self.task_factory.delete_task( course.get_id(), new_taskid) # import sections if "sections.yaml" in elements: try: from inginious.frontend.plugins.course_structure.webapp_course import \ get_sections_tasks_ids, replace_sections_tasks_ids, get_toc, update_toc_content sections_content = load( tar.extractfile("sections.yaml").read()) replace_sections_tasks_ids(sections_content, renamed_tasks) # if all tasks in the sections are imported, add sections to course structure and adapt rank if all(elem in imported_tasks for elem in get_sections_tasks_ids(sections_content)): toc = get_toc(course) for section in sections_content: section["rank"] = len(toc) toc.append(section) update_toc_content(self.course_factory, course.get_id(), toc) else: errors.append( _("Some tasks in the structure are not included in the archive" )) except: errors.append( _("Sections not supported for this instance")) except Exception as e: print() errors.append(_("Invalid archive format")) return self.page(course, errors, None if errors else _("Import successful."))
def POST_AUTH(self, courseid, aggregationid=''): # pylint: disable=arguments-differ """ Edit a aggregation """ course, __ = self.get_course_and_check_rights(courseid, allow_all_staff=True) if course.is_lti(): raise web.notfound() msg='' error = False errored_students = [] data = web.input(delete=[], tutors=[], groups=[], aggregationfile={}) if len(data["delete"]): for classid in data["delete"]: # Get the aggregation aggregation = self.database.aggregations.find_one({"_id": ObjectId(classid), "courseid": courseid}) if ObjectId.is_valid(classid) else None if aggregation is None: msg = _("Classroom with id {} not found.").format(classid) if course.use_classrooms() else _("Team with id {} not found.").format(classid) error = True elif aggregation['default'] and aggregationid: msg = _("You can't remove your default classroom.") error = True else: self.database.aggregations.find_one_and_update({"courseid": courseid, "default": True}, {"$push": { "students": {"$each": aggregation["students"]} }}) self.database.aggregations.delete_one({"_id": ObjectId(classid)}) msg = _("Classroom updated.") if aggregationid and aggregationid in data["delete"]: raise web.seeother(self.app.get_homepath() + "/admin/" + courseid + "/aggregations") try: if "upload" in data: self.database.aggregations.delete_many({"courseid": course.get_id()}) aggregations = custom_yaml.load(data["aggregationfile"].file) else: aggregations = json.loads(data["aggregations"]) for index, new_aggregation in enumerate(aggregations): # In case of file upload, no id specified new_aggregation['_id'] = new_aggregation['_id'] if '_id' in new_aggregation else 'None' # In case of no aggregation usage, set the first entry default new_aggregation["default"] = not aggregationid and index == 0 # If no groups field set, create group from class students if in groups only mode if "groups" not in new_aggregation: new_aggregation["groups"] = [] if aggregationid else [{'size': len(new_aggregation['students']), 'students': new_aggregation['students']}] # Update the aggregation aggregation, errors = self.update_aggregation(course, new_aggregation['_id'], new_aggregation) # If file upload was done, get the default aggregation id if course.use_classrooms() and aggregation['default']: aggregationid = aggregation['_id'] errored_students += errors if len(errored_students) > 0: msg = _("Changes couldn't be applied for following students :") + "<ul>" for student in errored_students: msg += "<li>" + student + "</li>" msg += "</ul>" error = True elif not error: msg = _("Classroom updated.") if course.use_classrooms() else _("Teams updated.") except: msg = _('An error occurred while parsing the data.') error = True # Display the page return self.display_page(course, aggregationid, msg, error)