Skip to content

Use Cases

NesiciCoding edited this page Aug 12, 2026 · 16 revisions

Use Cases

Real-world scenarios for the EFL classroom, each with the input you give the tools and the output you get back. Vocabulary examples are real runs of vocab_profile.py; grammar examples follow the profiler's documented JSON/pretty shape (see GRAMMARPROFILE.md).

Reading the results. Typical is the busiest band (where most of the text sits); Reaches / 90% coverage is the highest band you need to account for. A B1 class can usually handle a text that is typical A2, reaches B1; a text that reaches C1 will need pre-teaching or adaptation first.

Jump to a scenario

  1. Screening a reading text for a specific class
  2. Vocabulary pre-teaching — pull the hard words
  3. Checking grammatical range in a student's writing
  4. The A2-vocabulary / B2-grammar trap
  5. Auditing and comparing textbook passages
  6. Writing or simplifying a graded reader
  7. Curriculum mapping — did this unit cover the target grammar?
  8. Profiling a whole document — PDF, Word, Markdown
  9. Inside Claude Code / Cowork — just ask
  10. Scripting a classroom pipeline with JSON
  11. Which of these 20 articles suits my B1 class?
  12. Spaced introduction across a folder of readings
  13. The app consumes the report contract (RubricMaker)

1. Screening a reading text for a specific class

Scenario. You teach a B1 group and found an article you might assign. Before handing it out, you want to know whether the vocabulary is in reach.

Input

python3 vocab_profile.py --type cefr --text \
"The researchers analysed the philosophical implications of photosynthesis and chlorophyll synthesis in aquatic ecosystems."

Output (pretty, in a terminal)

Vocabulary Profile

Total words: 14
Typical: A1   90% coverage: C2
Most words are A1; you need C2 to cover ~90%.

■ A1 36%   ■ B1 29%   ■ B2 14%   ■ C1 7%   ■ C2 14%

B1  analysed · ecosystems · researchers · synthesis
B2  implications · philosophical
C1  aquatic
C2  chlorophyll · photosynthesis

What you learn. Grammatically simple, but the content words shoot up to C1–C2 (aquatic, chlorophyll, photosynthesis). For a B1 class this text needs the academic terms pre-taught or glossed — it is not a cold read. Compare with a text that stays inside A1/A2 and can be handed straight over:

python3 vocab_profile.py --type cefr --text "Yesterday I walked to the shop to buy bread and milk."
# Typical: A1   90% coverage: A1   → hand it out as-is

Want the exam equivalent of those bands instead of raw CEFR? text_report.py --cambridge maps the report's own bands to the matching Cambridge Qualification — a B2 reaches line reads B2 First (FCE), the exam a candidate at that level is working toward.


2. Vocabulary pre-teaching — pull the hard words

Scenario. You've decided to use the science text anyway. You want the list of words above your class level to put on a pre-teaching slide.

Input — ask for JSON and pull the upper bands (here with jq):

python3 vocab_profile.py --type cefr --format json --text \
"The researchers analysed the philosophical implications of photosynthesis and chlorophyll synthesis in aquatic ecosystems." \
| jq -r '.results.cefr | to_entries[] | select(.key|test("B2|C1|C2")) | .value.words[].word'

Output

implications
philosophical
aquatic
chlorophyll
photosynthesis

What you learn. A ready-made pre-teaching list of exactly the words above B1. The same JSON also drives the academic-vocabulary view — swap --type cefr for --type awl to get just Coxhead's Academic Word List hits:

Awl  29% (4)
  analysed, implications, philosophical, researchers

Nearly a third of the running words are academic vocabulary — a strong signal the text belongs in an EAP / upper-secondary setting, not a general A2 lesson.


3. Checking grammatical range in a student's writing

Scenario. A student is aiming for B2. You want to see whether their essay actually uses B2-level structures or just strings A2 sentences together.

Input

python3 grammar_profile.py --file student-essay.docx

Output (pretty)

Grammar Profile

Sentences: 24   Tokens: 410
Typical: A2   Reaches: B2

A1  Present simple ×31 · to-infinitive ×12 · Relative: that ×3
A2  Past simple ×18 · Present perfect ×4 · Passive (past) ×2 · First conditional ×2
B1  Modal: could ×2 · Past perfect ×1
B2  Comparison of equality (as … as) ×1 · Passive (perfect) ×1

What you learn. The writing reaches B2, but only just — one as…as comparison and a single perfect passive carry the whole upper band, while the bulk is A1/A2. Concrete, evidence-based feedback: "to push toward B2, bring in more of the passive, add a relative clause with which, try a second conditional." This is the same rule-based grammar evidence RubricMaker surfaces per-criterion during grading (see RubricMaker Alignment).


4. The A2-vocabulary / B2-grammar trap

