From 6cd42b1e126c2259bf12800a10086d8227475b85 Mon Sep 17 00:00:00 2001 From: Sochen Date: Wed, 4 Mar 2026 07:07:28 +0000 Subject: [PATCH] Sprint 5: dark mode CSS, alternate conjugation forms, README releases link fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add @media (prefers-color-scheme: dark) block to CARD_CSS covering all hardcoded colors - _parse_table: add table_el param to parse a specific table directly - _extract_conjugations: detect second active conjugation table; store alternate_forms - build_conj_deck: show "primary / alternate" when alternate form exists for a key - README: fix dead ../../releases link → git.nevo.engineer/nevo/pealim/releases Co-Authored-By: Claude Sonnet 4.6 --- README.md | 2 +- apkg_builder.py | 18 ++- conjugation_extract.py | 31 +++- data/conjugations.json | 356 ++++++++++++++++++++--------------------- 4 files changed, 226 insertions(+), 181 deletions(-) diff --git a/README.md b/README.md index f98d5ca..8807f5a 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ All card data comes from open or academic sources: ## Just give me the flashcards -1. Download the `.apkg` files from [Releases](../../releases) +1. Download the `.apkg` files from [Releases](https://git.nevo.engineer/nevo/pealim/releases) 2. Double-click to import into [Anki](https://apps.ankiweb.net/) (free, cross-platform) 3. Start studying diff --git a/apkg_builder.py b/apkg_builder.py index c075211..537d6eb 100644 --- a/apkg_builder.py +++ b/apkg_builder.py @@ -159,6 +159,18 @@ CARD_CSS = """ margin: 2px 0; font-size: 15px; } +@media (prefers-color-scheme: dark) { + .card { color: #e8e8e8; background: #1c1c1e; } + .hebrew { color: #f0f0f0; } + .hebrew-sm { color: #ddd; } + .meaning { color: #82b0ff; } + .root-info { color: #aaa; } + .sec-label { color: #aaa; } + .voice-label { color: #888; } + .example { color: #bbb; border-right-color: #555; } + .divider { border-top-color: #333; } + .freq-badge { color: #888; border-color: #444; } +} """ # ────────────────────────────────────────────────────────────────────────────── @@ -575,8 +587,12 @@ def build_conj_deck( deck.add_note(note) note_count += 1 + alternate_forms = data.get("alternate_forms", {}) + for form_key, form_data in forms.items(): - conj_form = form_data.get("form", "") + primary_form = form_data.get("form", "") + alt_form = alternate_forms.get(form_key, "") + conj_form = f"{primary_form} / {alt_form}" if alt_form else primary_form audio_url_for_key = form_data.get("audio_url", "") # Infinitive: shown on card front as reference — skip as a quiz form diff --git a/conjugation_extract.py b/conjugation_extract.py index 9e3434a..b2f883a 100755 --- a/conjugation_extract.py +++ b/conjugation_extract.py @@ -218,10 +218,11 @@ def _get_menukad(cell) -> tuple[str, str]: return "", audio_url -def _parse_table(soup: BeautifulSoup, passive: bool = False) -> dict[str, dict]: +def _parse_table(soup: BeautifulSoup, passive: bool = False, table_el=None) -> dict[str, dict]: """ Parse the pealim conjugation table and return form_key -> {form, audio_url} mapping. If passive=True, look for the passive table (after "Passive" heading). + If table_el is provided (and passive=False), parse that table directly. """ if passive: # Find

