# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Development Commands

### Setup
```bash
# Install dependencies using uv (preferred)
uv sync

# Configure environment
./setup_config.sh  # Interactive setup of .env file
# OR manually copy: cp env.example .env

# Run migrations
python manage.py migrate
```

### Running the Application
```bash
# Development server
python manage.py runserver

# Create superuser
python manage.py createsuperuser
```

### Testing
```bash
# Run all tests
python manage.py test

# Run tests for specific app
python manage.py test atlas_record_cleaner_report
python manage.py test eventhub
```

### Static Files
```bash
# Collect static files for production
python manage.py collectstatic

# Minify JavaScript (example for habitats_and_biotopes)
npm run minify:js
```

### Database
```bash
# Create migrations
python manage.py makemigrations

# Apply migrations
python manage.py migrate

# Create migration for specific app
python manage.py makemigrations eventhub
```

## High-Level Architecture

### Dual-Database Setup
This project uses **two databases**:
- **Primary (PostGIS)**: Main application data with spatial support. Search path: `devsgone,public`
- **Secondary (SQLite)**: Read-only UKSI taxonomy database (UK Species Inventory)

Database routing is handled by `config/db_router.py` (`UKSIBrowserRouter`). All `uksi_browser` models automatically route to the SQLite database.

### Django Apps Structure
The project is organized into 8 feature-based Django apps:

- **site_core**: Landing pages and core UI
- **components**: Reusable UI patterns (voting system, tree navigation, loading overlays, toast notifications)
- **eventhub**: Hierarchical event tracking (projects → sites → transects → visits → samples) with geospatial support
- **species_alert**: Generate biodiversity species occurrence alerts for geographic areas
- **atlas_record_cleaner_report**: Validate NBN Atlas occurrence records using Record Cleaner service
- **uksi_browser**: Browse UK Species Inventory taxonomy
- **habitats_and_biotopes**: Marine habitat mapping and biotope analysis
- **blog**: Technical blog with markdown support

URL routing is namespace-based: each app mounts at its own path (e.g., `/eventhub/`, `/species-alert/`, `/blog/`).

### Service Layer Pattern
Business logic is separated into `services/` directories within apps:
- `species_service.py`: Species queries and occurrence counting
- `atlas_service.py`: NBN Atlas API integration
- `record_cleaner_service.py`: Data validation
- `geometry.py`: WKT geometry handling utilities

This separation keeps views thin and logic testable.

### Frontend Architecture

**HTMX Integration**: The app uses HTMX 1.9.10 extensively for dynamic interactions:
- Hierarchical data navigation (event trees, species lists)
- Partial template swaps without page reloads
- Views detect HTMX via `HX-Request` header and return fragments
- Use `_is_htmx()` helper function in views

**Template Structure**:
- Base template: `site_core/templates/site_core/base.html`
- Includes Bootstrap 5.3.3, HTMX, Leaflet 1.9.4, Font Awesome 6.0
- Global toast notification system in base template
- Template blocks: `title`, `meta`, `content`, `extra_css`, `extra_scripts`, `breadcrumb`, `structured_data`

**Static Files Organization**:
- Project-level: `staticfiles/` (css/, js/, images/)
- App-level: `app_name/static/app_name/`
- WhiteNoise serves static files in production with compression
- Output directory: `static_collected/` (from `collectstatic`)

### Key Architectural Patterns

**Universal Voting System** (`components` app):
- Content-agnostic `Vote` model using `content_type` and `content_id`
- Template tag: `{% vote_button content_object %}`
- Session-based duplicate prevention using JSONField
- Integrated with toast notifications

**Hierarchical Event System** (`eventhub` app):
- Self-referential `Event` model with unlimited nesting
- Event types: project, site, transect, visit, sample, biotope_mapping_unit, note
- WKT geometry support per event
- Associated `Occurrence` records (taxon observations) and `EventAttribute` measurements
- Tree UI component for visual navigation using HTMX

**Geospatial Features**:
- PostGIS integration for spatial queries
- WKT geometry handling via Shapely
- Leaflet maps with WMS/WFS layer support
- Geometry buffering utilities in `services/geometry.py`

### Configuration Management
Uses `django-environ` for environment variables:
- Sensitive values in `.env` files (excluded from git)
- Location: project root OR external directory via `APP_CONFIG_DIR` env var
- Required: `SECRET_KEY`, `RECORD_CLEANER_USERNAME`, `RECORD_CLEANER_PASSWORD`
- Optional: `DEBUG`, `ALLOWED_HOSTS`, `CSRF_TRUSTED_ORIGINS`, `UKSI_SQLITE_PATH`, PostGIS connection details

### External API Integration
- **NBN Atlas records-ws API**: Fetch species occurrence data
- **NBN Registry API**: Dataset metadata
- **Record Cleaner service**: Data validation (requires auth credentials)
- Timeout defaults: 60 seconds
- Error handling with Python logging module

## Code Style (from .cursorrules)

### Python and Django
- Write idiomatic Django code: use ORM properly, follow common patterns for views, models, templates
- Put all imports at the top, grouped and ordered (stdlib, third-party, local)
- Put constants at the top of the file, just below imports
- Prefer concise, idiomatic code over excessive defensive checks
- Fail fast: let exceptions surface naturally; don't wrap everything in try/except
- Use class-based views where they keep things clearer, but don't over-engineer

### HTML, Templates, and CSS
- Follow the design and style of existing apps; keep markup consistent
- **Use Bootstrap classes wherever possible** instead of inventing new CSS
- Avoid creating new CSS classes unless absolutely necessary
- Prefer inheriting font weights, sizes, and colours from existing styles
- **Titles and headings in UI should use sentence case** (only capitalize first word unless proper noun)

### JavaScript
- **ALWAYS use async/await** for asynchronous code
- **NEVER use `.then()` or `.catch()` promise chains**
- **ALWAYS check `response.ok`** and throw error for non-2xx responses
- Use centralized `fetchJson()` helper for all fetch calls
- **Minimal defensive checks**: Only for external boundaries (network, user input, optional DOM elements, third-party payloads)
- **NEVER use** `typeof localFunction === 'function'` or `typeof localVar !== 'undefined'` for local code
- Use event delegation for dynamic content
- **NEVER attach event listeners in `htmx:afterSwap`** (only restore UI state)
- State lives in JS data structures, DOM reflects state (DOM is not source of truth)
- Prefer `const`/`let` over `var`, early returns over nested ifs

### Database Queries
- **Prefetch or annotate related objects** to avoid N+1 query patterns
- Use `select_related()`, `prefetch_related()`, or `annotate(Count(...))` for nested data
- Don't loop over `.children.all()` in Python; use bulk queries instead

### General Behavior
- Avoid unnecessary checks or wrappers; assume environment is valid
- Keep changes minimal and well-scoped
- Explain briefly what you're changing before showing code
- **Do not rewrite or refactor unrelated code** unless explicitly asked
- **Do not create `.md` files** unless asked (if creating, put in `AGENT_DOCS/` folder)
- Do not override helper functions defined in static assets (e.g., `eventhub_map.js`); extend them or add options instead

## Deployment

Production deployment uses Passenger WSGI on DigitalOcean:
```bash
ssh -i ~/.ssh/devsgonewild.pub devsgone@165.22.127.87
cd apps/devs-gone-wild-app
git pull
touch tmp/restart.txt  # Restart Passenger
```
