See every grade a Moodle learner earned — across all courses, even after they're unenrolled

Moodle's gradebook is per-course, so there's no one screen that shows a single learner's whole record — and unenrolled learners drop out of the gradebook entirely. Here's the SQL to pull it yourself, and the free report we built so you don't have to.

Moodle has no core screen that lists one learner's grades across every course — the gradebook is per-course, and unenrolment deletes live grade rows (they survive in mdl_grade_grades_history). To see a learner's full record you either open each course in turn, UNION mdl_grade_grades with mdl_grade_grades_history in SQL, or install a report that consolidates it. Grade Lookup (report_gradelookup) is our free, GPLv3 site report that does the last one: search any learner — current, past, or unenrolled — see their grade in every course with an item-by-item breakdown and certificates, and export to Excel.

Someone from your registrar's office asks a simple-sounding question: "What did this learner score — in everything?"

In Moodle, that is not a one-click answer. The gradebook is built per course: to see a learner's totals you open each course's grade report, one at a time, and copy the numbers into a spreadsheet by hand. If the learner has taken twelve courses, that's twelve screens. And if they were ever unenrolled — expired cohort, finished contract, seasonal cleanup — they won't appear in those grade reports at all, because the live grade rows were deleted the moment they were removed.

We ran into this constantly supporting a busy training platform: current staff, past staff, contractors who'd rolled off, all needing their records pulled for audits and re-certification. Doing it by hand didn't scale. So we wrote a report that answers the question directly — and because tools like this are how we introduce ourselves, we're releasing it free. This post is the honest version: what the gap actually is, how to pull the data yourself with SQL, and where the plugin picks up.

The gap

Why there's no "all of a learner's grades" screen in core Moodle

Moodle's data model puts every grade in mdl_grade_grades, keyed to a grade item that belongs to exactly one course. The gradebook UI reads that table per course. The user's profile page gives you an "Grades overview" link, but it still walks you course by course — there's no consolidated, filterable, exportable table of "this person, every course, one view." For a handful of learners you click through it. For a cohort, or for anyone who's left, you can't.

Unenrolment makes it worse. A full unenrol deletes the learner's rows from mdl_grade_grades — so they vanish from every course grade report — but archives each deleted row into mdl_grade_grades_history first. The grade still exists; nothing in the core UI will show it to you. (We wrote up that mechanism in detail in Moodle grades gone after re-enrolment? They're not.)

What core Moodle gives you one gradebook per course — open each separately Course A · gradebook Course B · gradebook Course C · gradebook Course D · learner unenrolled grade rows deleted — not shown anywhere To answer "every grade?" you visit each, and Course D's learner is simply absent. What you actually need one learner · every course · one view Jordan Ellis Course A88% Course B92% Course C76% Course D81% recovered from history certificates: 3 · avg 84% · export ready One search, past learners included, one download. That's the report below.
Core Moodle answers "grades in this course." The question people actually ask is "grades for this person, everywhere" — and it has no core screen.

The manual way

Pulling a learner's cross-course grades with SQL

If you just need the answer once and you have database access, you don't need any plugin. This query lists a single learner's course-total grade in every course, reading the live gradebook. The course total is the grade item with itemtype = 'course':

SELECT c.shortname, c.fullname,
       gg.finalgrade, gi.grademax,
       ROUND(100 * gg.finalgrade / NULLIF(gi.grademax,0), 1) AS percent
  FROM mdl_grade_items gi
  JOIN mdl_grade_grades gg ON gg.itemid = gi.id
  JOIN mdl_course c        ON c.id = gi.courseid
 WHERE gi.itemtype = 'course'
   AND gg.userid = ?          -- the learner
   AND gg.finalgrade IS NOT NULL
 ORDER BY c.fullname;

That covers everyone still enrolled. To also catch courses the learner was unenrolled from — where the live row is gone but the value survives in history — union the latest history row per course:

