stenodojo

stenodojo
git clone git@git.zachrice.app:repos/stenodojo.git
Log | Files | Refs

lessons.py (23230B)


      1 """Lesson structure and word frequency data for guided practice."""
      2 
      3 import random
      4 
      5 from sentence_generator import generate_sentences
      6 from wordfreq import top_n_list
      7 
      8 # Proper nouns / demonyms that wordfreq includes due to raw frequency.
      9 # These don't belong in a vocabulary practice list — steno dicts map them
     10 # to capitalized output, so practicing them lowercase is impossible.
     11 _PROPER_NOUNS = {
     12     # People names
     13     "john", "paul", "peter", "james", "david", "michael", "george", "mary",
     14     "thomas", "william", "charles", "henry", "edward", "robert", "richard",
     15     "joseph", "jack", "sam", "tom", "joe", "harry", "jane", "elizabeth",
     16     "kate", "anna", "sarah", "emma", "alice", "adam", "daniel", "steve",
     17     "alex", "ben", "max", "nick", "matt", "chris", "mike", "bob", "jim",
     18     "dan", "bill", "jeff", "ryan", "kevin", "brian", "scott", "eric",
     19     "patrick", "martin", "frank", "sean", "carl", "ray", "fred", "lee",
     20     "alan", "peter", "roger", "bruce", "ralph", "roy", "donald", "larry",
     21     "jerry", "terry", "wayne", "philip", "howard", "johnny", "tommy",
     22     "billy", "jimmy", "bobby", "charlie", "eddie", "willie", "annie",
     23     "helen", "margaret", "nancy", "ruth", "betty", "dorothy", "karen",
     24     "susan", "jessica", "lisa", "jennifer", "amy", "emily", "rachel",
     25     "laura", "marie", "victoria", "catherine", "diana", "sophie",
     26     # Countries / regions
     27     "america", "europe", "africa", "asia", "australia", "canada", "england",
     28     "britain", "france", "germany", "italy", "spain", "russia", "china",
     29     "japan", "india", "mexico", "ireland", "scotland", "wales", "israel",
     30     "iraq", "iran", "syria", "egypt", "brazil", "korea", "vietnam",
     31     "texas", "california", "florida", "york", "london", "paris",
     32     "washington", "hollywood", "broadway", "manhattan", "boston", "chicago",
     33     # Demonyms / nationalities — steno maps these capitalized
     34     "american", "americans", "european", "african", "asian", "australian",
     35     "canadian", "english", "british", "french", "german", "italian",
     36     "spanish", "russian", "chinese", "japanese", "indian", "mexican",
     37     "irish", "scottish", "korean", "dutch", "polish", "turkish", "swedish",
     38     "norwegian", "danish", "finnish", "czech", "hungarian", "portuguese",
     39     "brazilian", "thai", "vietnamese", "indonesian", "filipino", "arab",
     40     "arabic", "hebrew", "persian", "greek", "roman", "latin",
     41     # Religion / holidays
     42     "god", "jesus", "christ", "christian", "muslim", "jewish", "catholic",
     43     "bible", "christmas", "easter", "halloween",
     44     # Days / months — steno maps these capitalized
     45     "sunday", "monday", "tuesday", "wednesday", "thursday", "friday",
     46     "saturday", "january", "february", "march", "april", "june", "july",
     47     "august", "september", "october", "november", "december",
     48     # Brands / platforms
     49     "facebook", "twitter", "google", "youtube", "instagram", "amazon",
     50     "apple", "microsoft", "netflix", "uber", "disney", "walmart",
     51     "samsung", "toyota", "nike", "tesla", "bitcoin",
     52     # Political
     53     "democrat", "republican", "nazi", "obama", "trump",
     54     "olympic", "olympics", "grammy", "emmy", "oscar",
     55     # Historical
     56     "shakespeare", "darwin", "einstein", "newton", "napoleon",
     57 }
     58 
     59 # Top 10,000 English words by real-world frequency (wordfreq: Google, Wikipedia,
     60 # Reddit, Twitter, OpenSubtitles, etc.).  Filtered to alphabetic common nouns/
     61 # verbs/adjectives — no proper nouns, no single-letter junk.
     62 _raw = top_n_list("en", 10000)
     63 TOP_WORDS = [
     64     w for w in _raw
     65     if ((w.isalpha() and len(w) > 1) or w in ("a", "i"))
     66     and w not in _PROPER_NOUNS
     67 ]
     68 
     69 
     70 # Practice sentences organized by difficulty
     71 SENTENCES = {
     72     "beginner": [
     73         "The cat sat on a mat.",
     74         "I have a big red hat.",
     75         "He can run and jump.",
     76         "She will go to the store.",
     77         "We like to play in the park.",
     78         "It is a good day to go out.",
     79         "They came home from school.",
     80         "I want to read a book.",
     81         "The dog ran to the door.",
     82         "We need to get some food.",
     83         "Can you help me find my bag?",
     84         "I think we should go now.",
     85         "She put the cup on the table.",
     86         "He will come back soon.",
     87         "They like to play games.",
     88         "I need to do my work.",
     89         "The sun is up in the sky.",
     90         "We have a lot to do today.",
     91         "You can sit here with me.",
     92         "She said it was time to go.",
     93     ],
     94     "intermediate": [
     95         "The quick brown fox jumps over the lazy dog.",
     96         "I would like to know what you think about this.",
     97         "She has been working on the project all morning.",
     98         "They don't believe that it will work out in the end.",
     99         "We should try to find a better way to solve this problem.",
    100         "He doesn't seem to care about what other people think.",
    101         "I have been trying to learn how to play the piano.",
    102         "You will need to come back tomorrow for the results.",
    103         "She would like to travel around the world some day.",
    104         "They should have told us about the change in plans.",
    105         "The children played in the garden until it got dark.",
    106         "I remember when this street was just a small road.",
    107         "He could not believe what he saw through the window.",
    108         "We must find a way to make this work before the end.",
    109         "You should always try your best no matter what happens.",
    110         "The old house at the end of the street is for sale.",
    111         "I think we need to talk about what happened last night.",
    112         "She has never been to the ocean, but she wants to go.",
    113         "They were late because the train did not come on time.",
    114         "We have enough food for everyone if we share it well.",
    115     ],
    116     "advanced": [
    117         "The government announced several important changes to the education system.",
    118         "Scientists believe that the discovery could lead to significant medical advances.",
    119         "The temperature dropped suddenly, and the roads became dangerous to drive on.",
    120         "Experience has taught me that nothing worthwhile comes without considerable effort.",
    121         "The company decided to separate the research division from the main business.",
    122         "Understanding different cultures requires patience and an open mind toward others.",
    123         "The original design was modified to include additional safety features throughout.",
    124         "Modern technology has completely transformed how we communicate with each other.",
    125         "The solution to the problem required thinking about it from a different angle.",
    126         "Environmental protection has become one of the most important issues of our time.",
    127         "The committee reached a decision after several hours of careful consideration.",
    128         "Effective communication depends on both speaking clearly and listening carefully.",
    129         "The population of the city has grown considerably over the past few decades.",
    130         "Financial planning requires discipline and a clear understanding of your goals.",
    131         "The relationship between science and society continues to evolve in complex ways.",
    132     ],
    133     "literature": [
    134         # Jane Austen — Pride and Prejudice (1813)
    135         "It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife.",
    136         "She was a woman of mean understanding, little information, and uncertain temper.",
    137         "Vanity and pride are different things, though the words are often used as one.",
    138         "I could easily forgive his pride, if he had not mortified mine.",
    139         "There is a stubbornness about me that never can bear to be frightened at the will of others.",
    140         "A lady's imagination is very rapid; it jumps from admiration to love, from love to matrimony in a moment.",
    141         # Jane Austen — Sense and Sensibility (1811)
    142         "The family of Dashwood had long been settled in Sussex.",
    143         "It is not time or opportunity that is to determine intimacy; it is disposition alone.",
    144         # Charles Dickens — A Tale of Two Cities (1859)
    145         "It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness.",
    146         "A wonderful fact to reflect upon, that every human creature is constituted to be that profound secret and mystery to every other.",
    147         "It is a far, far better thing that I do, than I have ever done; it is a far, far better rest that I go to than I have ever known.",
    148         # Charles Dickens — Great Expectations (1861)
    149         "In a word, I was too cowardly to do what I knew to be right, as I had been too cowardly to avoid doing what I knew to be wrong.",
    150         "Take nothing on its looks; take everything on evidence. There is no better rule.",
    151         "We need never be ashamed of our tears, for they are rain upon the blinding dust of earth.",
    152         "Suffering has been stronger than all other teaching, and has taught me to understand what your heart used to be.",
    153         # Charles Dickens — Oliver Twist (1838)
    154         "There are books of which the backs and covers are by far the best parts.",
    155         # Charlotte Bronte — Jane Eyre (1847)
    156         "I am no bird; and no net ensnares me; I am a free human being with an independent will.",
    157         "I would always rather be happy than dignified.",
    158         "There was no possibility of taking a walk that day.",
    159         "Life appears to me too short to be spent in nursing animosity or registering wrongs.",
    160         # Emily Bronte — Wuthering Heights (1847)
    161         "He is more myself than I am. Whatever our souls are made of, his and mine are the same.",
    162         "I have not broken your heart; you have broken it; and in breaking it, you have broken mine.",
    163         "Terror made me cruel; and finding it useless to attempt shaking the creature off, I pulled its wrist on to the broken pane.",
    164         # Herman Melville — Moby Dick (1851)
    165         "Call me Ishmael. Some years ago, having little or no money in my purse, I thought I would sail about a little and see the watery part of the world.",
    166         "It is not down on any map; true places never are.",
    167         "Better to sleep with a sober cannibal than a drunken Christian.",
    168         "There is a wisdom that is woe; but there is a woe that is madness.",
    169         # Leo Tolstoy — Anna Karenina (1877, trans. Constance Garnett)
    170         "All happy families are alike; each unhappy family is unhappy in its own way.",
    171         "He stepped down, trying not to look long at her, as if she were the sun, yet he saw her, like the sun, even without looking.",
    172         "If you look for perfection, you will never be content.",
    173         # Leo Tolstoy — War and Peace (1869, trans. Constance Garnett)
    174         "The strongest of all warriors are these two: time and patience.",
    175         "We can know only that we know nothing. And that is the highest degree of human wisdom.",
    176         "Nothing is so necessary for a young man as the company of intelligent women.",
    177         # Mark Twain — The Adventures of Tom Sawyer (1876)
    178         "The less there is to justify a traditional custom, the harder it is to get rid of it.",
    179         "He had discovered a great law of human action, without knowing it, that in order to make a man covet a thing, it is only necessary to make the thing difficult to attain.",
    180         # Mark Twain — Adventures of Huckleberry Finn (1884)
    181         "All right, then, I will go to it. It was awful thoughts and awful words, but they were said.",
    182         "It does not matter what you do; he can find fault with it.",
    183         "That is just the way with some people. They get down on a thing when they do not know nothing about it.",
    184         # Mary Shelley — Frankenstein (1818)
    185         "Nothing is so painful to the human mind as a great and sudden change.",
    186         "Even broken in spirit as he is, no one can feel more deeply than he does the beauties of nature.",
    187         "Beware; for I am fearless, and therefore powerful.",
    188         "Life, although it may only be an accumulation of anguish, is dear to me, and I will defend it.",
    189         # Lewis Carroll — Alice's Adventures in Wonderland (1865)
    190         "It was all very well to say drink me, but the wise little Alice was not going to do that in a hurry.",
    191         "If everybody minded their own business, the world would go around a great deal faster than it does.",
    192         "Begin at the beginning and go on till you come to the end; then stop.",
    193         "Why, sometimes I have believed as many as six impossible things before breakfast.",
    194         # Oscar Wilde — The Picture of Dorian Gray (1890)
    195         "The only way to get rid of a temptation is to yield to it.",
    196         "Every portrait that is painted with feeling is a portrait of the artist, not of the sitter.",
    197         "I am too fond of reading books to care to write them.",
    198         "Experience is merely the name men gave to their mistakes.",
    199         "To define is to limit.",
    200         # Arthur Conan Doyle — Sherlock Holmes stories (1887-1905)
    201         "When you have eliminated the impossible, whatever remains, however improbable, must be the truth.",
    202         "There is nothing more deceptive than an obvious fact.",
    203         "The world is full of obvious things which nobody by any chance ever observes.",
    204         "It is a capital mistake to theorize before one has data.",
    205         "I am a brain, Watson. The rest of me is a mere appendage.",
    206         # H.G. Wells — The Time Machine (1895)
    207         "We should strive to welcome change and challenges, because they are the things that let us grow.",
    208         "It sounds plausible enough tonight, but wait until tomorrow. Wait for the common sense of the morning.",
    209         "There is no intelligence where there is no change and no need of change.",
    210         # H.G. Wells — The War of the Worlds (1898)
    211         "No one would have believed in the last years of the nineteenth century that this world was being watched keenly and closely by intelligences greater than ours.",
    212         "Few people realized the ordeal that was at hand. I think that the greatest wonder of the whole thing was the swiftness of it all.",
    213         # Jack London — The Call of the Wild (1903)
    214         "He was mastered by the sheer surging of life, the tidal wave of being, the perfect joy of each separate muscle, joint, and sinew.",
    215         "There is an ecstasy that marks the summit of life, and beyond which life cannot rise.",
    216         # Edgar Allan Poe — various (1830s-1840s)
    217         "All that we see or seem is but a dream within a dream.",
    218         "I became insane, with long intervals of horrible sanity.",
    219         "Deep into that darkness peering, long I stood there, wondering, fearing, doubting.",
    220         "Words have no power to impress the mind without the exquisite horror of their reality.",
    221         # Robert Louis Stevenson — Treasure Island (1883)
    222         "Fifteen men on the dead man's chest. Drink and the devil had done for the rest.",
    223         "I was far less afraid of the captain than anyone else who knew him.",
    224         # Louisa May Alcott — Little Women (1868)
    225         "I am not afraid of storms, for I am learning how to sail my ship.",
    226         "I like good strong words that mean something.",
    227         "Love is a great beautifier.",
    228         # Nathaniel Hawthorne — The Scarlet Letter (1850)
    229         "No man, for any considerable period, can wear one face to himself and another to the multitude.",
    230         "She had not known the weight until she felt the freedom.",
    231         # Bram Stoker — Dracula (1897)
    232         "There are darknesses in life and there are lights, and you are one of the lights.",
    233         "We learn from failure, not from success.",
    234         # L. Frank Baum — The Wonderful Wizard of Oz (1900)
    235         "A heart is not judged by how much you love; but by how much you are loved by others.",
    236         "No matter how dreary and gray our homes are, we people of flesh and blood would rather live there than in any other country.",
    237         # Daniel Defoe — Robinson Crusoe (1719)
    238         "Thus fear of danger is ten thousand times more terrifying than danger itself.",
    239         "All our discontents about what we want appeared to spring from the want of thankfulness for what we have.",
    240         # Jonathan Swift — Gulliver's Travels (1726)
    241         "I cannot but conclude the bulk of your natives to be the most pernicious race of little odious vermin that nature ever suffered to crawl upon the surface of the earth.",
    242         "Whoever could make two ears of corn or two blades of grass to grow where only one grew before, would deserve better of mankind.",
    243         # Homer — The Odyssey (trans. Samuel Butler, 1900)
    244         "There is a time for many words, and there is also a time for sleep.",
    245         "Of all creatures that breathe and move upon the earth, nothing is bred that is weaker than man.",
    246         # Walt Whitman — Leaves of Grass (1855)
    247         "Do I contradict myself? Very well then I contradict myself. I am large, I contain multitudes.",
    248         "I exist as I am, that is enough.",
    249         "Keep your face always toward the sunshine, and shadows will fall behind you.",
    250         # Assorted public domain (pre-1928)
    251         "The only thing we have to fear is fear itself.",
    252         "In the middle of difficulty lies opportunity.",
    253         "The mind is everything. What you think, you become.",
    254         "To be yourself in a world that is constantly trying to make you something else is the greatest accomplishment.",
    255         "It does not do to dwell on dreams and forget to live.",
    256         "We are what we repeatedly do. Excellence, then, is not an act, but a habit.",
    257         "The unexamined life is not worth living.",
    258         "Knowing yourself is the beginning of all wisdom.",
    259         "An investment in knowledge pays the best interest.",
    260         "Well done is better than well said.",
    261         "Lost time is never found again.",
    262         "Either write something worth reading or do something worth writing.",
    263         "Tell me and I forget. Teach me and I remember. Involve me and I learn.",
    264         "The journey of a thousand miles begins with a single step.",
    265     ],
    266 }
    267 
    268 
    269 LESSONS = [
    270     {
    271         "id": "basics-1",
    272         "title": "The Basics",
    273         "subtitle": "Top 100 most common words",
    274         "level": "beginner",
    275         "word_range": (0, 100),
    276         "sentences_key": "beginner",
    277         "order": 1,
    278     },
    279     {
    280         "id": "building-1",
    281         "title": "Building Up",
    282         "subtitle": "Words 101-200",
    283         "level": "intermediate",
    284         "word_range": (100, 200),
    285         "sentences_key": "intermediate",
    286         "order": 2,
    287     },
    288     {
    289         "id": "building-2",
    290         "title": "Growing Vocabulary",
    291         "subtitle": "Words 201-350",
    292         "level": "intermediate",
    293         "word_range": (200, 350),
    294         "sentences_key": "intermediate",
    295         "order": 3,
    296     },
    297     {
    298         "id": "building-3",
    299         "title": "Expanding Range",
    300         "subtitle": "Words 351-500",
    301         "level": "intermediate",
    302         "word_range": (350, 500),
    303         "sentences_key": "intermediate",
    304         "order": 4,
    305     },
    306     {
    307         "id": "phrases-1",
    308         "title": "Common Phrases",
    309         "subtitle": "Two and three word phrases",
    310         "level": "intermediate",
    311         "word_range": None,
    312         "mode": "phrases",
    313         "sentences_key": "intermediate",
    314         "order": 5,
    315     },
    316     {
    317         "id": "advanced-1",
    318         "title": "Advanced Words",
    319         "subtitle": "Words 501-700",
    320         "level": "advanced",
    321         "word_range": (500, 700),
    322         "sentences_key": "advanced",
    323         "order": 6,
    324     },
    325     {
    326         "id": "advanced-2",
    327         "title": "Expert Words",
    328         "subtitle": "Words 701-1000",
    329         "level": "advanced",
    330         "word_range": (700, 1000),
    331         "sentences_key": "advanced",
    332         "order": 7,
    333     },
    334     {
    335         "id": "advanced-3",
    336         "title": "Uncommon Words",
    337         "subtitle": "Words 1001-1500",
    338         "level": "advanced",
    339         "word_range": (1000, 1500),
    340         "order": 8,
    341     },
    342     {
    343         "id": "expert-1",
    344         "title": "Expert Vocabulary",
    345         "subtitle": "Words 1501-2500",
    346         "level": "expert",
    347         "word_range": (1500, 2500),
    348         "order": 9,
    349     },
    350     {
    351         "id": "expert-2",
    352         "title": "Broad Vocabulary",
    353         "subtitle": "Words 2501-4000",
    354         "level": "expert",
    355         "word_range": (2500, 4000),
    356         "order": 10,
    357     },
    358     {
    359         "id": "expert-3",
    360         "title": "Deep Vocabulary",
    361         "subtitle": "Words 4001-6000",
    362         "level": "expert",
    363         "word_range": (4000, 6000),
    364         "order": 11,
    365     },
    366     {
    367         "id": "master-1",
    368         "title": "Master I",
    369         "subtitle": "Words 6001-8000",
    370         "level": "master",
    371         "word_range": (6000, 8000),
    372         "order": 12,
    373     },
    374     {
    375         "id": "master-2",
    376         "title": "Master II",
    377         "subtitle": "Words 8001-10000",
    378         "level": "master",
    379         "word_range": (8000, 10000),
    380         "order": 13,
    381     },
    382     {
    383         "id": "sentences-1",
    384         "title": "Real Sentences",
    385         "subtitle": "Literature and real-world text",
    386         "level": "advanced",
    387         "word_range": None,
    388         "mode": "sentences",
    389         "sentences_key": "literature",
    390         "order": 14,
    391     },
    392 ]
    393 
    394 
    395 def get_lesson_words(lesson, engine, count=10):
    396     """Get practice content for a lesson.
    397 
    398     Word-range lessons generate sentences from their cumulative word pool.
    399     The literature lesson returns randomized public domain passages.
    400     Phrase lessons return multi-word phrases.
    401     """
    402     if lesson.get("mode") == "phrases":
    403         phrases = engine.all_phrases
    404         phrasing = engine.generate_phrasing_phrases(200)
    405         phrases = phrases + phrasing
    406         random.shuffle(phrases)
    407         return phrases[:50]
    408 
    409     if lesson.get("mode") == "sentences":
    410         key = lesson.get("sentences_key", "literature")
    411         sents = list(SENTENCES.get(key, SENTENCES["literature"]))
    412         random.shuffle(sents)
    413         return sents[:count]
    414 
    415     word_range = lesson.get("word_range")
    416     if word_range:
    417         start, end = word_range
    418         cumulative = TOP_WORDS[:end]
    419         pool = [w for w in cumulative if engine.word_exists(w)]
    420         if not pool:
    421             pool = list(cumulative)
    422         return generate_sentences(pool, count=count)
    423 
    424     return []
    425 
    426 
    427 def get_sentences(level="beginner", count=10):
    428     sents = list(SENTENCES.get(level, SENTENCES["beginner"]))
    429     random.shuffle(sents)
    430     return sents[:count]
    431 
    432 
    433 def get_lesson_list():
    434     return [
    435         {
    436             "id": l["id"],
    437             "title": l["title"],
    438             "subtitle": l["subtitle"],
    439             "level": l["level"],
    440             "order": l["order"],
    441             "mode": l.get("mode", "sentences"),
    442         }
    443         for l in LESSONS
    444     ]
    445 
    446 
    447 def get_lesson_word_list(lesson):
    448     """Return the raw word list for a word-range lesson (for progress tracking)."""
    449     word_range = lesson.get("word_range")
    450     if word_range:
    451         start, end = word_range
    452         return TOP_WORDS[start:end]
    453     return []
    454 
    455 
    456 def get_lesson_by_id(lesson_id):
    457     for l in LESSONS:
    458         if l["id"] == lesson_id:
    459             return l
    460     return None