Scenario. Two texts look equally "easy" on a quick skim. You profile both ways and discover they are not.

Input

python3 vocab_profile.py   --type cefr --text "The house was built by workers who had been trained abroad."
python3 grammar_profile.py --text            "The house was built by workers who had been trained abroad."

Output

# vocabulary
Typical: A1   90% coverage: A2         ← looks easy

# grammar
Typical: A2   Reaches: B2
A1  Relative: who ×1
A2  Passive (past) ×1
B1  Past perfect ×1
B2  Passive (perfect) ×1

What you learn. The words are almost all A1/A2, so a readability formula would call this "easy" — but the sentence stacks a past passive, a past perfect, a perfect passive, and a relative clause. For a real learner it's a B2 sentence. This is the case that justifies running both profilers: difficulty is lexical and grammatical, and the two can diverge sharply.


5. Auditing and comparing textbook passages

Scenario. You're choosing between two coursebook units for the same B1 group and want an objective difficulty comparison rather than a gut feeling.

Input — one command over the whole folder (vocabulary + grammar per text, in a spreadsheet-ready summary):

python3 class_profile.py --file units/ --format csv

Output (units.csv on stdout)

file,total_words,vocab_typical,vocab_reached,grammar_typical,grammar_reaches,estimated_level
unit-3.txt,412,A1,A2,A2,B1,A2
unit-7.txt,398,A1,B1,B1,B2,B1
...