containing "Passive" @@ -240,6 +241,8 @@ def _parse_table(soup: BeautifulSoup, passive: bool = False) -> dict[str, dict]: break if not table: return {} + elif table_el is not None: + table = table_el else: table = soup.find("table", class_="conjugation-table") @@ -480,6 +483,32 @@ def _extract_conjugations(slug: str, search_term: str, is_3ms_search: bool = Fal "tense": TENSE_DESCRIPTION.get(key, ""), } + # Check for a second conjugation table (alternate paradigm, e.g. להתגלות) + # Collect all active tables (exclude passive tables which follow the "Passive" h3) + passive_h3 = next( + (h for h in soup.find_all("h3") if "passive" in h.get_text(strip=True).lower()), + None, + ) + passive_table_ids = { + id(t) for t in (passive_h3.find_all_next("table", class_="conjugation-table") if passive_h3 else []) + } + active_tables = [ + t for t in soup.find_all("table", class_="conjugation-table") + if id(t) not in passive_table_ids + ] + if len(active_tables) >= 2: + alt_raw = _parse_table(soup, passive=False, table_el=active_tables[1]) + alternate_forms = {} + for key, form_data in alt_raw.items(): + if key in PRONOUN_LABELS: + alt_form = form_data["form"] + primary_form = forms_raw.get(key, {}).get("form", "") + if alt_form and alt_form != primary_form: + alternate_forms[key] = alt_form + if alternate_forms: + result["alternate_forms"] = alternate_forms + logger.info(f" Found {len(alternate_forms)} alternate forms for {search_term}") + logger.info(f" Extracted {len(result['forms'])} forms for {search_term}") return result diff --git a/data/conjugations.json b/data/conjugations.json index ca2ecd2..11aeb8a 100644 --- a/data/conjugations.json +++ b/data/conjugations.json @@ -8667,184 +8667,6 @@ } } }, - "להתגלות": { - "infinitive": "להתגלות", - "slug": "332-lehitgalot", - "root": "ג - ל - ה", - "binyan": "Hitpa'el", - "is_passive": false, - "reference_form": "לְהִתְגַּלּוֹת", - "forms": { - "present_ms": { - "form": "מִתְגַּלֶּה", - "audio_url": "https://audio.pealim.com/v0/17/17aik28prm3ox.mp3", - "pronoun": "", - "tense": "הוֹוֶה" - }, - "present_fs": { - "form": "מִתְגַּלָּה", - "audio_url": "https://audio.pealim.com/v0/17/17a2ezsn3tstn.mp3", - "pronoun": "", - "tense": "הוֹוֶה" - }, - "present_mp": { - "form": "מִתְגַּלִּים", - "audio_url": "https://audio.pealim.com/v0/ic/ic1fbwv4cmec.mp3", - "pronoun": "", - "tense": "הוֹוֶה" - }, - "present_fp": { - "form": "מִתְגַּלּוֹת", - "audio_url": "https://audio.pealim.com/v0/1x/1xkipm6geolmn.mp3", - "pronoun": "", - "tense": "הוֹוֶה" - }, - "past_1s": { - "form": "נִתְגַּלֵּיתִי", - "audio_url": "https://audio.pealim.com/v0/8s/8slivd19nhsg.mp3", - "pronoun": "אֲנִי", - "tense": "עָבָר" - }, - "past_1p": { - "form": "נִתְגַּלֵּינוּ", - "audio_url": "https://audio.pealim.com/v0/9r/9r6dsilbbfoq.mp3", - "pronoun": "אֲנַחְנוּ", - "tense": "עָבָר" - }, - "past_2ms": { - "form": "נִתְגַּלֵּיתָ", - "audio_url": "https://audio.pealim.com/v0/1e/1ephuiupr8yqt.mp3", - "pronoun": "אַתָּה", - "tense": "עָבָר" - }, - "past_2fs": { - "form": "נִתְגַּלֵּית", - "audio_url": "https://audio.pealim.com/v0/xb/xbkrr0255b0s.mp3", - "pronoun": "אַתְּ", - "tense": "עָבָר" - }, - "past_2mp": { - "form": "נִתְגַּלֵּיתֶם", - "audio_url": "https://audio.pealim.com/v0/ht/ht8tvie1mfln.mp3", - "pronoun": "אַתֶּם", - "tense": "עָבָר" - }, - "past_2fp": { - "form": "נִתְגַּלֵּיתֶן", - "audio_url": "https://audio.pealim.com/v0/5u/5udjkfd31qpk.mp3", - "pronoun": "אַתֶּן", - "tense": "עָבָר" - }, - "past_3ms": { - "form": "נִתְגַּלָּה", - "audio_url": "https://audio.pealim.com/v0/1y/1y107sic2yaoe.mp3", - "pronoun": "הוּא", - "tense": "עָבָר" - }, - "past_3fs": { - "form": "נִתְגַּלְּתָה", - "audio_url": "https://audio.pealim.com/v0/11/11zheoh29qyfq.mp3", - "pronoun": "הִיא", - "tense": "עָבָר" - }, - "past_3p": { - "form": "נִתְגַּלּוּ", - "audio_url": "https://audio.pealim.com/v0/1x/1xuof8hxr9c8d.mp3", - "pronoun": "הֵם / הֵן", - "tense": "עָבָר" - }, - "future_1s": { - "form": "אֶתְגַּלֶּה", - "audio_url": "https://audio.pealim.com/v0/1g/1gnwp3ylk9wr8.mp3", - "pronoun": "אֲנִי", - "tense": "עָתִיד" - }, - "future_1p": { - "form": "נִתְגַּלֶּה", - "audio_url": "https://audio.pealim.com/v0/1y/1y1gcuyeqqljo.mp3", - "pronoun": "אֲנַחְנוּ", - "tense": "עָתִיד" - }, - "future_2ms": { - "form": "תִּתְגַּלֶּה", - "audio_url": "https://audio.pealim.com/v0/1k/1kk6fs1tcnua2.mp3", - "pronoun": "אַתָּה", - "tense": "עָתִיד" - }, - "future_2fs": { - "form": "תִּתְגַּלִּי", - "audio_url": "https://audio.pealim.com/v0/1k/1kjrlkkpqslgf.mp3", - "pronoun": "אַתְּ", - "tense": "עָתִיד" - }, - "future_2mp": { - "form": "תִּתְגַּלּוּ", - "audio_url": "https://audio.pealim.com/v0/1k/1kqydeiac53ld.mp3", - "pronoun": "אַתֶּם", - "tense": "עָתִיד" - }, - "future_2fp": { - "form": "תִּתְגַּלֶּינָה", - "audio_url": "https://audio.pealim.com/v0/te/tetf67m6olwq.mp3", - "pronoun": "אַתֶּן", - "tense": "עָתִיד" - }, - "future_3ms": { - "form": "יִתְגַּלֶּה", - "audio_url": "https://audio.pealim.com/v0/nk/nktxxd761qhj.mp3", - "pronoun": "הוּא", - "tense": "עָתִיד" - }, - "future_3fs": { - "form": "תִּתְגַּלֶּה", - "audio_url": "https://audio.pealim.com/v0/1k/1kk6fs1tcnua2.mp3", - "pronoun": "הִיא", - "tense": "עָתִיד" - }, - "future_3mp": { - "form": "יִתְגַּלּוּ", - "audio_url": "https://audio.pealim.com/v0/nr/nrlvjto5izsu.mp3", - "pronoun": "הֵם", - "tense": "עָתִיד" - }, - "future_3fp": { - "form": "תִּתְגַּלֶּינָה", - "audio_url": "https://audio.pealim.com/v0/te/tetf67m6olwq.mp3", - "pronoun": "הֵן", - "tense": "עָתִיד" - }, - "imperative_ms": { - "form": "הִתְגַּלֵּה!‏", - "audio_url": "https://audio.pealim.com/v0/1d/1dpfnmygco8oz.mp3", - "pronoun": "אַתָּה", - "tense": "צִוּוּי" - }, - "imperative_fs": { - "form": "הִתְגַּלִּי!‏", - "audio_url": "https://audio.pealim.com/v0/1d/1dpmfb7imnc19.mp3", - "pronoun": "אַתְּ", - "tense": "צִוּוּי" - }, - "imperative_mp": { - "form": "הִתְגַּלּוּ!‏", - "audio_url": "https://audio.pealim.com/v0/1d/1difnh9y1atwb.mp3", - "pronoun": "אַתֶּם", - "tense": "צִוּוּי" - }, - "imperative_fp": { - "form": "הִתְגַּלֶּינָה!‏", - "audio_url": "https://audio.pealim.com/v0/wx/wxh6vsca0qyi.mp3", - "pronoun": "אַתֶּן", - "tense": "צִוּוּי" - }, - "infinitive": { - "form": "לְהִתְגַּלּוֹת", - "audio_url": "https://audio.pealim.com/v0/1o/1ocngj7nlgsu7.mp3", - "pronoun": "", - "tense": "מְקוֹר" - } - } - }, "להתקלקל": { "infinitive": "להתקלקל", "slug": "1906-lehitkalkel", @@ -14720,5 +14542,183 @@ "tense": "מְקוֹר" } } + }, + "להתגלות": { + "infinitive": "להתגלות", + "slug": "332-lehitgalot", + "root": "ג - ל - ה", + "binyan": "Hitpa'el", + "is_passive": false, + "reference_form": "לְהִתְגַּלּוֹת", + "forms": { + "present_ms": { + "form": "מִתְגַּלֶּה", + "audio_url": "https://audio.pealim.com/v0/17/17aik28prm3ox.mp3", + "pronoun": "", + "tense": "הוֹוֶה" + }, + "present_fs": { + "form": "מִתְגַּלָּה", + "audio_url": "https://audio.pealim.com/v0/17/17a2ezsn3tstn.mp3", + "pronoun": "", + "tense": "הוֹוֶה" + }, + "present_mp": { + "form": "מִתְגַּלִּים", + "audio_url": "https://audio.pealim.com/v0/ic/ic1fbwv4cmec.mp3", + "pronoun": "", + "tense": "הוֹוֶה" + }, + "present_fp": { + "form": "מִתְגַּלּוֹת", + "audio_url": "https://audio.pealim.com/v0/1x/1xkipm6geolmn.mp3", + "pronoun": "", + "tense": "הוֹוֶה" + }, + "past_1s": { + "form": "נִתְגַּלֵּיתִי", + "audio_url": "https://audio.pealim.com/v0/8s/8slivd19nhsg.mp3", + "pronoun": "אֲנִי", + "tense": "עָבָר" + }, + "past_1p": { + "form": "נִתְגַּלֵּינוּ", + "audio_url": "https://audio.pealim.com/v0/9r/9r6dsilbbfoq.mp3", + "pronoun": "אֲנַחְנוּ", + "tense": "עָבָר" + }, + "past_2ms": { + "form": "נִתְגַּלֵּיתָ", + "audio_url": "https://audio.pealim.com/v0/1e/1ephuiupr8yqt.mp3", + "pronoun": "אַתָּה", + "tense": "עָבָר" + }, + "past_2fs": { + "form": "נִתְגַּלֵּית", + "audio_url": "https://audio.pealim.com/v0/xb/xbkrr0255b0s.mp3", + "pronoun": "אַתְּ", + "tense": "עָבָר" + }, + "past_2mp": { + "form": "נִתְגַּלֵּיתֶם", + "audio_url": "https://audio.pealim.com/v0/ht/ht8tvie1mfln.mp3", + "pronoun": "אַתֶּם", + "tense": "עָבָר" + }, + "past_2fp": { + "form": "נִתְגַּלֵּיתֶן", + "audio_url": "https://audio.pealim.com/v0/5u/5udjkfd31qpk.mp3", + "pronoun": "אַתֶּן", + "tense": "עָבָר" + }, + "past_3ms": { + "form": "נִתְגַּלָּה", + "audio_url": "https://audio.pealim.com/v0/1y/1y107sic2yaoe.mp3", + "pronoun": "הוּא", + "tense": "עָבָר" + }, + "past_3fs": { + "form": "נִתְגַּלְּתָה", + "audio_url": "https://audio.pealim.com/v0/11/11zheoh29qyfq.mp3", + "pronoun": "הִיא", + "tense": "עָבָר" + }, + "past_3p": { + "form": "נִתְגַּלּוּ", + "audio_url": "https://audio.pealim.com/v0/1x/1xuof8hxr9c8d.mp3", + "pronoun": "הֵם / הֵן", + "tense": "עָבָר" + }, + "future_1s": { + "form": "אֶתְגַּלֶּה", + "audio_url": "https://audio.pealim.com/v0/1g/1gnwp3ylk9wr8.mp3", + "pronoun": "אֲנִי", + "tense": "עָתִיד" + }, + "future_1p": { + "form": "נִתְגַּלֶּה", + "audio_url": "https://audio.pealim.com/v0/1y/1y1gcuyeqqljo.mp3", + "pronoun": "אֲנַחְנוּ", + "tense": "עָתִיד" + }, + "future_2ms": { + "form": "תִּתְגַּלֶּה", + "audio_url": "https://audio.pealim.com/v0/1k/1kk6fs1tcnua2.mp3", + "pronoun": "אַתָּה", + "tense": "עָתִיד" + }, + "future_2fs": { + "form": "תִּתְגַּלִּי", + "audio_url": "https://audio.pealim.com/v0/1k/1kjrlkkpqslgf.mp3", + "pronoun": "אַתְּ", + "tense": "עָתִיד" + }, + "future_2mp": { + "form": "תִּתְגַּלּוּ", + "audio_url": "https://audio.pealim.com/v0/1k/1kqydeiac53ld.mp3", + "pronoun": "אַתֶּם", + "tense": "עָתִיד" + }, + "future_2fp": { + "form": "תִּתְגַּלֶּינָה", + "audio_url": "https://audio.pealim.com/v0/te/tetf67m6olwq.mp3", + "pronoun": "אַתֶּן", + "tense": "עָתִיד" + }, + "future_3ms": { + "form": "יִתְגַּלֶּה", + "audio_url": "https://audio.pealim.com/v0/nk/nktxxd761qhj.mp3", + "pronoun": "הוּא", + "tense": "עָתִיד" + }, + "future_3fs": { + "form": "תִּתְגַּלֶּה", + "audio_url": "https://audio.pealim.com/v0/1k/1kk6fs1tcnua2.mp3", + "pronoun": "הִיא", + "tense": "עָתִיד" + }, + "future_3mp": { + "form": "יִתְגַּלּוּ", + "audio_url": "https://audio.pealim.com/v0/nr/nrlvjto5izsu.mp3", + "pronoun": "הֵם", + "tense": "עָתִיד" + }, + "future_3fp": { + "form": "תִּתְגַּלֶּינָה", + "audio_url": "https://audio.pealim.com/v0/te/tetf67m6olwq.mp3", + "pronoun": "הֵן", + "tense": "עָתִיד" + }, + "imperative_ms": { + "form": "הִתְגַּלֵּה!‏", + "audio_url": "https://audio.pealim.com/v0/1d/1dpfnmygco8oz.mp3", + "pronoun": "אַתָּה", + "tense": "צִוּוּי" + }, + "imperative_fs": { + "form": "הִתְגַּלִּי!‏", + "audio_url": "https://audio.pealim.com/v0/1d/1dpmfb7imnc19.mp3", + "pronoun": "אַתְּ", + "tense": "צִוּוּי" + }, + "imperative_mp": { + "form": "הִתְגַּלּוּ!‏", + "audio_url": "https://audio.pealim.com/v0/1d/1difnh9y1atwb.mp3", + "pronoun": "אַתֶּם", + "tense": "צִוּוּי" + }, + "imperative_fp": { + "form": "הִתְגַּלֶּינָה!‏", + "audio_url": "https://audio.pealim.com/v0/wx/wxh6vsca0qyi.mp3", + "pronoun": "אַתֶּן", + "tense": "צִוּוּי" + }, + "infinitive": { + "form": "לְהִתְגַּלּוֹת", + "audio_url": "https://audio.pealim.com/v0/1o/1ocngj7nlgsu7.mp3", + "pronoun": "", + "tense": "מְקוֹר" + } + } } } \ No newline at end of file