Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Copilot Instructions for scout-merit-badges-anki
# Copilot Instructions for scout-anki

## Repository Overview

This is a Python CLI tool that generates Anki flashcard decks (.apkg files) for learning Scouting America merit badges by image. The tool processes local archive files (.zip, .tar.gz) containing merit badge data and images, maps badges to their corresponding images using sophisticated pattern matching, and creates structured Anki decks with stable IDs to prevent duplicates on reimport.
This is a Python CLI tool that generates Anki flashcard decks (.apkg files) for Scouting content. Supports learning Scouting America merit badges and Cub Scout adventures by image. The tool processes local archive files (.tar.gz) containing badge/adventure data and images, maps content to images using direct `image_filename` field mapping, and creates structured Anki decks with stable IDs to prevent duplicates on reimport.

**Repository Stats:**
- Language: Python 3.11+ (currently using 3.12)
Expand Down Expand Up @@ -59,7 +59,13 @@ make cov-report
uv run pytest --cov-report=html && open htmlcov/index.html

# Run CLI tool for testing
uv run scout-merit-badges-anki --help
uv run scout-anki --help

# Build merit badge deck
uv run scout-anki build merit-badges extracted/

# Build cub adventure deck
uv run scout-anki build cub-adventures extracted/
```

### Pre-commit Hooks (MANDATORY)
Expand All @@ -79,7 +85,7 @@ uv run pre-commit run --all-files
### Validation Commands
```bash
# Test CLI functionality (dry run - safe to run)
uv run scout-merit-badges-anki build --dry-run --quiet *.zip *.tar.gz
uv run scout-anki build --dry-run --quiet *.zip *.tar.gz

# Download and test with latest release
make fetch-and-build
Expand Down Expand Up @@ -115,7 +121,7 @@ make clean # Removes build artifacts, .venv, __pycache__, *.apkg files, coverag
tests/
├── test_cli_simple.py # Basic CLI tests (4 tests)
├── test_cli_comprehensive.py # CLI functionality with mocking (2 tests)
└── test_scout_merit_badges_anki.py # Directory processing integration (2 tests)
└── test_scout_anki.py # Directory processing integration (2 tests)
```

### Coverage by Module
Expand Down Expand Up @@ -152,7 +158,7 @@ When adding new functionality:

## Project Architecture

### Core Modules (`scout_merit_badges_anki/`)
### Core Modules (`scout_anki/`)
- `cli.py` - Click-based command line interface with build command
- `archive.py` - Archive processing (ZIP/TAR.GZ) and file extraction
- `mapping.py` - Sophisticated image-to-badge mapping logic with pattern matching
Expand Down Expand Up @@ -213,7 +219,7 @@ Uses deterministic hashing in `schema.py` to generate stable Anki model/deck IDs
```bash
# 1. Clone repository
git clone <repo-url>
cd scout-merit-badges-anki
cd scout-anki

# 2. Set up environment (REQUIRED)
uv sync
Expand Down Expand Up @@ -243,7 +249,7 @@ git commit -m "Your commit message"
### Testing Changes
```bash
# Test CLI functionality without creating files
uv run scout-merit-badges-anki build --dry-run *.zip *.tar.gz
uv run scout-anki build --dry-run *.zip *.tar.gz

# Download and test with latest release
make fetch-and-build
Expand Down Expand Up @@ -295,11 +301,11 @@ make test
## File Structure Reference

```
scout-merit-badges-anki/
scout-anki/
├── .github/
│ ├── copilot-instructions.md # This file - development guidelines
│ └── workflows/ # CI/CD pipelines
├── scout_merit_badges_anki/ # Main package
├── scout_anki/ # Main package
│ ├── __init__.py # Package metadata
│ ├── __main__.py # Entry point for python -m
│ ├── cli.py # Command line interface
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ __pycache__/
venv
.eggs
.pytest_cache
.ruff_cache
*.egg-info
.DS_Store

Expand Down
42 changes: 26 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
.PHONY: fmt lint test test-cov test-no-cov cov-report build-deck clean setup-pre-commit check-all help fetch-releases extract-archives fetch-and-build
.PHONY: fmt lint test test-cov test-no-cov cov-report build-merit-badges build-cub-adventures build-all clean setup-pre-commit check-all help fetch-releases extract-archives fetch-and-build-merit-badges fetch-and-build-cub-adventures