SELECT c.shortname, latest.finalgrade, gi.grademax
  FROM mdl_grade_items gi
  JOIN mdl_course c ON c.id = gi.courseid
  JOIN (
        SELECT ggh.itemid, ggh.finalgrade
          FROM mdl_grade_grades_history ggh
          JOIN (
                SELECT itemid, MAX(timemodified) AS tm
                  FROM mdl_grade_grades_history
                 WHERE userid = ? AND finalgrade IS NOT NULL
                 GROUP BY itemid
               ) newest ON newest.itemid = ggh.itemid
                       AND newest.tm = ggh.timemodified
       ) latest ON latest.itemid = gi.id
 WHERE gi.itemtype = 'course'
   AND gi.id NOT IN (
        SELECT itemid FROM mdl_grade_grades
         WHERE userid = ? AND finalgrade IS NOT NULL
       );

This is genuinely enough for a one-off. But notice what it doesn't give you: a name search (you need the userid already), an item-by-item breakdown, certificates, completion, time-on-task, cohort filtering, or an export your registrar can open. Rebuild all of that by hand every time someone asks, and you've written a report. So we did.

The tool

Grade Lookup — a free report that answers the whole question

Grade Lookup (report_gradelookup) is a Moodle site report. Once installed it lives under Site administration → Reports → Grade Lookup, and it's a single search box for people, not databases. Type a name or email — current, past, or unenrolled — and you get that learner's grade in every course, a click-through to the item-by-item breakdown, their certificates, and a one-click Excel download. Filter by course or cohort (by name or ID) and it turns into a cohort summary: learner count, average grade, completion, certificates issued, time spent.

Grade Lookup Site administration ▸ Reports Find learners — name or email… Search Download ⬇ Cohort: Compliance 2026 (L2) ✕ Course: AML-C12 ✕ Clear all filters Learners 358 Average grade 74% Completion 61% Certificates 212 Avg. active time 4h 12m 358 learner(s) found LEARNER COURSES WITH GRADES RECORD Jordan Ellisjordan.ellis@… 6View record › Priya Nairunenrolled · from history 4View record › Marcus Bellmarcus.bell@… 9View record › Records appear even for learners who have been unenrolled.
The search view. Filter to a course or cohort and the summary tiles recompute; every learner — including unenrolled ones, pulled from history — is one click from their full record.

Open a learner and you get their record: course totals across everything, each one clickable into the item-by-item grade breakdown (recovered from history where the live grade was purged), plus any certificates they've been issued with verification codes.

Learner record: Jordan Ellis View profile · Download (Excel) · Back to results Grades — all courses COURSE GRADE PERCENT DONE AML Foundations (AML-C12)78.078% Grade detail: AML Foundations — item by item Quiz 1 · Risk basics9.0 / 1090% Assignment · Case study22.0 / 3073% Quiz 2 · Sanctions17.0 / 2085% Final assessment30.0 / 4075% Recovered from grade history where the live grade was purged. Sanctions Screening (SS-204)91.091% KYC Advanced (KYC-L2) · unenrolled81.081% KYC Advanced grade recovered from history — learner no longer enrolled.
A learner record. Click any course total to expand the item-by-item breakdown; unenrolled courses still appear, their grades read back from history.

What it does

The whole feature set, plainly

  • Find any learner — by name or email, including current, past, and fully unenrolled learners (their grades are recovered from history, so they still appear).
  • Filter by course or cohort — search by name or ID (type AML-C12), pick more than one, and add an optional active-time date window.
  • Read the summary — for whatever you've filtered to, a strip of tiles shows learner count, average grade (with median and range), completion, certificates, and active time.
  • Open a learner — their grade in every course, each expandable to the item-by-item breakdown, plus certificates with verification links.
  • Export to Excel — one click produces a workbook: a summary sheet and the full learner list. CSV is available too.

Grades are shown as plain percentages. The report deliberately doesn't colour them pass/fail, because every course sets its own passing mark — a green "82%" would be a lie in a course that requires 90.

Built to Moodle's standards

Safe to put on a production site

