# Photographed school exam OCR, assembly, and reusable practice PDFs

Use when a user uploads/scans photographed school exams page-by-page (often HEIC/iPhone photos) and wants a searchable learning archive, per-exam PDFs, and later cleaned practice versions with student answers/teacher marks removed.

## Recommended pipeline

1. **Preserve originals first**
   - Download every source image into a local `_raw_google_drive/` or `_raw_originals/` folder before transforming anything.
   - Keep original filenames and a Drive/file-id inventory so the import can be resumed.

2. **Convert/preprocess images**
   - Convert HEIC/JPEG/PNG to oriented JPG/PNG with ImageMagick: `convert "$src" -auto-orient -resize 1800x1800 "$jpg"`.
   - Keep preprocessed images separate from raw originals.

3. **OCR/tool choice**
   - Use **Docling for document structure/export** when available, but do not rely on its default `auto` OCR blindly for photographed worksheets.
   - Prefer `docling convert <image-or-pdf> --to text --ocr-engine tesseract --ocr-lang deu,eng` for mixed German/English school scans if Tesseract language packs are present.
   - Also run standalone Tesseract (`tesseract <jpg> stdout -l deu+eng --psm 6`) as a cheap fallback/comparison text source.
   - Consider **PaddleOCR** as an additional search-index source for titles, worksheet headers, and subject/topic terms. If modern PaddleOCR fails with runtime/PIR/oneDNN issues, a pragmatic fallback tested here was an isolated venv with `paddleocr==2.7.3`, `paddlepaddle==2.6.2`, `setuptools`, and `numpy<2`; smoke-test on 6–7 representative pages before using it broadly.
   - For the initial archive/searchable-index phase, keep **vision usage sparse**: use it only for a small OCR quality sample or ambiguous grouping. Do **not** run vision across every page just to archive filled exams. Save systematic vision checks for later cleanup/blank-practice generation, where detecting remaining handwriting/red teacher annotations matters.

4. **Group pages into exams**
   - Sort photos by original sequence number/time first.
   - Use visible headers/titles, page numbers, subject vocabulary, and vision/OCR evidence to split into exam groups.
   - Store per subject, e.g. `Englisch/`, `Mathe/`, `NMG/`, `Deutsch/`, with one folder per exam.

5. **Create per-exam PDFs**
   - For each exam group, produce:
     - `original_fotos/` or `seiten/` with the page images.
     - `<subject>__<exam-title>__original.pdf` assembled from pages in order.
     - OCR text/JSON/CSV metadata with page references.
   - Verify page count and open/render sample pages, not just command success.

6. **Future cleaned practice PDFs**
   - Removing student answers and red teacher annotations is an image-editing/vision QA task, not OCR-only.
   - Create a separate `uebung_leer/` or `clean_practice/` artifact; never overwrite the original.
   - After cleanup, run a vision verification pass asking specifically whether handwriting, scores, red notes, checks/crosses, and names are still visible.

## Docling pitfall and fix

Docling may be installed globally but fail due to missing Python deps, or its default RapidOCR/auto path may fail on a host. The durable lesson is not “Docling is broken”; instead:

- Create an isolated `uv` venv for the job if the global CLI/import is unhealthy.
- Verify with a one-page smoke test before batch processing.
- If RapidOCR/auto fails, switch to the Tesseract engine explicitly: `--ocr-engine tesseract --ocr-lang deu,eng`.
- Report the chosen pipeline as Docling + Tesseract + vision QA, rather than presenting Docling as a magic one-tool solution.

## Minimal smoke test

```bash
# after conversion to sample.jpg
source .docling_venv/bin/activate
mkdir -p docling_text tesseract_text

docling convert sample.jpg --to text \
  --ocr-engine tesseract \
  --ocr-lang deu,eng \
  --output docling_text

tesseract sample.jpg stdout -l deu+eng --psm 6 > tesseract_text/sample.txt
```

Then compare text length/readability and inspect the page visually for subject/title/marks on a small sample only.

## Search archive vs. cleanup mode

