SPB Git

Repository Public Research preview

moodle_grades.py 3.1 KB 75 lines python

1#!/usr/bin/env python32"""Lit le carnet de notes Moodle@UQO (rapport de l'évaluateur) en pilotant Safari — réutilise la recette de moodle_publish.py.3Usage : moodle_grades.py <course_id> > notes.json"""4import json, subprocess, sys56MOODLE = "https://moodle.uqo.ca"78def osa(script):9    r = subprocess.run(["osascript", "-"], input=script, capture_output=True, text=True, encoding="utf-8")10    if r.returncode != 0:11        raise SystemExit("osascript: " + r.stderr.strip())12    return r.stdout.strip()1314def as_quote(s):15    return '"' + s.replace('\\', '\\\\').replace('"', '\\"') + '"'1617def step(url, js, delay=6, poll=15):18    nav = f'set URL of current tab of front window to "{url}"\n  delay 8' if url else ""19    script = f'''tell application "Safari"20  {nav}21  do JavaScript {as_quote("window.__res=null;" + js)} in current tab of front window22  delay {delay}23  set out to ""24  repeat {poll} times25    set out to do JavaScript "window.__res===null?'':String(window.__res)" in current tab of front window26    if out is not "" then exit repeat27    delay 228  end repeat29  return out30end tell'''31    return osa(script)3233SCRAPE = r"""34(function(){35  try {36  const t = document.querySelector('table.gradereport-grader-table, table#user-grades');37  if(!t){ window.__res = JSON.stringify({error:'no table', title: document.title, url: location.href}); return; }38  const clean = s => (s||'').replace(/\s+/g,' ').trim();39  const hrow = t.querySelector('tr.heading');40  const heads = [];41  hrow.querySelectorAll('th.item, th.courseitem').forEach(th => {42    const a = th.querySelector('a.gradeitemheader');43    let txt = a ? clean(a.textContent) : clean(th.textContent);44    txt = txt.replace(/^Déplier la colonne\s*/,'').replace(/\s*Actions de cellule.*$/,'').replace(/\s*Actions de.*$/,'');45    const id = (th.className.match(/\bi(\d+)\b/)||[])[1] || '';46    heads.push({text: txt, id, course: th.classList.contains('courseitem')});47  });48  const rows = [];49  t.querySelectorAll('tr.userrow').forEach(tr => {50    const uth = tr.querySelector('th.user');51    const a = uth.querySelector('a');52    const name = clean(a ? a.textContent : uth.textContent).replace(/\s*Actions de.*$/,'');53    const email = clean((tr.querySelector('td.useremail')||{}).textContent);54    const cells = [];55    tr.querySelectorAll('td.gradecell').forEach(td => {56      const id = (td.className.match(/\bi(\d+)\b/)||[])[1] || '';57      const gv = td.querySelector('.gradevalue');58      const inp = td.querySelector('input[type=text]');59      let v = inp ? inp.value : (gv ? gv.textContent : td.textContent);60      v = clean(v).replace(/\s*Actions de cellule.*$/,'');61      cells.push({id, v, overridden: td.classList.contains('overridden'), excluded: td.classList.contains('excluded')});62    });63    rows.push({name, email, cells});64  });65  window.__res = JSON.stringify({title: document.title, heads, rows, n: rows.length});66  } catch(e) { window.__res = JSON.stringify({error: e.message}); }67})();68"""6970if __name__ == "__main__":71    cid = sys.argv[1]72    url = f"{MOODLE}/grade/report/grader/index.php?id={cid}&perpage=200"73    out = step(url, SCRAPE)74    print(out)75
Files main
Menu

Theme