Skip to content
v0.1.4latest release159 skills5 categories7 sub-categories303 tested scripts468 reference docsContact us

cando_lint.py

Helper script for can-do-statement-writer · 94 lines

Languages, Linguistics & Translation / Language Teachingv1.0.0Updated OCT 25 20262 references1 script
scripts/cando_lint.py
#!/usr/bin/env python3
"""Lint can-do statements: learner ability, observable action, context, one activity, quality, communicative purpose.

Input: one statement per line. Each is classified by mode (reception, production, interaction, mediation) from its
verb; a set that misses a mode is reported.

Checks:
  CD001 high    not phrased as learner ability ('I can' / 'Can' / 'Learners can')
  CD002 high    knowledge, not use: know, learn, be aware of, be familiar with, understand + a grammar term
  CD003 medium  no context (topic, text type, situation or audience)
  CD004 medium  two activities in one statement (read and write, listen and speak)
  CD005 low     vague quality word (well, correctly, fluently, properly) without a condition
  CD006 low     longer than 30 words
  CD007 low     names a grammar item as the outcome without a purpose ('use the past simple')
  CD008 low     the set has no statement for a mode

Usage:
    python cando_lint.py statements.txt

Exit codes: 0 no high findings, 1 high findings, 2 read error or no statements.
"""

import argparse
import re
import sys

START = re.compile(r"^(i can|can|learners can|students can|the learner can|we can)\b", re.I)
KNOW = re.compile(r"\b(know|learn|be aware of|be familiar with|recogni[sz]e the (form|rule))\b|\bunderstand (the )?"
                  r"(grammar|tense|present|past|future|conditional|passive|rules?|modals?|articles?)\b", re.I)
CONTEXT = re.compile(r"\b(about|on|in|at|for|with|from|when|during|to|if|using|such as|e\.g\.)\b", re.I)
GRAMMAR = re.compile(r"\buse (the |a |an )?(present|past|future|second|first|third|zero|passive|modal|article|"
                     r"comparative|superlative|conditional|gerund|infinitive|relative)\b", re.I)
VAGUE = re.compile(r"\b(well|correctly|fluently|properly|effectively|accurately|perfectly|appropriately)\b", re.I)
CONDITION = re.compile(r"\b(if|with (some|little|no|support|help)|when|provided|slowly|without)\b", re.I)
MODES = {
    "reception": r"\b(understand|read|listen|follow|identify|recogni[sz]e|find|scan|catch|watch|get the gist)",
    "interaction": r"\b(ask|answer|take part|discuss|exchange|chat|interact|negotiate|agree|respond|reply|order|book|"
                   r"make (a |an )?(request|arrangement|complaint|appointment)|participate|greet)",
    "mediation": r"\b(summari[sz]e|explain (to|what|the (main|gist|meaning|idea))|relay|translate|paraphrase|mediate|pass on|interpret|"
                 r"report (back|what)|facilitate)",
    "production": r"\b(write|speak|describe|tell|give (a )?(talk|presentation|reasons|opinion)|present|introduce|narrate|"
                  r"produce|report|express|talk about|say|fill in|complete (a )?form)",
}
TWO = re.compile(r"\b(read|write|listen|speak|talk)\b.*\b(and|&)\b.*\b(read|write|listen|speak|talk)\b", re.I)


def main():
    ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("statements", help="text file, one statement per line, or - for stdin")
    a = ap.parse_args()
    try:
        lines = (sys.stdin if a.statements == "-" else open(a.statements, encoding="utf-8")).read().splitlines()
    except OSError as e:
        print(f"error: {e}", file=sys.stderr)
        return 2
    items = [re.sub(r"^\s*(\d+[.)]|[-*])\s*", "", l).strip() for l in lines if l.strip() and not l.startswith("#")]
    if not items:
        print("error: no statements", file=sys.stderr)
        return 2
    f, seen = [], set()
    for i, s in enumerate(items, 1):
        mode = next((m for m, p in MODES.items() if re.search(p, s, re.I)), "?")
        seen.add(mode)
        print(f"[{i}] ({mode}) {s}")
        body = START.sub("", s).strip()
        if not START.search(s):
            f.append(("CD001", "high", f"[{i}] start with 'I can' (learner) or 'Can' (descriptor)"))
        if KNOW.search(s):
            f.append(("CD002", "high", f"[{i}] '{KNOW.search(s).group(0)}' is knowledge; say what the learner does with it"))
        if not CONTEXT.search(body) or len(body.split()) < 4:
            f.append(("CD003", "medium", f"[{i}] add a context: topic, text type, situation or audience"))
        if TWO.search(s):
            f.append(("CD004", "medium", f"[{i}] two activities in one statement: split it"))
        if VAGUE.search(s) and not CONDITION.search(s):
            f.append(("CD005", "low", f"[{i}] '{VAGUE.search(s).group(0)}' is vague: describe quality (e.g. 'with some hesitation', 'if speech is slow')"))
        if len(s.split()) > 30:
            f.append(("CD006", "low", f"[{i}] {len(s.split())} words: shorten to one clear outcome"))
        if GRAMMAR.search(s) and not re.search(r"\bto (talk|describe|say|ask|tell|write|explain|give|make|compare)\b", s, re.I):
            f.append(("CD007", "low", f"[{i}] names a grammar item: say what it is used for (e.g. 'to talk about...')"))
    for m in MODES:
        if m not in seen and len(items) >= 4:
            f.append(("CD008", "low", f"no {m} statement in the set"))
    print()
    order = {"high": 0, "medium": 1, "low": 2}
    for code, sev, msg in sorted(f, key=lambda x: (order[x[1]], x[0])):
        print(f"{code} [{sev}] {msg}")
    if not f:
        print("OK: all statements are observable, contextualised learner outcomes")
    return 1 if any(x[1] == "high" for x in f) else 0


