- 01It Started With “Sorting Alone Eats Up So Much Time”
- 02The Overall Structure
- 03Converting the PDF Into Page-by-Page Images
- 04The Judgment Logic
- 05The Scoring Rule: Unified Around “Just the Check Mark”
- 06Changing the “Unit of Mistake” Depending on the Subject
- 07Why the Setup Spans Two Separate Environments
- 08What I’ve Noticed Since Doing This
- 09Who This Is a Good Fit For
- 10Wrapping Up
Good morning. It’s まさきん.
We have more than one kid in our house, and each of them is working through different worksheets and materials. Manually sorting the pile of printouts every time was a quietly heavy chore.
So I built a system where I scan everything in one batch and let AI handle the sorting afterward. This time I want to walk through the actual code and rules I’m using, in concrete detail.
It Started With “Sorting Alone Eats Up So Much Time”
Finished worksheets and drills come in as many varieties as there are kids. Different subjects, different grade levels. If I just pile them all up together, I eventually lose track of whose is whose.
Flipping through each sheet by hand to check is simple work, but it takes forever. So I started wondering whether I could automate just that one “sorting” step.
The Overall Structure
The flow is simple. I’ve split it into three stages:
- Batch-scan the finished worksheets into a single PDF
- Convert the PDF into images page by page, and have AI judge whose worksheet it is and which subject it belongs to, based on header and layout characteristics
- Reflect the judged results into per-child, per-subject tools
The key point is that you don’t need to think about sorting at all at scan time. Just scan everything in one big batch, and leave the judgment call to AI afterward.
Converting the PDF Into Page-by-Page Images
Feeding a scanned PDF straight to AI doesn’t always work well. That’s especially true for scans with handwritten scoring marks on them — they’re often just a collection of images with no text layer at all.
So the first step is converting each page into a PNG image using PyMuPDF (fitz).
import fitz
doc = fitz.open("scan.pdf")
for i in range(doc.page_count):
pix = doc[i].get_pixmap(matrix=fitz.Matrix(2, 2)) # 2x resolution
pix.save(f"page_{i+1}.png")
Doubling the resolution with Matrix(2, 2) before converting to an image improves how accurately AI reads text and check marks. This matters especially for handwritten scoring — at low resolution, it’s easy to miss things.
An Easy Trap to Fall Into: Coordinate Units
When you want to zoom in on just the check-mark column, you can specify a range with the clip argument to get_pixmap. There’s one trap here: clip coordinates are given in the PDF’s point coordinates — the original page size — not in the pixel coordinates of the zoomed-in image.
# Even zoomed 2x or 4x, clip values are still given in the page's actual size (roughly 590x836pt for A4)
pix = doc[i].get_pixmap(matrix=fitz.Matrix(4, 4), clip=fitz.Rect(0, 140, 589, 600))
If you scale up the clip numbers just because you applied a zoom factor, the range ends up outside the image and you get nothing back. Mix this up and you’ll run straight into the mysterious “why is my cropped image all white” phenomenon — knowing this ahead of time saves a lot of wasted effort.
The Judgment Logic
Here’s a generalized version of the instructions I give the AI.
The following is an image of a scanned worksheet.
Using the hints below, judge which child's worksheet this is and which subject it belongs to.
- The design of the logo or heading at the top of the page
- The ruling and layout style (grid size, line spacing, etc.)
- The unit name or question format written on the page
- The name written in the name field
Return the result as three fields: "child ID," "subject," and "unit."
If you can't tell, answer "unknown" rather than forcing a guess.
For the actual sorting, I first build an “identification table” that combines heading text, layout characteristics, and the name field, and have the AI cross-reference against that table. For example, “a kanji-writing drill with the grade level printed in the upper right” and “a calculation drill with a small symbol printed in the lower-left corner of each question indicating the problem type” can be told apart almost uniquely just from appearance. Keeping a table like this made the AI’s judgments noticeably more consistent.
The idea of using a system to cut down on sorting effort might apply beyond worksheets, too. Reviewing a mobile plan you’ve been putting off feels like a similar story.
Clicking this opens the Rakuten login page. Once you log in, you’ll see the campaign details.
The Scoring Rule: Unified Around “Just the Check Mark”
There’s one more thing I’ve settled on for handling multiple kids’ materials: keep the scoring criteria the same no matter which child it is.
Specifically, the only thing I use to judge right or wrong is whether the check box to the left (or upper-left) of each question is filled in.
- Check box filled in → wrong (needs review)
- Check box left blank → correct
- A red circle (◯) near the answer is a “correct-answer confirmation mark,” not a sign that it’s wrong
- An answer rewritten in red pen is the parent’s correction, not the child’s own answer — I don’t judge right or wrong based on whether red ink is present
Handwriting neatness and eraser marks don’t factor into the judgment at all. The reason is simple: whether a check mark is present can be read mechanically, but the moment you mix in an evaluation of how neat the handwriting is, the standard immediately starts to wobble.
So I’ve spelled this out explicitly to the AI up front, as a rule: “Judge correctness only by whether the check box is filled in — handwriting quality and red-ink corrections are not factors.” Whether or not that one sentence is included makes a real difference in how consistent the judgments turn out to be.
The Zoom Procedure That Prevents Missed Check Marks
If you look at the whole scanned image shrunk down, filled-in check boxes can end up too small to read clearly. To avoid jumping to the conclusion that “everything’s filled in, so it must all be correct,” I always follow these steps:
- Look at the whole page first to get a rough sense of the layout
- Crop out just the column of check boxes and zoom in (using the
cliptrick from earlier) - Go through the zoomed image question by question to check whether each one is filled in
- For any check box that’s ambiguous, or anywhere the text simply can’t be read, have a human confirm it rather than guessing
I’ve learned firsthand that relying only on a thumbnail-sized image makes it easy to miss filled-in check boxes.
Changing the “Unit of Mistake” Depending on the Subject
There’s one more thing I’ve put thought into: changing the unit at which I record mistakes, depending on the subject.
| Subject | Unit of Record | Example |
|---|---|---|
| Kanji / hiragana practice | Per individual character | The character itself becomes the key — for example, a single kanji, like the one meaning “school,” or a single hiragana character |
| Math | Per problem pattern | The problem type becomes the key, such as “unit conversion,” “division with remainder,” or “place value” |
| Science / social studies | Per theme + sub-item | A hierarchical key like “theme name:section:number” |
Even for the same “missed question,” the right granularity for review differs by subject. Lump kanji together at the unit level, and the precision of the next review session drops. Break math “patterns” down to the level of individual questions, on the other hand, and it gets harder to narrow down what actually needs review.
For math, I print a small symbol representing the problem type in the corner of each question, and when reading the scoring results, I tally mistakes by that symbol. The rule is simple: if there are multiple questions of the same type and even one of them is wrong, the whole type gets flagged as “needs review.”
For science and social studies, the review section at the end of a worksheet sometimes draws its questions from a different unit than the day’s main theme, so I cross-reference the question text against a list of themes before deciding which theme it’s actually reviewing. This is one place where I never assume — I always cross-check before recording anything.
This whole mechanism was actually repurposed from a system I’d originally built for a different purpose: recording missed items and bringing them back for repeated practice. Even though the purpose changed, the underlying logic carried over as-is.
Why the Setup Spans Two Separate Environments
This system uses a two-tier setup: the actual back-and-forth with AI happens in a cloud sandbox environment, while the real file operations and state management are handled by a background process running on my home computer.
The reason is simple — the database that stores scoring results, along with the scripts that generate drills, only exist on my home computer. The cloud side reaches the home side through a lightweight client script that just sends simple commands across.
# Example: reflect a missed word based on the judged result
python3 tools/kanji_drill/kanji_bridge_client.py mark-miss schoolyard music
python3 tools/kanji_drill/kanji_bridge_client.py mark-correct classroom
# Before reflecting anything, check that the home-side background process is alive
python3 tools/kanji_drill/kanji_bridge_client.py health
I always run this health command right before actually reflecting anything. If the home-side process has gone down without my noticing and I keep sending mark-miss commands anyway, they look like they succeed but actually reflect nothing at all — and that’s exactly the kind of accident this check is there to prevent.
What I’ve Noticed Since Doing This
What’s helped the most is that the work of checking “whose is this, and which subject” has simply disappeared. Now I just scan everything in a batch and look at the results afterward.
That said, I don’t fully trust the AI’s judgment. Anything marked “unknown,” or any check box the AI seemed unsure about, I always double-check with my own eyes. I’ve found that letting the AI say “I don’t know” when it doesn’t know, rather than forcing it to sort everything regardless, ends up making the whole system more trustworthy in the long run.
Who This Is a Good Fit For
- Anyone whose worksheets and drills from multiple kids get mixed together, losing time to sorting them
- Anyone who wants scoring criteria unified around mechanical rules instead of “how neat it looks”
- Anyone who doesn’t mind building something on the scale of PyMuPDF plus a small bridge script
Wrapping Up
With materials from multiple kids, the more volume there is, the heavier the sorting burden gets. By combining scanning with AI-driven sorting, I was able to carve out just that piece and automate it.
Unglamorous details — like getting coordinate units right and spelling out scoring criteria explicitly — are, I think, actually what stabilizes accuracy the most. When you’re designing a system like this, the work of deciding on the judgment criteria themselves might matter more than you’d expect.
Worksheet sorting probably isn’t the only burden a system like this can lighten. Next, I’m planning to give my mobile phone plan the same kind of review.
Clicking this opens the Rakuten login page. Once you log in, you’ll see the campaign details.
This article contains affiliate advertising. If you sign up for a product or service through a link on this site, we may receive compensation from the partner company. The site operator is also an employee of Rakuten Group and may receive compensation through an employee referral program. The content and opinions here are based on the operator’s own experience and research regardless of any advertising relationship, but please read with the above in mind. See the Disclaimer & Affiliate Disclosure for details. Disclaimer & Affiliate Disclosure
This article includes machine-translated content. Please check the official Rakuten Mobile multilingual page for exact terms.
If you have concerns about Rakuten Mobile coverage or signal quality, you can consult through the official signal improvement request form .