help:
@echo "Available commands:"
@echo " fetch-and-build - Fetch, extract, and build deck in one command"
@echo " fetch-releases - Fetch scout-archive releases using gh CLI"
@echo " extract-archives - Extract downloaded archives to extracted/ directory"
@echo " build-deck - Build Anki deck from extracted directory"
@echo " setup-pre-commit - Install pre-commit hooks"
@echo " check-all - Run all pre-commit hooks on all files"
@echo " fmt - Format code with ruff"
@echo " lint - Lint code with ruff"
@echo " test - Run tests with coverage reporting (80% target)"
@echo " test-no-cov - Run tests without coverage reporting"
@echo " cov-report - Generate and open HTML coverage report"
@echo " clean - Clean up temporary files"
@echo " fetch-and-build-merit-badges - Fetch, extract, and build merit badge deck"
@echo " fetch-and-build-cub-adventures - Fetch, extract, and build cub adventure deck"
@echo " fetch-releases - Fetch scout-archive releases using gh CLI"
@echo " extract-archives - Extract downloaded archives to extracted/ directory"
@echo " build-merit-badges - Build merit badge Anki deck from extracted directory"
@echo " build-cub-adventures - Build cub adventure Anki deck from extracted directory"
@echo " build-all - Build both merit badge and cub adventure decks"
@echo " setup-pre-commit - Install pre-commit hooks"
@echo " check-all - Run all pre-commit hooks on all files"
@echo " fmt - Format code with ruff"
@echo " lint - Lint code with ruff"
@echo " test - Run tests with coverage reporting (80% target)"
@echo " test-no-cov - Run tests without coverage reporting"
@echo " cov-report - Generate and open HTML coverage report"
@echo " clean - Clean up temporary files"

setup-pre-commit:
uv run pre-commit install
Expand Down Expand Up @@ -46,10 +49,17 @@ extract-archives:
mkdir -p extracted/
for file in *.tar.gz; do [ -f "$$file" ] && tar -xzf "$$file" -C extracted/ || true; done

build-deck:
uv run scout-merit-badges-anki build extracted/
build-merit-badges:
uv run scout-anki build merit-badges extracted/

fetch-and-build: fetch-releases extract-archives build-deck
build-cub-adventures:
uv run scout-anki build cub-adventures extracted/

build-all: build-merit-badges build-cub-adventures

fetch-and-build-merit-badges: fetch-releases extract-archives build-merit-badges

fetch-and-build-cub-adventures: fetch-releases extract-archives build-cub-adventures

clean:
rm -rf build/
Expand Down
90 changes: 46 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# scout-merit-badges-anki
# scout-anki

Generates an Anki deck for learning Scouting America merit badges by sight. The front of the card is an image of the merit badge. The back of the card is the name, description, and an indicator if it is required for the Eagle rank.
Anki deck builder for Scouting content. Generates Anki decks for learning Scouting America merit badges and Cub Scout adventures by sight. The front of the card is an image of the merit badge or adventure loop. The back of the card is the name, description, and additional details.

## Usage

Expand All @@ -9,87 +9,105 @@ Generates an Anki deck for learning Scouting America merit badges by sight. The
First, download and extract the scout-archive release files:

```bash
# Download and extract latest release archives
make fetch-and-build
# Download and extract latest release archives for merit badges
make fetch-and-build-merit-badges

# Download and extract latest release archives for cub adventures
make fetch-and-build-cub-adventures

# Build both deck types
make fetch-releases extract-archives build-all
```

This downloads archives, extracts them to `extracted/` directory, and creates `merit_badges_image_trainer.apkg` that can be imported into Anki.
This downloads archives, extracts them to `extracted/` directory, and creates `.apkg` files that can be imported into Anki.

### Manual Usage

```bash
# Download archives
gh release download --repo dasevilla/scout-archive --pattern "*.zip" --pattern "*.tar.gz"
gh release download --repo dasevilla/scout-archive --pattern "*.tar.gz"

# Extract archives
mkdir -p extracted/
for file in *.zip; do unzip -q "$file" -d extracted/; done
for file in *.tar.gz; do tar -xzf "$file" -C extracted/; done

# Generate Anki deck from extracted directory
scout-merit-badges-anki build extracted/
# Generate merit badge Anki deck from extracted directory
scout-anki build merit-badges extracted/

# Generate cub adventure Anki deck from extracted directory
scout-anki build cub-adventures extracted/
```

### Advanced Usage

```bash
# Custom output file
scout-merit-badges-anki build extracted/ --out my_badges.apkg
# Merit badges with custom output file
scout-anki build merit-badges extracted/ --out my_badges.apkg

# Cub adventures with custom output file
scout-anki build cub-adventures extracted/ --out my_adventures.apkg

# Dry run to preview without creating file
scout-merit-badges-anki build extracted/ --dry-run
scout-anki build merit-badges extracted/ --dry-run
scout-anki build cub-adventures extracted/ --dry-run

# Custom deck and model names
scout-merit-badges-anki build extracted/ --deck-name "My Badges" --model-name "Badge Quiz"
scout-anki build merit-badges extracted/ --deck-name "My Badges" --model-name "Badge Quiz"
scout-anki build cub-adventures extracted/ --deck-name "My Adventures" --model-name "Adventure Quiz"
```

### Command Reference

#### `build` - Generate Anki deck

```bash
scout-merit-badges-anki build [DIRECTORY] [OPTIONS]
scout-anki build DECK_TYPE DIRECTORY [OPTIONS]
```

**Arguments:**
- `DECK_TYPE` - Type of deck to build: `merit-badges` or `cub-adventures`
- `DIRECTORY` - Directory containing extracted badge data and images

