Backend CLI tools for Plaite recipe management. Get stats from Firebase, upload recipes, and generate images.
# Install uv if you haven't already
curl -LsSf https://astral.sh/uv/install.sh | sh
# Clone and install
cd ~/Documents/plaite_backend
uv sync --all-extrasCreate a .env file in the project root:
# Path to recipe data (parquet format)
RECIPES_PATH=/path/to/all_enriched_recipes.parquet
# Google API key for image generation (optional)
GOOGLE_API_KEY=your-google-api-key-hereEdit configs/firebase.yaml with your Firebase credential paths:
prod:
credentials_path: "/path/to/plaite-production-firebase-adminsdk.json"
storage_bucket: "plaite-production.firebasestorage.app"
collection: "recipes"
dev:
credentials_path: "/path/to/plaite-dev-firebase-adminsdk.json"
storage_bucket: "plaite-ff1e7.firebasestorage.app"
collection: "recipes"Edit configs/upload.yaml for batch upload settings:
batch_size: 50
skip_existing: true
image_storage_path: "recipe_images/"The plaite.data module provides efficient recipe data loading using Polars:
import plaite.data as data
# Get available columns
print(data.get_recipes_columns())
# Load all recipes
df = data.load_recipes()
# Load specific columns
df = data.load_recipes(columns=["id", "name", "ingredients"])
# Filter with Django-style operators
df = data.filter_recipes({
"category": "dessert",
"calories__lt": 500,
"rating__gte": 4.0
})
# Advanced queries
import polars as pl
df = (
data.recipes_table.scan()
.filter(pl.col("calories") < 500)
.group_by("category")
.agg(pl.col("id").count())
.collect()
)See: QUICK_START.md and src/plaite/data/README.md
Get statistics from the Firebase recipe database.
# Basic usage (defaults to dev environment)
uv run plaite stats
# Use production environment
uv run plaite stats --env prod
# Save output to JSON file
uv run plaite stats --env prod --output stats.json
# Limit number of recipes to analyze (for testing)
uv run plaite stats --env dev --limit 100
# Custom config file
uv run plaite stats --config /path/to/firebase.yaml --env prodStats included:
- Total recipe count
- Tag distribution (count per tag)
- Ingredient distribution (top 100)
- Nutrient coverage (% with nutrients, which fields)
- Missing fields analysis
Upload recipes from local data with interactive filtering.
Basic Usage
# Interactive upload with filters (default: excludes already-uploaded)
plaite sync 50
# You'll be prompted for filters:
# - Title (contains)
# - Ingredient (contains)
# - Health grade (A/B/C/D/F)
# - Max cook time
# - Min rating
Command Options
# Dry run - validate only, don't upload
plaite sync 50 --dry-run
# Include recipes already in Firebase (default excludes them)
plaite sync 50 --include-uploaded
# Skip confirmation prompt
plaite sync 50 --yes
# Use production environment
plaite sync 50 --env prod
Upload Flow (as implemented)
1. Interactive filter selection
Prompts for filters (title, ingredient, health grade, cook time, rating)
Shows total matching recipes
2. Create batch
Selects N random recipes from local data matching filters
Shows preview table
3. Check Firebase
Fetches existing recipe IDs from Firebase
Filters out already-uploaded recipes (unless --include-uploaded)
Shows skip count
4. Validate & Transform
Maps local schema to Firebase schema
Validates required fields (tags, instructions, ingredients)
Type checks (must be lists)
Transforms nutrients (dict → array)
Normalizes numServings ("4 servings" → 4.0)
Validates image URL exists
5. Upload
Uploads valid recipes to Firebase in batches
Shows detailed results
Example Session
$ plaite sync 50
Plaite Sync - Upload to Firebase (dev)
Target: recipes-dev
Recipe Selection
Filter by title (contains): chicken
Filter by ingredient (contains):
Filter by health grade (A/B/C/D/F):
Max cook time (minutes):
Min rating (0-5):
Found 234 matching recipes.
Will select 50 random recipes.
Continue with upload? [Y/n]: y
Selecting recipes from local data...
Selected 50 recipes
# Preview table shown here
Checking Firebase for existing recipes...
Found 1500 recipes in Firebase
Skipping 10 already-uploaded recipes
Validating and transforming recipes...
Validating: 100%|████████| 40/40
Valid: 38, Invalid: 2
Uploading to Firebase...
Uploading: 100%|████████| 38/38
Done!
Selected: 50
Valid: 38
Uploaded: 38
Skipped: 10 (already uploaded)
Failed: 2
recipe_123: Missing required fields (tags, instructions, or ingredients)
recipe_456: tags is not a listUpload recipes from a JSON file to Firebase.
# Upload from JSON file (default: excludes already-uploaded)
uv run plaite upload recipes/batch4.json --env dev
# Dry run - validate without uploading
uv run plaite upload recipes/batch4.json --dry-run
# Upload to production
uv run plaite upload recipes/batch4.json --env prod
# Include recipes already in Firebase
uv run plaite upload recipes/batch4.json --include-uploaded
# Custom config files
uv run plaite upload recipes/batch4.json \
--config configs/firebase.yaml \
--upload-config configs/upload.yaml \
--env prodOptions:
| Option | Description |
|---|---|
--env |
Environment: prod or dev (default: dev) |
--config, -c |
Firebase config path |
--upload-config |
Upload config path |
--dry-run |
Validate only, don't upload |
--include-uploaded |
Include recipes already in Firebase |
Flow:
- Load recipes from JSON file
- Check Firebase for existing recipes
- Validate and transform (field renaming, nutrients transformation, type checking)
- Upload to Firebase
Scrape a recipe from a URL and upload to Firebase.
# Scrape and upload a recipe (default: excludes already-uploaded)
uv run plaite scrape "https://www.allrecipes.com/recipe/123/chocolate-cake/"
# Dry run to test scraping and validation
uv run plaite scrape "https://example.com/recipe" --dry-run
# Upload to production
uv run plaite scrape "https://example.com/recipe" --env prod
# Include even if already uploaded
uv run plaite scrape "https://example.com/recipe" --include-uploadedOptions:
| Option | Description |
|---|---|
--env |
Environment: prod or dev (default: dev) |
--config, -c |
Firebase config path |
--upload-config |
Upload config path |
--dry-run |
Validate only, don't upload |
--include-uploaded |
Include recipes already in Firebase |
Flow:
- Scrape recipe data from URL (using recipe-scrapers library)
- Preview scraped title, host, author, tags
- Check Firebase for existing recipes (by URL hash)
- Validate and transform (field renaming, nutrients transformation, type checking)
- Upload to Firebase if valid
Note: All three commands (sync, upload, scrape) use identical validation and produce consistent output.
Generate recipe images using Google Imagen 4 AI.
# Basic usage
uv run plaite generate-image "Delicious chocolate cake with strawberries"
# Specify output path and model
uv run plaite generate-image "Homemade pizza" --output pizza.png --model ultra
# Change aspect ratio and generate multiple variations
uv run plaite generate-image "Pasta carbonara" --aspect-ratio 16:9 --num-images 4Options:
| Option | Description |
|---|---|
--output, -o |
Output file path (default: generated_image.png) |
--model |
Model variant: standard, ultra, or fast (default: standard) |
--aspect-ratio |
Aspect ratio: 1:1, 3:4, 4:3, 9:16, or 16:9 (default: 4:3) |
--num-images |
Number of images to generate, 1-4 (default: 1) |
Requirements:
- Set
GOOGLE_API_KEYenvironment variable - See
IMAGE_GENERATION.mdfor detailed documentation
Show version information.
uv run plaite version# Step 1: Dry run to validate
uv run plaite upload recipes/batch4.json --env dev --dry-run
# Step 2: Upload to dev for testing
uv run plaite upload recipes/batch4.json --env dev
# Step 3: Verify with stats
uv run plaite stats --env dev
# Step 4: Upload to production
uv run plaite upload recipes/batch4.json --env prod# Quick check
uv run plaite stats --env prod
# Full export for analysis
uv run plaite stats --env prod --output stats_$(date +%Y%m%d).json# Run linter
uv run ruff check src/
# Auto-fix lint issues
uv run ruff check src/ --fix
# Format code
uv run ruff format src/
# Run tests
uv run pytestplaite_backend/
├── configs/
│ ├── firebase.yaml # Firebase credentials (prod/dev)
│ └── upload.yaml # Upload and image settings
├── src/plaite/
│ ├── __init__.py
│ ├── cli.py # CLI entrypoint (typer)
│ ├── config.py # Config loading & validation
│ ├── data/ # Data loading module
│ │ ├── __init__.py # Public API
│ │ ├── _tables.py # Table wrapper (parquet)
│ │ ├── _queries.py # Pre-built query templates
│ │ ├── loader.py # Loading functions
│ │ ├── columns.py # Column definitions
│ │ ├── query.py # Col query builder
│ │ └── README.md # Full documentation
│ ├── pipeline/ # Upload pipelines
│ │ ├── upload.py # Upload from local/file/URL
│ │ └── validation.py # Comprehensive validation
│ ├── scraper/ # Recipe scraping
│ │ ├── __init__.py
│ │ └── scraper.py # URL scraping with recipe-scrapers
│ ├── firebase/
│ │ ├── client.py # Firebase initialization
│ │ ├── stats.py # Stats collection
│ │ └── upload.py # Legacy batch upload
│ └── images/
│ └── process.py # Image download & processing
├── examples/
│ ├── test_data_module.py # Test data module
│ └── advanced_queries.py # Advanced query examples
├── archive/ # Old code for reference
├── tests/
├── .env # Environment variables (not committed)
├── pyproject.toml # Project config (uv, ruff, pytest)
├── QUICK_START.md # Data module quick start
└── README.md