# -*- mode: python -*- # -*- coding: utf-8 -*- ############################################################################## # # Gestion scolarite IUT # # Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Emmanuel Viennet emmanuel.viennet@viennet.net # ############################################################################## """Importation de notes depuis Moodle Contrib. Pierre-Alain Jacquot, mai 2021 """ # QUESTION: un (long) commentaire expliquant le principe de base de ce module import requests import re def cleanhtml(raw_html): cleanr = re.compile("<.*?>") cleantext = re.sub(cleanr, " ", raw_html) cleantext = cleantext.strip() cleantext = cleantext.encode("utf-8") return cleantext # def get_moodle_course_id(moodle_serveur, moodle_token, courses_short_name): param_cours = { "wstoken": moodle_token, "moodlewsrestformat": "json", "wsfunction": "core_course_get_courses_by_field", "field": "shortname", "value": courses_short_name, } try: r = requests.post(url=moodle_serveur, data=param_cours).json() except ValueError: raise ValueError("Erreur de connexion vérifiez l'URL de Moodle") if "exception" in r: raise ValueError( "Connexion au service web de Moodle impossible %s : Vérifiez votre paramétrage" % r["message"] ) if len(r["courses"]) == 0: courseid = 0 else: courseid = r["courses"][0]["id"] return courseid def has_student_role(user): """ Retourne vrai si l'utilisateur a le role 5 : «etudiant» ou «student» dans le cours i.e. il a des notes """ # QUESTION: ce nombre "5" est une constante universelle dans Moodle ? est_etudiant = False for role in user["roles"]: # print "role : "+str(role['roleid'] ) if role["roleid"] == 5: # print "est_etudiant "+str(role['roleid'] ) est_etudiant = 1 return est_etudiant def get_etudiants_from_course(moodle_serveur, moodle_token, courses_short_name): """ Extrait la liste des étudiants des utilisateurs inscrit dans le cours. Cette liste contient les informations suivante : - id moodle - email - idnumber (numéro d'identification) s'il existe : celui ci peut servir a stocker le EID ou le nip """ courseid = get_moodle_course_id(moodle_serveur, moodle_token, courses_short_name) param_cours = { "wstoken": moodle_token, "moodlewsrestformat": "json", "wsfunction": "core_enrol_get_enrolled_users", "options[0][name]": "onlyactive", "options[0][value]": "1", "options[1][name]": "userfields", "options[1][value]": "id,email,idnumber,roles", "courseid": courseid, } r = requests.post(url=moodle_serveur, data=param_cours).json() etudiants = [user for user in r if has_student_role(user)] # le role n'est plus une information pertinente : suppression for etudiant in etudiants: del etudiant["roles"] etudiant["email"] = etudiant["email"].encode("ascii").lower() return etudiants def get_evaluation_list(moodle_serveur, moodle_token, courses_short_name): """ Récupère la liste des evaluations du cours Moodle On recherche les notes d'un seul etudiant pour gagner du temps. """ # QUESTION: documenter les valeurs résultats etudiants = get_etudiants_from_course( moodle_serveur, moodle_token, courses_short_name ) a_userid = etudiants[0]["id"] courseid = get_moodle_course_id(moodle_serveur, moodle_token, courses_short_name) param_notes = { "wstoken": moodle_token, "moodlewsrestformat": "json", "wsfunction": "gradereport_user_get_grades_table", "courseid": courseid, "userid": a_userid, } r = requests.post(url=moodle_serveur, data=param_notes) notes = r.json() bareme = {} liste_evals = [] for etu_notes in notes["tables"][0]["tabledata"]: if "grade" in etu_notes: nom_eval = cleanhtml(etu_notes["itemname"]["content"]) liste_evals.append(nom_eval) bareme_min, bareme_max = etu_notes["range"]["content"].split("–") bareme[nom_eval] = { "min": float(bareme_min.replace(",", ".")), "max": float(bareme_max.replace(",", ".")), } return liste_evals, bareme def get_grades_from_moodle_course(moodle_serveur, moodle_token, courses_short_name): """ Récupère toutes les notes du cours et les remet en forme dans un dictionnaire indexé par le userid de moodle {userid: { nom_eval:note, ...}} """ courseid = get_moodle_course_id(moodle_serveur, moodle_token, courses_short_name) param_notes = { "wstoken": moodle_token, "moodlewsrestformat": "json", "wsfunction": "gradereport_user_get_grades_table", "courseid": courseid, } r = requests.post(url=moodle_serveur, data=param_notes) notes = r.json() notes_evals = {} for etudiant in notes["tables"]: # remise en forme des notes dans un dictionnaire indexe par le nom de la note tab_notes = {} for etu_notes in etudiant["tabledata"]: if "grade" in etu_notes: if etu_notes["grade"]["content"] == "-": etu_notes["grade"]["content"] = "SUPR" tab_notes[cleanhtml(etu_notes["itemname"]["content"])] = etu_notes[ "grade" ]["content"] notes_evals[etudiant["userid"]] = tab_notes return notes_evals # QUESTION: j'ai l'impression qu'il y a trop de code en commun entre cette fonction et # sco_saisie_notes._form_saisie_notes # QUESTION: manque vérification de la présence de décisions de jury ?? (qui devrait bloquer l'import amha) def import_eval_notes_from_moodle(context, evaluation_id, group_ids=[], REQUEST=None): """Récuperation des notes sur moodle""" moodle_token = context.get_preference("moodle_ws_token", formsemestre_id) moodle_serveur = context.get_preference("moodle_server_url", formsemestre_id) # Désactive si l'interface n'est pas configurée: if not moodle_serveur or not moodle_token: return "Interface Moodle non paramétrée !" authuser = REQUEST.AUTHENTICATED_USER evals = context.do_evaluation_list({"evaluation_id": evaluation_id}) if not evals: raise ScoValueError("invalid evaluation_id") E = evals[0] M = context.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] # M = context.do_moduleimpl_list( args={ 'moduleimpl_id' : E['moduleimpl_id'] } )[0] formsemestre_id = M["formsemestre_id"] if not can_edit_notes(context, authuser, E["moduleimpl_id"]): return ( context.sco_header(REQUEST) + "
(vérifiez que le semestre n'est pas verrouillé et que vous avez l'autorisation d'effectuer cette opération)
""" % E["moduleimpl_id"] + context.sco_footer(REQUEST) ) if E["description"]: page_title = 'Saisie des notes de "%s"' % E["description"] else: page_title = "Saisie des notes" # Informations sur les groupes à afficher: groups_infos = sco_groups_view.DisplayedGroupsInfos( context, group_ids=group_ids, formsemestre_id=formsemestre_id, select_all_when_unspecified=True, etat=None, REQUEST=REQUEST, ) H = [ context.sco_header( REQUEST, page_title=page_title, javascripts=sco_groups_view.JAVASCRIPTS, cssstyles=sco_groups_view.CSSSTYLES, init_qtip=True, ), sco_evaluations.evaluation_describe( context, evaluation_id=evaluation_id, REQUEST=REQUEST ), """Import des notes depuis Moodle""", ] H.append( """" %s " n'est pas un nom abrégé de cours connu sur ce Moodle
""" % course_short_name ) else: list_evaluations, bareme = get_evaluation_list( moodle_serveur, moodle_token, course_short_name ) msg = "Remarque : Si l'étudiant n'a pas de note sur Moodle la note dans cette évaluation sera supprimée
" if len(list_evaluations) > 5: msg += "ATTENTION : Le chargement des notes peut prendre beaucoup de temps
" H.append( """Notes chargées. Revenir au tableau de bord du module Charger d'autres notes dans cette évaluation
""" % E ) else: H.append( """Notes non chargées !
""" + updiag[1] ) H.append( """ """ % E ) # H.append("""%d notes changées (%d sans notes, %d absents, %d note supprimées)
" % (nb_changed, len(withoutnotes), len(absents), nb_suppress) ) if existing_decisions: msg += """Important: il y avait déjà des décisions de jury enregistrées, qui sont potentiellement à revoir suite à cette modification !
""" # msg += '' + str(notes) # debug return 1, msg except InvalidNoteValue: if diag: msg = ( '
(pas de notes modifiées)
"