SPB Git

Repository Public Research preview

moodle_feedback.py 3.7 KB 50 lines python

1#!/usr/bin/env python32"""Note + commentaire + fichiers de rétroaction sur un devoir Moodle@UQO via Safari (grader Moodle 4).3Usage : moodle_feedback.py <cmid> <userid> <note ex. 8,50> "<commentaire html>" fichier1 [fichier2 ...]4Recette 2026-10-28 : page action=grader&userid=… ; fichiers → repository_ajax.php?action=upload (repo_id 3, itemid du filemanager5files_<userid>_filemanager, client_id = id du .filemanager) ; note dans #id_grade ; commentaire dans TinyMCE ; bouton name=savechanges."""6import base64, json, os, subprocess, sys7sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))8from moodle_grades import step, osa, as_quote910def upload_one(path):11    b64 = base64.b64encode(open(path, "rb").read()).decode()12    name = os.path.basename(path)13    js = f'''(async function(){{try{{14var fmInput=document.querySelector('input[name$="_filemanager"]'); var it=fmInput.value;15var fm=document.querySelector('.filemanager'); var cid=fm.id.replace('filemanager-','');16var ctx=M.cfg.contextid, sk=M.cfg.sesskey;17var bin=atob({json.dumps(b64)}); var arr=new Uint8Array(bin.length); for(var i=0;i<bin.length;i++)arr[i]=bin.charCodeAt(i);18var blob=new Blob([arr]);19var fd=new FormData(); fd.append('repo_upload_file',blob,{json.dumps(name)}); fd.append('sesskey',sk); fd.append('repo_id','3'); fd.append('itemid',it); fd.append('savepath','/'); fd.append('title',''); fd.append('author',''); fd.append('license','allrightsreserved'); fd.append('ctx_id',ctx); fd.append('client_id',cid); fd.append('overwrite','1');20var up=await (await fetch('/repository/repository_ajax.php?action=upload',{{method:'POST',body:fd,credentials:'same-origin'}})).json();21window.__res=JSON.stringify(up);}}catch(e){{window.__res='ERROR '+e;}}}})();'''22    return step("", js, delay=3, poll=10)2324def set_and_save(grade, comment):25    js = f'''(async function(){{try{{26document.querySelector('#id_grade').value={json.dumps(grade)};27document.querySelector('#id_grade').dispatchEvent(new Event('change',{{bubbles:true}}));28var ed=window.tinyMCE&&tinyMCE.get('id_assignfeedbackcomments_editor'); if(ed) ed.setContent({json.dumps(comment)}); else document.querySelector('#id_assignfeedbackcomments_editor').value={json.dumps(comment)};29var ta=document.querySelector('#id_assignfeedbackcomments_editor'); if(ed) ta.value=ed.getContent();30await new Promise(r=>setTimeout(r,800));31document.querySelector('button[name="savechanges"]').click();32await new Promise(r=>setTimeout(r,6000));33var g=document.querySelector('#id_grade'); var status=(document.querySelector('[data-region="grade"] .alert, .alert-success, [data-region="status"]')||{{textContent:''}}).textContent.trim();34window.__res=JSON.stringify({{grade_now:g?g.value:null, status:status.slice(0,120)}});}}catch(e){{window.__res='ERROR '+e;}}}})();'''35    return step("", js, delay=8, poll=10)3637def feedback(cmid, userid, grade, comment, files):38    url = f"https://moodle.uqo.ca/mod/assign/view.php?id={cmid}&action=grader&userid={userid}"39    chk = step(url, "window.__res=(document.querySelector('#id_grade')&&document.querySelector('input[name$=\"_filemanager\"]'))?'READY '+document.querySelector('input[name$=\"_filemanager\"]').name:'NOFORM';", delay=6, poll=10)40    if not chk.startswith("READY"): raise SystemExit("grader non chargé : " + chk)41    if f"files_{userid}_filemanager" not in chk: raise SystemExit("mauvais utilisateur dans le formulaire : " + chk)42    ups = [upload_one(f) for f in files]43    res = set_and_save(grade, comment)44    return chk, ups, res4546if __name__ == "__main__":47    cmid, userid, grade, comment, *files = sys.argv[1:]48    chk, ups, res = feedback(cmid, userid, grade, comment, files)49    print(chk); [print("  upload:", u[:160]) for u in ups]; print("  save:", res[:200])50
Files main
Menu

Theme