From PDF Archive to Study App: An LLM-Driven Data Pipeline

 

A lot of real-world problems boil down to the same shape: some scattered, unstructured source material exists (PDFs, archives, scanned documents), and what you actually need is a structured, usable product on the other end — categorized, searchable, and packaged for a specific audience. That transformation used to mean writing a pipeline by hand: a parser here, a classification script there, a templating layer to render the final output. With an LLM as a development partner, the same pipeline can be built through conversation — research, extraction, categorization, content generation and packaging, each step still a real, inspectable script or file, but designed and iterated on in natural language rather than written line by line. The part that changed the most for me wasn’t the coding — it was the analysis phase. Reading five years of exam PDFs, working out the syllabus scope, and deciding how to categorize dozens of tasks by topic and difficulty is the kind of manual review that would normally cost me a day or two of evenings. Doing it as a conversation with an LLM instead compressed that down to a couple of hours.

I used exactly this process to build a real, working use case: a study app for my daughter, a 7th grader preparing for a regional math competition (in Poland, Konkurs Przedmiotowy, organized by the local educational authority, Kuratorium Oświaty). She had about a month to prepare, three to four sessions a week. Instead of buying a prep book, I turned five years of archived, unstructured exam PDFs into a categorized, searchable study kit — theory pages, a task index, printable worksheets and a small offline web app — almost entirely by talking to Claude Code rather than writing code by hand. The rest of this article walks through that pipeline step by step.

Step 1: Research before a single line of code

Before building anything, I needed to know what I was building for. The competition has an official rulebook (regulamin) published as a PDF, defining the exam date, duration and — most importantly — the syllabus scope for the school-level stage: 15 topic areas from the national curriculum, from natural numbers to an introduction to combinatorics and probability. I asked Claude to read that PDF and turn it into a plain-language summary, instead of re-reading a dense administrative document myself.

The second research task was archival: finding past exam sheets. The Kuratorium publishes an archive of previous years’ sheets and answer keys. Claude searched it, and we ended up with five usable years (2018/19 through 2024/25, excluding two years that simply weren’t published and one older year that belonged to a different school structure). Five years of real, graded exam questions became the raw material for everything else.

Step 2: A parser script instead of an agent reading PDFs

This is the part that felt most like “real” software engineering, just compressed — and it’s also where I made a deliberate architectural choice worth calling out. Instead of manually downloading five years of PDFs and copy-pasting questions, I asked Claude to write a small, deterministic ETL pipeline:

  • scripts/manifest.json — a plain list of {year, sheet_url, key_url} entries, the single source of truth for what to fetch.
  • scripts/download_arkusze.py — pulls every PDF into data/raw/, skipping files that already exist.
  • scripts/extract_pdf.py — uses PyMuPDF to pull raw text and embedded images out of each PDF into a cheap, tokenizer-friendly .txt preview.

The point of writing an actual parser here, rather than handing raw PDFs to the LLM and asking it to “read and structure this,” was to save tokens. Five years of exam sheets and keys is a lot of pages, and re-feeding that volume of raw PDF content into a model every time I needed to look something up would have been wasteful and slow. A script that runs once and produces a clean, reusable .txt/.png cache is strictly cheaper — the model reads the parser’s output, not the PDFs themselves.

This is actually the opposite of an approach I used in another project — Self-Healing Scrapers.

Step 3: Grouping every task by topic and difficulty

With five years of clean questions, I asked for something like a search index: given a year and a task number, tell me what kind of problem it is and how hard it is. This became katalog_zadan.md, a single Markdown table that now classifies all 98 collected tasks across the 16 syllabus topics (rounding, divisibility, fractions, percentages, plane figures, solids, equations, word problems, and so on), each tagged with a difficulty marker and linked straight to the matching theory file and the original question.

Deciding how to group tasks wasn’t a fixed rule — it came out of a back-and-forth: what taxonomy makes sense for a 13-year-old revising by topic, and how fine-grained the categories should be so a study session stays focused. That’s a modeling decision, and having a conversation about it, rather than hard-coding categories upfront, is exactly where an LLM earns its keep.

Step 4: Writing the theory

For each of the 16 topics, Claude wrote a self-contained theory file: definitions, formulas, common competition tricks and traps, plus a few simple, fully worked examples to build confidence with the concept itself before moving on. What the theory files don’t contain is solutions to real archived tasks — those stay in an index linking straight to the matching question, no answer shown, so my daughter always solves them fresh instead of reading the solution.

Step 5: Printable PDFs, one per topic

For offline study sessions, I wanted printable worksheets: one PDF with just the questions for a topic, one with the answers, generated straight from the same theory-file “task index” so there’s exactly one source of truth.

Step 6: A local, offline HTML site

Finally, scripts/build_html.py turns the whole Markdown-based repository (study plan, catalog, theory, per-year sheets, mini-tests) into a fully static html/ folder — fonts and the KaTeX math renderer vendored locally, zero external requests, opens straight from disk in a browser.

On top of the archived exam content, the site also has an “AI quizzes” section: 76 brand-new, numerically verified practice questions written from scratch — one full set per topic — with click-to-reveal answers, so my daughter never runs out of fresh material even after exhausting five years of real exams.

What this actually was

None of this required me to write ETL code, PDF-rendering logic, or a static site generator myself. My job was to define intent, review output, and catch mistakes like the answer-leaking theory files. The AI did the research, wrote and ran the pipeline, classified the content, and generated a working static site — all through iterative conversation, committed to git step by step like any normal software project. For a scoped, well-defined problem like “turn five years of PDFs into a structured study tool,” that division of labor worked remarkably well.

Worth being honest about scope: this whole thing is a proof of concept, not production software. There’s no test suite, no error handling beyond the happy path, and I reviewed output rather than auditing every generated file line by line. For a one-off study tool used by one student, that level of AI-assisted code is entirely fine — the point was to validate the idea and get something usable fast. If this ever became something other people depend on, that’s exactly the point where I’d go back and harden it.

The result



Comments

Popular posts from this blog

C#- Change form language at runtime.

C# Custom Performance Counters - First Performance Counter

TCExecutor v0.0.1 released