From 7de936fc5ebed67ff527bc53e2d72eaf3291bab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?n=CE=B9c=D0=BDola=D1=95=20w=CE=B9lde?= Date: Sun, 14 Jun 2026 12:18:21 -0700 Subject: [PATCH] feat(scripts): Add find_missing_sources.py and check-missing-sources Taskfile command --- Taskfile.yaml | 4 +++ scripts/README.md | 12 +++++++- scripts/find_missing_sources.py | 51 +++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100755 scripts/find_missing_sources.py diff --git a/Taskfile.yaml b/Taskfile.yaml index 5b313363..90e83c99 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -13,6 +13,10 @@ tasks: desc: Check recipes for missing iamges. cmds: - 'uv run scripts/check_missing_images.py' + check-missing-sources: + desc: Find recipes missing a source section. + cmds: + - 'uv run scripts/find_missing_sources.py' build: desc: Build the static site cmds: diff --git a/scripts/README.md b/scripts/README.md index 4b4842a9..4a884259 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -20,6 +20,7 @@ Where possible, run these via `Taskfile.yaml` using the `task` runner. | `scripts/scrape_to_cook.py` | - | Scrapes a recipe webpage URL and compiles it into a CookLang `.cook` file while downloading the image. | | `scripts/import_recipe_workflow.py` | - | Orchestrates the entire recipe import workflow (scrape, move, emojis, units, spellcheck, whitelist). | | `scripts/whitelist_typos.py` | - | Whitelists words in the typos dictionary, sorts the file, and rebuilds typos config. | +| `scripts/find_missing_sources.py` | `task check-missing-sources` | Scans markdown recipes to find files that do not have a Source/Sources section. | --- @@ -58,7 +59,10 @@ Where possible, run these via `Taskfile.yaml` using the `task` runner. #### [import_recipe_workflow.py](import_recipe_workflow.py) * **Usage**: `uv run scripts/import_recipe_workflow.py [category]` -* **Description**: Orchestrates the entire single recipe import pipeline: scrapes a recipe URL or extracts it from a GitHub issue, moves it to the appropriate category, runs emoji verification and auto-fixing, converts volumetric measurements to weights, and runs the spellchecker (with auto-whitelisting for proper nouns). +* **Description**: Orchestrates the entire single recipe import pipeline: scrapes a recipe URL or + extracts it from a GitHub issue, moves it to the appropriate category, runs emoji verification + and auto-fixing, converts volumetric measurements to weights, and runs the spellchecker (with + auto-whitelisting for proper nouns). --- @@ -147,6 +151,12 @@ Where possible, run these via `Taskfile.yaml` using the `task` runner. * **Description**: Queries GitHub Discussions via `gh api` to locate recipes with active comments, automatically setting `comments: true` in their front-matter. +#### [find_missing_sources.py](find_missing_sources.py) + +* **Usage**: `uv run scripts/find_missing_sources.py` or `task check-missing-sources` +* **Description**: Scans all markdown recipe files under `docs/` (excluding configuration, index, and reference files) + to identify those missing a Source/Sources heading. + --- ### 4. Helper Libraries diff --git a/scripts/find_missing_sources.py b/scripts/find_missing_sources.py new file mode 100755 index 00000000..8756bb52 --- /dev/null +++ b/scripts/find_missing_sources.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +import os +import re + +def main(): + docs_dir = "docs" + exclude_files = {"index.md", "tags.md"} + exclude_dirs = {"reference", "assets"} + + # Regex to match Source or Sources with optional emoji prefix + # Matches: ## :link: Source, ## Source, ## :link: Sources, ## Sources + source_pattern = re.compile(r'^##\s+(?::[a-zA-Z0-9_-]+:\s+)?Sources?(?:\s+|$)', re.IGNORECASE) + + missing_files = [] + total_checked = 0 + + for root, dirs, files in os.walk(docs_dir): + # Exclude specific directories + dirs[:] = [d for d in dirs if d not in exclude_dirs] + + for file in files: + if not file.endswith(".md") or file in exclude_files: + continue + + file_path = os.path.join(root, file) + total_checked += 1 + + has_source = False + try: + with open(file_path, "r", encoding="utf-8") as f: + for line in f: + if source_pattern.match(line.strip()): + has_source = True + break + except Exception as e: + print(f"Error reading {file_path}: {e}") + continue + + if not has_source: + missing_files.append(file_path) + + print(f"Checked {total_checked} markdown files.") + if missing_files: + print(f"Found {len(missing_files)} file(s) missing a Source section:") + for path in sorted(missing_files): + print(f"- [ ] {path}") + else: + print("All recipes contain a Source section!") + +if __name__ == "__main__": + main()