**Options:**
- `--out PATH` - Output file path (default: `merit_badges_image_trainer.apkg`)
- `--deck-name TEXT` - Anki deck name (default: `Merit Badges Image Trainer`)
- `--model-name TEXT` - Anki model name (default: `Merit Badge Image → Text`)
- `--out PATH` - Output file path (auto-generated based on deck type if not specified)
- `--deck-name TEXT` - Anki deck name (auto-generated based on deck type if not specified)
- `--model-name TEXT` - Anki model name (auto-generated based on deck type if not specified)
- `--dry-run` - Preview without creating .apkg file
- `-q, --quiet` - Only show errors
- `-v, --verbose` - Increase verbosity

## How It Works

1. **Reads local archive files** (.zip, .tar.gz) containing badge data and images
2. **Extracts badge data** from JSON files using flexible schema normalization
3. **Maps badges to images** using explicit filenames or smart pattern matching
1. **Reads local archive files** (.tar.gz) containing Scouting data and images
2. **Extracts content data** from JSON files using flexible schema normalization
3. **Maps content to images** using direct `image_filename` field mapping
4. **Creates Anki deck** with stable IDs to prevent duplicates on reimport
5. **Bundles media files** into a complete .apkg package

### Image Mapping Strategy

The tool uses a sophisticated strategy to match badges with images:
The tool uses direct field mapping for reliable image association:

1. **Explicit mapping**: If JSON specifies an image filename, match by basename
2. **Pattern matching**: Look for `<badge-slug>-merit-badge.*` format
3. **Exact slug match**: Match badge slug directly to image filename
4. **Shortest path preference**: When multiple candidates exist, choose the shortest filename
1. **Direct mapping**: Uses the `image_filename` field from JSON data
2. **100% success rate**: All badges and adventures now have explicit image filenames
3. **No pattern matching needed**: Simplified from complex inference logic

### Card Format

**Merit Badges:**
- **Front**: Merit badge image (centered, 85% width)
- **Back**: Badge name and description
- **Back**: Badge name, description, and Eagle required indicator

**Cub Adventures:**
- **Front**: Adventure loop image (centered, 85% width)
- **Back**: Adventure name, rank, type, and description

## Development

To contribute to this tool, first checkout the code. Then set up the development environment:

```bash
cd scout-merit-badges-anki
cd scout-anki
uv sync
```

Expand All @@ -107,22 +125,6 @@ To run the tests:
make test
```

### Development Commands

```bash
make setup-pre-commit # Install pre-commit hooks (REQUIRED)
make fmt # Format code with ruff and fix linting issues
make lint # Lint code with ruff (check only, no fixes)
make check-all # Run all pre-commit hooks on all files
make test # Run tests (8 focused tests)
make test-no-cov # Run tests without coverage (faster for development)
make fetch-releases # Download scout-archive releases using gh CLI
make extract-archives # Extract downloaded archives to extracted/ directory
make build-deck # Build Anki deck from extracted directory
make fetch-and-build # Fetch, extract, and build deck in one command
make clean # Clean temporary files
```

## Data Source

This tool works with releases from the [scout-archive](https://github.com/dasevilla/scout-archive) repository, which contains merit badge data and images updated roughly weekly.
This tool is built using data from the [scout-archive](https://github.com/dasevilla/scout-archive) repository, which is updated roughly weekly.
18 changes: 9 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
name = "scout-merit-badges-anki"
name = "scout-anki"
version = "0.1.0"
description = "Generates an Anki deck for learning Scouting America merit badges by image"
description = "Anki deck builder for Scouting content"
readme = "README.md"
authors = [{name = "Devin Sevilla"}]
license = "Apache-2.0"
Expand All @@ -13,13 +13,13 @@ dependencies = [
]

[project.urls]
Homepage = "https://github.com/dasevilla/scout-merit-badges-anki"
Changelog = "https://github.com/dasevilla/scout-merit-badges-anki/releases"
Issues = "https://github.com/dasevilla/scout-merit-badges-anki/issues"
CI = "https://github.com/dasevilla/scout-merit-badges-anki/actions"
Homepage = "https://github.com/dasevilla/scout-anki"
Changelog = "https://github.com/dasevilla/scout-anki/releases"
Issues = "https://github.com/dasevilla/scout-anki/issues"
CI = "https://github.com/dasevilla/scout-anki/actions"

[project.scripts]
scout-merit-badges-anki = "scout_merit_badges_anki.cli:cli"
scout-anki = "scout_anki.cli:cli"

[build-system]
requires = ["hatchling"]
Expand Down Expand Up @@ -58,7 +58,7 @@ minversion = "7.0"
addopts = [
"--strict-markers",
"--strict-config",
"--cov=scout_merit_badges_anki",
"--cov=scout_anki",
"--cov-report=term-missing",
"--cov-report=html:htmlcov",
"--cov-report=xml",
Expand All @@ -70,7 +70,7 @@ markers = [
]

[tool.coverage.run]
source = ["scout_merit_badges_anki"]
source = ["scout_anki"]
omit = [
"*/tests/*",
"*/test_*",
Expand Down
File renamed without changes.
File renamed without changes.
Loading