if __name__ == "__main__":
    sys.exit(main())
More in Language Teaching

academic-english-eap-course-planner

Plan English for Academic Purposes (EAP) courses: pre-sessional, in-sessional, foundation, IELTS preparation and discipline-specific EAP, with syllabus strands (academic reading, lecture listening and note-taking, essay and report writing, paraphrase, summary and synthesis, referencing and plagiarism, seminars and presentations, academic vocabulary, critical thinking), a 10-week pre-sessional outline, exit assessment, IELTS-CEFR alignment and academic style (formality, hedging, cohesion, reporting verbs). Estimates study hours and weeks to close an IELTS gap from overall or per-skill scores (IELTS rounding, minimum per skill, priority skills, feasibility before a deadline), and checks learner texts for contractions, informal words with academic alternatives, unhedged claims, 'you', And/But/So starts, rhetorical questions and missing citations.

actfl-proficiency-curriculum-mapper

Map world language curricula to the ACTFL framework: proficiency levels (Novice, Intermediate, Advanced, Superior, Distinguished with sublevels), functions, contexts, text types and accuracy, proficiency vs performance, the three modes of communication (interpretive, interpersonal, presentational), the 5 Cs World-Readiness Standards, backward-designed thematic units with Integrated Performance Assessments, typical programme targets and FSI time estimates. Looks up approximate ACTFL-CEFR-ILR equivalences with what each level can do, and checks a unit map for missing modes, rehearsed skits mislabelled as interpersonal, translation or word lists as interpretive tasks, and units without an integrated performance assessment.

adult-literacy-esl-lesson-builder

Build lessons for adult ESL learners who are developing literacy (LESLLA learners: pre-literate, non-literate, semi-literate, literate in a non-Roman script): learner profiles and sensitive initial assessment, principles (oral language first, known to new, systematic phonics with adult-relevant text, repetition, classroom skills, respect for experience), a lesson routine (oral warm-up, review, new grapheme, blending, segmenting, decodable text, writing, functional literacy, Language Experience Approach), a phonics sequence for adults, sight words, functional literacy targets (forms, signs, medicine labels) and portfolio assessment. Checks whether a reading text is decodable with the letter-sound patterns and sight words taught so far, catching untaught digraphs, listing non-decodable words with the untaught part, and suggesting the most useful next patterns.

authentic-material-lesson-adapter

Turn authentic material (news articles, podcasts, videos, songs, menus, timetables, adverts, social media posts, forms, infographics) into language lessons by grading the task rather than the text. Selects texts for interest, relevance, exploitability, coverage, cultural load and rights; finds the 4-6 words to pre-teach, words to gloss on the page and words learners can ignore, plus names and cultural references to explain, with reading time by level; and builds a lesson shape (lead-in, pre-teach, gist or scanning, graded detail task, language focus on chunks and register, response) with task ideas per material type and level from A1 to C1. Covers copyright, fair dealing and fair use, Creative Commons and attribution.

cefr-aligned-syllabus-builder

Build language course syllabuses aligned to the CEFR (A1-C2, plus levels, Companion Volume 2020): entry and exit levels, guided learning hours and course length (with an L1-distance adjustment), syllabus type (topic, functional-notional, task-based, skills-based, multi-strand), unit-by-unit can-do outcomes, grammar from a core inventory by level, vocabulary, functions, pronunciation, balance of reception, production, interaction and mediation, recycling and assessment points. Checks a syllabus for units without can-do statements, core grammar for the level not covered, items above the course level, skills missing from most units and units with no vocabulary or functions.

classroom-management-for-esl-coach

Coach ESL/EFL teachers in classroom management: the instruction sequence (attention, group first, graded imperatives, demonstration, ICQs, hand out, start, check), ICQ examples, online instructions, routines for the first weeks (class agreement, L1 use, phones, lateness, homework, seating), strategies for L1 use in tasks, dominant and quiet learners, mixed levels, large classes, fast finishers, low motivation, disruptive young learners and teens, cultural expectations, classroom language to teach early and teacher presence. Checks planned instructions for length, missing ICQs, useless ICQs like 'Do you understand?', no demonstration, long sentences, filler and missing grouping or timing, and builds seating plans that keep named learners apart, seat learners who need the front, mix first languages and alternate stronger and weaker learners.