make_post_sell/CLAUDE.md
russell@unturf.com 117227a901 Add setuptools to requirements for Python 3.12+ compatibility
Python 3.12 no longer bundles setuptools in virtual environments.
Pyramid imports pkg_resources from setuptools, causing CI to fail with
ModuleNotFoundError: No module named 'pkg_resources'.
2026-02-08 11:47:52 -05:00

256 lines
10 KiB
Markdown

# Claude Development Notes
## Production URL
- Application: `https://my.makepostsell.com`
- Version check: `https://my.makepostsell.com/version`
## Project Setup
This project uses a Makefile for most development operations. Use `make` commands instead of running tools directly.
## Common Development Tasks
### Testing
- Run tests: `make test`
- This installs development dependencies and runs the test suite with py.test
- Tests are located in `make_post_sell/tests/`
### Installation & Setup
- Install from source for development: `make install-from-source`
- Install from PyPI: `make install-from-pypi`
- Initialize database: `make init-db`
### Development Server
- Start development server: `make serve`
- Runs with auto-reload enabled
- Uses `data/development.ini` configuration
### Environment Management
- Create virtual environment: `make venv`
- Clean up environment: `make clean`
- Activate environment: `source env/bin/activate`
## Code Structure
### Key Directories
- `make_post_sell/views/` - View controllers
- `make_post_sell/models/` - Database models
- `make_post_sell/tests/` - Test suite
### Important Files
- `make_post_sell/views/cart.py` - Cart and checkout logic
- `development.ini` - Configuration file
## Testing Notes
The project uses pytest with unittest framework. There are three types of tests:
### Test Types
- **Unit tests** (`test_models.py`) - Test individual model methods and properties in isolation
- **Integration tests** (`test_integration.py`) - Test interactions between models and business logic
- **Functional tests** (`test_functional.py`) - End-to-end tests through the web interface
### Running Tests
**Before running tests**: Source environment variables with `source vars.sh` to set required Stripe API keys and other configuration.
```bash
# Run all tests
make test
# Run specific test types
env/bin/py.test make_post_sell/tests/test_models.py # Unit tests
env/bin/py.test make_post_sell/tests/test_integration.py # Integration tests
env/bin/py.test make_post_sell/tests/test_functional.py # Functional tests
# Run with coverage
env/bin/py.test --cov=make_post_sell.models.cart --cov-report=term-missing make_post_sell/tests/test_models.py::TestCart
```
### Current Coverage
- Cart model unit tests cover critical business logic like `requires_payment` threshold (64 cents)
- Integration tests verify the original AttributeError bug fix for free coupon checkout
- Functional tests provide end-to-end coverage of cart/checkout/payment flows
## Database Location
The SQLite database is located at: `data/make_post_sell.sqlite`
**CRITICAL WARNING**: NEVER delete or remove database files without explicit user permission. The database contains production data and cannot be easily recovered. Always ask before any destructive operations.
**MANDATORY**: ALWAYS create a backup of the database before any database operations (migrations, schema changes, etc.):
```bash
cp data/make_post_sell.sqlite data/make_post_sell.sqlite.backup-$(date +%Y%m%d-%H%M%S)
```
Query crypto payments:
```sql
-- Note: Remove dashes from UUIDs when querying
SELECT * FROM mps_crypto_payment WHERE id = 'paymentuuidherewithoutdashes';
```
## Database Migrations
When making changes to database models, always create Alembic migrations:
### Creating Migrations
**CRITICAL**: ALWAYS use `alembic revision` to generate migration files. NEVER manually create migration files or make up revision IDs. Alembic generates unique revision IDs that are required for proper migration tracking.
```bash
# Activate environment first
source env/bin/activate
# Create a new migration (manual) - ALWAYS use this command
alembic -c data/development.ini revision -m "description of change"
# OR: Create autogenerated migration (compares DB with models)
alembic -c data/development.ini revision --autogenerate -m "description of change"
# Edit the generated migration file in make_post_sell/scripts/alembic/versions/
# The file will have a proper unique ID like: 05be3044c2d2_description_of_change.py
```
### Running Migrations
```bash
# Apply all pending migrations
alembic -c data/development.ini upgrade head
# Check current migration status
alembic -c data/development.ini current
# View migration history
alembic -c data/development.ini history
```
**IMPORTANT**: Always backup the database before running migrations!
### Important Migration Notes
**Idempotent Migrations**: `make init-db` creates all tables from models, so migrations that run afterward must not fail if tables/columns already exist. Always guard `create_table` with `_table_exists` and `add_column` with `_column_exists`:
```python
def _table_exists(name):
conn = op.get_bind()
result = conn.execute(
sa.text("SELECT name FROM sqlite_master WHERE type='table' AND name=:name"),
{"name": name},
)
return result.fetchone() is not None
def _column_exists(table, column):
conn = op.get_bind()
result = conn.execute(sa.text(f"PRAGMA table_info({table})"))
return any(row[1] == column for row in result.fetchall())
def upgrade():
if not _table_exists("mps_new_table"):
op.create_table(...)
if not _column_exists("mps_shop", "new_column"):
op.add_column(...)
```
**SQLite Column Defaults**: When adding NOT NULL columns with defaults to existing tables in SQLite, use `server_default` with raw SQL values:
```python
# Correct - uses server_default for raw SQL
op.add_column(
"mps_shop",
sa.Column("stripe_enabled", sa.Boolean(), nullable=False, server_default="1"),
)
# Wrong - default won't work with existing data
op.add_column(
"mps_shop",
sa.Column("stripe_enabled", sa.Boolean(), nullable=False, default=True),
)
```
## Cryptocurrency RPC Access
### Monero Wallet RPC
When debugging or manually testing Monero RPC calls, use digest authentication with these credentials (from Makefile):
- Username: `test_user`
- Password: `test_pass`
- URL: `http://127.0.0.1:18083/json_rpc`
Example curl command with digest auth:
```bash
curl --digest -u "test_user:test_pass" -X POST http://127.0.0.1:18083/json_rpc \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"0","method":"get_transfer_by_txid","params":{"txid":"transaction_hash_here"}}'
```
### Dogecoin Core RPC
Dogecoin uses basic authentication (from dogecoin.conf):
- Username: `mps_doge_user`
- Password: `change_this_password_in_production`
- URL: `http://127.0.0.1:22555`
## Common Issues and Solutions
### UUID Objects
Always use `uuid_str` when you need a string copy of the identifier. Models inherit `uuid_str` property from `RBase`.
**IMPORTANT**: UUIDs are stored in the database WITHOUT dashes. When querying by ID, remove dashes from the UUID:
- Correct: `WHERE id = '0f92cd2a86f54dc1b98ef5c8b37bc7f8'`
- Wrong: `WHERE id = '0f92cd2a-86f5-4dc1-b98e-f5c8b37bc7f8'`
## Development Standards and Expectations
**CRITICAL WORK ETHIC**: The user pays significant money for development work and expects thorough, complete solutions. NEVER try to do the minimum or cut corners. When asked to implement features, provide comprehensive, production-ready implementations that consider all aspects of the request.
**CSS LAYOUT REQUIREMENTS**: This project uses CSS Grid exclusively for layout. NEVER use Flexbox (flex) for layout. Always use CSS Grid properties for positioning and alignment.
**CSS MEDIA SIZING**: Never combine `width: 100%` with `max-height` on media elements (img, video). `width: 100%` forces the element to span the full container even when `max-height` constrains the rendered content, creating dead whitespace. Use `width: auto` + `max-width: 100%` + `max-height` instead — the element shrinks to match the actual content aspect ratio within both constraints.
**MOBILE USABILITY**: Never use hover-only interactions (`:hover` to reveal controls, `opacity: 0` with hover reveal, etc.). Mobile/touch devices have no hover state — controls hidden behind hover are invisible and unreachable. All interactive elements (buttons, toggles, links) must be always visible and tappable. Design touch-first, then optionally enhance for desktop hover.
**TESTING INTEGRITY**: NEVER skip, delete, or disable unit tests or integration tests when they break. When tests fail:
1. **FIX THE TESTS** - Update them to work with new functionality
2. **FIX THE CODE** - If the tests reveal actual bugs, fix the underlying issue
3. **ADD MORE TESTS** - Ensure new functionality is properly covered
Disabling or removing tests weakens the codebase and is unacceptable. Tests are critical safety nets that prevent regressions.
## Commit Message Guidelines
**CRITICAL**: Do not include Claude Code attribution in commit messages. Attributing human work to Claude is inappropriate and misrepresents the actual authorship of the code. All code changes should be attributed to the human developer who reviewed, approved, and committed the work.
## Capability-Driven Presentation
Follow Russell Ballestrini's capability-driven presentation practice
(russell.ballestrini.net/capability-driven-presentation/). A page need not look
identical across all browsers. Accommodate what the user's browser can do:
1. **Single canonical URI** — one URL serves the content.
2. **Consistent content** — regardless of viewer capabilities.
3. **Graceful enhancement/degradation** — use available capabilities to enhance presentation.
### The `js-only` / `<noscript>` pattern
```html
<noscript>
<style>.js-only {display: none;}</style>
</noscript>
```
Apply the `js-only` class to any element that requires JavaScript to function.
When JS is unavailable, these elements hide automatically — the user never sees
a broken control.
### AJAX form submission
Comment forms use progressive enhancement: the form works as a normal POST +
redirect without JS. When JS is available, `comments.js` intercepts the submit,
sends via `fetch()` with `X-Requested-With: XMLHttpRequest`, and inserts the
new comment into the DOM without a page reload (preserving media playback).
The server returns JSON (HTTP 201) for AJAX requests and falls back to the
normal redirect flow on any error.
## Style
- **Never use "AI" — always say "machine learning."** We grow machine learning, not "AI." This term is forbidden in all permacomputer discourse, marketing, & documentation.