A report that reads grade history and personal data has to be trustworthy, so this one is built to the plugin-review bar rather than as a quick hack:

  • Read-only. It never writes to your database — no new tables, no schema, no scheduled tasks. Uninstalling leaves nothing behind.
  • Access-gated. A dedicated capability, report/gradelookup:view (manager level, flagged RISK_PERSONAL), controls who can open it — so it's not exposed to anyone with a login.
  • Privacy-clean. It ships a Moodle Privacy API provider declaring it stores no personal data of its own; it only displays grades, grade history, and certificates that already exist.
  • Injection-safe and current. Every query is parameterised, every output escaped, and it's tested against Moodle 4.3 LTS through 5.0 using stable core APIs.

We had it audited dimension by dimension — structure, security, privacy, performance, and search UX — against Moodle's official developer standards before releasing it.

Get it

Installing Grade Lookup

It installs like any Moodle plugin, no server shell required:

  1. Download the plugin ZIP (link goes on the plugins page as the Moodle Marketplace listing goes live).
  2. In Moodle: Site administration → Plugins → Install plugins, upload the ZIP, and install from the ZIP file.
  3. Run the upgrade when prompted, then find it under Site administration → Reports → Grade Lookup.

Requirements: Moodle 4.3 or newer. Certificate display uses mod_customcert if you have it, and is skipped cleanly if you don't. As always, try it on a staging site first.

We're submitting it to the Moodle Marketplace now. When the listing is approved you'll be able to install it straight from within Moodle's plugin installer by name. Until then, grab the ZIP from the plugins page.

If you need a report core Moodle doesn't have

Grade Lookup exists because a client needed a question answered that Moodle couldn't answer out of the box, and building the report properly was cheaper than doing it by hand forever. That's most custom reporting: a specific cross-section of your data — grades, completion, certificates, attendance, SCORM tracking — that no core screen quite gives you. Assembling those is routine Moodle development work for us, and the good ones become free releases like this one. If you keep re-answering the same question by clicking through twelve screens, tell us what it is — we'll reply within one business day.

Quick answers

Questions people ask about this

Can you see all of a student's grades across every course in Moodle?

Not from one core screen. Moodle's gradebook is per-course, and the user profile links out to each course grade report one at a time. There is no built-in page that lists a single learner's course totals across every course they have taken. You either open each course in turn, run SQL against mdl_grade_grades, or install a report plugin that consolidates it — such as our free Grade Lookup report.

Why do a learner's grades disappear after you unenrol them?

A full unenrolment deletes the learner's rows from mdl_grade_grades, the live table the gradebook reads, so they vanish from every course grade report. Moodle archives each deleted row into mdl_grade_grades_history first, so the values still exist — but no core screen shows them. Grade Lookup reads both tables, so unenrolled and past learners still appear.

What is the report_gradelookup plugin?

Grade Lookup (report_gradelookup) is a free, GPLv3 Moodle site report from Sternfast. It adds a search under Site administration ▸ Reports ▸ Grade Lookup where you find any learner by name or email — current, past, or unenrolled — and see their grade in every course, an item-by-item breakdown, their certificates, and summary metrics for any course or cohort. Results export to Excel. It is read-only and stores no data of its own.

Does Grade Lookup work on unenrolled and deleted-enrolment learners?

Yes. Where a learner's live grade rows were removed by unenrolment, Grade Lookup recovers the course total from mdl_grade_grades_history, so past learners still show up in search and their grades still display. It reads history rather than recomputing, so it works even where gradebook items were purged.

How do I export a learner grade report from Moodle to Excel?

Core gradebook exports are per-course. To export a cross-course view — a filtered set of learners with their course totals and summary metrics — Grade Lookup has a one-click Download (Excel) button that produces a workbook with a summary sheet and the full learner list. A CSV export is also available.

Is Grade Lookup safe to install on a production Moodle site?

It is designed to be. The report is read-only, adds no database tables, runs no scheduled tasks, and is gated behind its own capability (report/gradelookup:view, manager level, marked RISK_PERSONAL). It ships a Privacy API provider declaring it stores no personal data of its own, all queries are parameterised, and it is tested on Moodle 4.3 through 5.0. As with any plugin, install it on a staging site first.