(Add --target-level B1 for each text's %-above-target and fits verdict, or --targets A2,B1,B2 to see how the set splits across several classes at once.)

What you learn. Unit 7 is a step up on both axes — more upper-band vocabulary and B2 grammar. If your group has just reached B1, Unit 3 is the safer opener and Unit 7 the stretch text for later in the term. The old way — a shell loop diffing JSON summaries per file — still works and remains the way to pull a single number, but the built-in CSV replaces the loop for the comparison itself.


6. Writing or simplifying a graded reader

Scenario. You're rewriting an authentic article down to A2 for a graded reader. You want a fast feedback loop: edit, re-profile, repeat until the upper bands are gone.

Input — check where you are, then let the rewrite aid name the swaps:

python3 vocab_profile.py --type cefr --file draft.md                # where am I?
python3 text_report.py --file draft.md --target-level A2 --suggest  # what do I change?

Output — the vocabulary view tints every word by its band and lists the offenders; --suggest shows a simpler alternative next to each one above the target (and the --export md handout gains a Simpler alternative column):

Typical: A2   90% coverage: B1
■ A1 61%   ■ A2 24%   ■ B1 11%   ■ Off List 4%

B1  acquire · obtain · beneath        ← replace these

Above A2 — words: acquire (B1) → get (A1), obtain (B1) → get (A1),
                 beneath (B1) → under (A1)
→ suggests a simpler alternative (rewrite aid)

What you learn. Three words keep the text at B1 — and --suggest names the swap straight from the bundled curated list (acquireget, obtainget, beneathunder). Swap them and re-run; when the B1 band empties, you've hit your A2 target. This is the CLI echo of the original VocabKitchen's "adjust a text to a target level" workflow, now with the suggestions surfaced automatically (see Roadmap).


7. Curriculum mapping — did this unit cover the target grammar?

Scenario. Your B1 scheme of work promises the present perfect, the first and second conditionals, and relative clauses. You want to confirm the unit's texts actually contain those structures — and the vocabulary it expects students to meet.

Input — write the unit's promises as a checklist file, then run it:

cat > unit-checklist.txt <<'EOF'
# Unit 3 — what the scheme of work promises
[vocabulary]
purchase
circumstances

equipment

[grammar]
present perfect
first conditional
second conditional
relative clause: that
EOF
python3 text_report.py --file unit-reading.txt --curriculum unit-checklist.txt

Output (terminal)

Curriculum checklist — 2 of 3 vocabulary, 3 of 4 grammar ✗
  ✓ purchase (B2)      ✗ circumstances
  ✓ equipment (B1)     ✓ Present perfect
  ✓ First conditional  ✓ Second conditional
  ✗ Relative clause: that
  missing: circumstances, Relative clause: that

What you learn. The unit covers most of its promises — but circumstances never appears and no relative clause shows up, two gaps to fill before teaching. Grammar items resolve against the grammar profiler's construction names (or ids), case-insensitively; vocabulary items carry their CEFR band when recognised. The same checklist threads through class_profile.py --export md (per-text sections) and --export csv (a folder-level coverage grid — one row per text, one column per required item), so a folder of candidate texts can be compared against the unit's requirements in one run; the set-level summary handout mirrors the same grid in its Curriculum coverage section, the JSON report carries it as curriculumCoverage (items × rows × cells) for scripts, and text_report.py --watch re-checks the checklist on every save while you edit. The checklist file is schema-validated before any profiling — a typo like [grammer] fails fast with a did you mean hint, and an empty required section — or a grammar item that doesn't resolve against the construction list (e.g. second conditinal) — warns with a suggestion before the run. With class_profile.py --cando --export md, each per-text handout also gains its CEFR Can-Do descriptors plus an Above the target list — the demands beyond what the class is expected to do yet — and --cando-diff adds the set-level Can-Do demands across the set table to the summary handout: which above-target descriptors the texts share, most-common first, or ordered by the CEFR ladder with --cando-diff-sort band.


8. Profiling a whole document — PDF, Word, Markdown

Scenario. The reading you want to assess arrived as a PDF handout or a Word document; you don't want to copy-paste it.

Input

python3 vocab_profile.py   --type all --file article.pdf     # needs: pip install pypdf
python3 grammar_profile.py --file worksheet.docx
echo "Some pasted prose." | python3 vocab_profile.py --type cefr

Output. Identical reports to the --text examples above — the file's format is detected from its extension and the prose is extracted first:

Extension How it's read
.txt / other UTF-8 text
.md / .markdown Markdown stripped to prose
.docx paragraph text from the Word XML (stdlib only)
.pdf text layer via pypdf (no OCR for scanned pages)

What you learn. The same profiling works on the materials you already have, in the formats teachers actually receive them in — no reformatting step.


9. Inside Claude Code / Cowork — just ask

Scenario. You're in a Claude Code or Cowork session and don't want to remember flags. All four tools ship as plugins, so you can ask in plain language.

Input

/plugin install vocab-profiler@vocabkitchen
/plugin install grammar-profiler@vocabkitchen
/plugin install text-report@vocabkitchen
/plugin install class-profile@vocabkitchen

then simply:

"What CEFR level is this paragraph, and what grammar does it use?" (paste the paragraph)

"Which of the articles in this folder suit my B1 class?" (point at the folder)

Output. Claude runs the plugins and answers conversationally — e.g. "The vocabulary is typically A2 and reaches B1 (purchase, enormous); grammatically it reaches B2 on the strength of two passives and a relative clause with which." — and for the folder: "Three of the eight articles fit B1; two just miss it with ~15% of words above level — here are the pre-teaching lists." — with the option to show the full per-band breakdown.

What you learn. The same analysis, with zero CLI syntax — useful for teachers who live in the chat rather than the terminal.


10. Scripting a classroom pipeline with JSON

Scenario. You maintain a shared folder of reading texts and want a spreadsheet of every text's level for colleagues.

Input — the built-in batch summary (vocabulary + grammar per text, one row per text, ready for a spreadsheet):

python3 class_profile.py --file readings/ --format csv > levels.csv

Output (levels.csv)

file,total_words,vocab_typical,vocab_reached,grammar_typical,grammar_reaches,estimated_level
climate.txt,312,A1,B1,A2,B2,B1
recipe.txt,208,A1,A2,A2,A2,A2
sports-news.txt,287,A1,B1,A2,B2,B1

For pipelines that need the full JSON, the automatic switch to JSON whenever output is piped still applies to every tool; the loop below is the "manual" version the batch summary replaces:

for f in readings/*.txt; do
  level=$(python3 grammar_profile.py --format json --file "$f" | jq -r '.estimatedLevel.reaches')
  vocab=$(python3 vocab_profile.py --type cefr --format json --file "$f" \
          | jq -r '[.results.cefr | to_entries[] | select(.value.percentage|rtrimstr("%")|tonumber>0) | .key] | last')
  printf '%s,%s,%s\n' "$(basename "$f")" "$vocab" "$level"
done > levels.csv

What you learn. One row per text, vocabulary and grammar side by side — exactly the shape a batch report or a gradebook import expects — now without maintaining the loop. For per-file detail beyond the summary, pipe the single- text tools to JSON as before (see Roadmap).


11. Which of these 20 articles suits my B1 class?

Scenario. You've collected a folder of candidate readings for next term and need to shortlist the ones a B1 group can handle — and know which are the close calls worth pre-teaching.

Input — rank the set by level and keep only the texts whose estimated level is at or below B1:

python3 class_profile.py --file candidates/ --max-level B1 --target-level B1

Output (pretty, in a terminal)

Class Profile — candidates/ (20 texts)

Aggregate vocabulary (20 texts, 6 412 words)
  Typical: A1   90% coverage: B2   7% off list
  ■■■■■■■■□□□□□□□□□□□□□□□□□□□□□□□□□

#  file               words  typical  reached  grammar    est   %above  fits
1  lakes-and-rivers   214    A1       A1       A2→B1      A2    0%      ✓
2  city-birds         187    A1       A2       A2→B1      A2    2%      ✓
...
14  synthetic-dyes     302    A1       B2       B1→B2      B2    18%     ✗

What you learn. With --max-level B1, only the texts that fit the band are listed — the "which of these suits B1?" question answered in one command instead of twenty single-text reports. The close calls are the ones just over the line: their % above B1 tells you how much pre-teaching they'd need, and --export md --target-level B1 hands you the per-text word lists to teach them with (see Roadmap).

12. Spaced introduction across a folder of readings

Scenario. You've picked a folder of texts for a term of extensive reading — but if reading 1 throws all its hard words at once, students drown. You want the new vocabulary to enter in a spaced pattern across the repeated readings: a controlled number of new words each time, with earlier words coming back for review.

Input — build the schedule across the whole folder, capped at 4 new above-B1 words per reading:

python3 class_profile.py --file candidates/ --target-level B1 \
    --interleave --new-words-per-reading 4 --export md --output plan/

Outputplan/candidates-interleave-B1.md, one handout for the whole set:

# Vocabulary interleaving — Target B1 (5 readings, 4 new words/reading)

## Reading 1 — lakes-and-rivers.txt
**Introduce (4):** `absorb` (B2); `pollutants` (B2); `sediment` (C1); `tributary` (C1)

## Reading 2 — city-birds.txt
**Introduce (4):** `habitat` (B2) — deferred from reading 1; ...
**Due for review (4):** `absorb` (B2 — last seen reading 1); ...

What you learn. Overflow from a crowded reading is deferred to the next with room, so every reading introduces at most the budget; words that recur in a later reading are listed for spaced review, and words absent for two or more readings are flagged due — the teacher's plan for introducing a folder's vocabulary at a controlled rate (see Roadmap). The same schedule is available as --export csv (one row per word: level, introduction reading, appearances, deferral) and in JSON (interleave in the payload).

Per-reading handouts. With --export md the run also writes one <set>-interleave-<LEVEL>-reading-<N>.md per reading — that reading's Introduce / Review / Due words, each with a definition: the in-text sentence it appears in by default, upgraded to a real dictionary definition when the cache is primed. A --pre-enrich --interleave run primes the cache for exactly the words the schedule introduces in one rate-limited pass, so the handouts are fully enriched offline afterwards.

13. The app consumes the report contract (RubricMaker)

The single-text report is a versioned JSON contract, not an ad-hoc dump: python3 -c "import analysis" is the entry point, every payload carries schemaVersion, and the JSON Schema is checked in at analysis.schema.json (kept byte-equal to analysis.payload_schema() by the tests and CI, and printable with --schema on either CLI).

Input — profile one uploaded essay exactly like the CLI does, from code:

import analysis
payload = analysis.analyze(essay_text, target_level="B1", cando=True)
assert payload["schemaVersion"] == analysis.SCHEMA_VERSION  # 1.0

What you learn. The payload carries the whole report under one roof: vocabulary bands + counts, grammar (when spaCy is installed), the above-target words/structures, coverage figure, verdict, readability, the curriculum pass/fail, and — the shape RubricMaker's grammar linker consumes — grammarCriteria: one entry per registered construction, each judged used (pass, with count + up to two example sentences) or not used (fail). The app can attach a comment per criterion — "Uses the passive correctly" / "Doesn't use present perfect yet" — without deriving anything, and because the schema is versioned, a consumer can check schemaVersion before trusting the shape.

With --comments the same payload also carries the full apply-as-comment reference implementation — the rubric covers the whole report: grammarComments (one ready-to-apply rubric comment per construction, Uses the … — E.g. "…" with the detected span as evidence for each used one, Doesn't use the … yet for the rest) plus vocabComments (one comment per above-target word — Above B1: "anticipate" (B2) — used 1×. E.g. "…", with the curated simpler synonym appended under --suggest), so the linker can attach them verbatim. Under a target level the grammar half is filtered to the class level: at/below constructions keep their pass/fail comments (kind "rubric"), a used above-target construction becomes a "pre-teach" note (Uses the Modal + perfect (B2) — above the B1 target: pre-teach or rewrite. E.g. "would have passed" Rewrite: swap for a past simple or a present modal ("would have passed" → "passed") — the rewrite suggestion comes from the curated WordLists/structure-rewrites.csv, validated by build_wordlists.py --check), and unused above-target constructions are dropped. The CLI renders them as Rubric comments and Vocabulary comments sections in the --export md handout, and class_profile.py --comments puts both sections in every per-text handout of a folder run — plus a Demand scan table in the set summary (per-text above-target word and pre-teach structure counts) and a combined rubric-comment deck per --targets level, so the same construction is plain rubric at its own level and pre-teach above it.

  • Roadmap — where these workflows are heading, phase by phase.
  • RubricMaker Alignment — how the profilers feed the RubricMaker grading platform.