- **Archive/search mode:** keep filled-in exams as-is; create raw-photo backups, per-exam PDFs, OCR text from multiple engines, and CSV/JSON/Markdown indexes. Optimize for later retrieval by subject/title/topic (e.g. “LZK 4.3 Längen/Zeit”), not perfect transcription of every handwritten answer.
- **Cleanup/practice mode:** only after the user asks to create a blank practice version, duplicate the original exam and remove student answers, names, scores, ticks/crosses, and red teacher comments. This mode warrants targeted vision checks on the specific cleaned pages.
- **External OCR APIs such as Mathpix:** useful candidates for math/formula-heavy pages, but require explicit credentials and explicit user approval before uploading children’s school photos to an external service. If no credentials are configured, record “not tested” rather than downgrading the local pipeline.

## Cleanup/practice PDF lessons from filled-in math worksheets

When removing written solutions from photographed worksheets, prefer a conservative, artifact-aware workflow — and switch strategies quickly if readability suffers.

1. **Duplicate first**: output to a clearly separate folder such as `_Uebungsblaetter_bereinigt/` and never overwrite original photos/PDFs.
2. **Inspect/contact-sheet before editing**: create a contact sheet of the target pages and use one sparse vision pass to identify where handwriting, pen solutions, names, scores, signatures, and red teacher marks sit (top/middle/bottom; answer fields vs. whole page).
3. **Color-mask first, but do not trust it alone**:
   - HSV masks for red/blue/purple ink can remove teacher marks and ballpoint answers.
   - Exclude yellow/green sidebars or other colored printed elements from masks.
   - Run a first contact-sheet vision check before generating final PDFs.
4. **Inpainting pitfall**: broad OpenCV inpainting over graph-paper regions can create ugly flower/starburst artifacts and damage worksheet usability. If this appears, abandon broad inpainting for those regions.
5. **Manual whiteout/raster overlay is only a fallback, not the default finish**:
   - It may be acceptable for dense answer regions, but it can also make worksheets unusable by covering prompts/rechenaufgaben or leaving pixelated text.
   - Keep printed prompts/headings outside the whiteout wherever possible.
   - If the user says the cleaned artifact is not brauchbar because text is unreadable, pixelated, or white boxes cover tasks, do **not** keep iterating on image cleanup. Switch to recreating a clean worksheet from the same/analogous task structure.
6. **Recreate rather than over-clean when the source is a filled photographed worksheet**:
   - Use the original/OCR/one sparse vision pass to extract only the printed structure: header, topic, exercise types, numbers where legible, point values, and required workspace size.
   - Generate a fresh A4 PDF with ReportLab/Word/LaTeX: clean typography, empty answer lines, and light graph-paper grids for calculation space.
   - Preserve the educational intent and difficulty with same or similar numbers/tasks, but do not attempt perfect visual restoration of the photo.
   - This is especially appropriate for 4th-grade math LZKs with written solutions over graph paper: a newly generated sheet is more useful for a sibling than a heavily retouched scan.
7. **Iterate with vision only on the target artifact**: after cleanup or recreation, build a contact sheet of the produced pages and ask vision only whether tasks are legible, no handwriting/red corrections remain, no white boxes cover prompts, and text is not clipped/overlapping. Avoid full-page vision in archive mode; use it here because practice-sheet quality depends on pixels.
8. **Be explicit about limitations**: label recreated sheets as “neu erstellt auf Basis der Original-LZK” rather than claiming they are exact cleaned originals.

Minimal implementation patterns:

```python
# Cleanup path (only when it remains readable)
# 1) remove colored ink with HSV mask + inpaint small strokes
# 2) overlay manual rectangles on answer regions
# 3) redraw graph grid inside large calculation regions when useful
# 4) assemble cleaned JPGs to PDF and verify page count
```

```python
# Recreation path (preferred when cleanup damages readability)
# 1) make a contact sheet of the original pages
# 2) extract printed exercise structure, not handwriting
# 3) generate fresh A4 pages with clear headers, tasks, answer blanks, and graph-paper grids
# 4) render the new PDFs to a contact sheet and check visually for legibility/clipping
```

Deliver concise artifact-first output: send the PDFs (`MEDIA:/path`) plus a one-line verification summary.