# Code Quality

This guide covers the code quality tools and standards used in the Orion platform.

## Overview

The project uses modern Python tooling to maintain high code quality:

- **Ruff** - All-in-one linter and formatter (replaces black, isort, flake8, and more)
- **pre-commit** - Git hooks that auto-fix lint issues before each commit
- **mypy** - Static type checker
- **pytest** - Testing framework with coverage reporting

All tools are configured in `pyproject.toml` for consistency and ease of use.

## Setup (after cloning)

```bash
# Install dev dependencies (includes ruff, pre-commit, mypy)
pip install -r requirements-dev.txt

# Install pre-commit git hooks (required once per clone)
pre-commit install
```

The pre-commit hook runs automatically on every `git commit` and will:

- Validate architecture patterns
- Fix trailing whitespace and missing end-of-file newlines
- Auto-fix ruff lint issues (import sorting, unused imports, etc.)

If a hook modifies files (e.g. fixes imports), the commit is aborted. Simply `git add` the fixed files and commit again.

## Quick Start

```bash
# Format code
make format

# Lint and auto-fix issues
make lint

# Run both formatting and linting
make check

# Strict linting (no auto-fix) - for CI/CD
make lint-strict

# Run pre-commit hooks manually on all files
pre-commit run --all-files
```

## Ruff - Modern Linting and Formatting

Ruff is a blazingly fast Python linter and formatter written in Rust. It replaces multiple tools with a single, comprehensive solution.

### What Ruff Does

**Formatting** (replaces black):
- Consistent code style
- Automatic quote normalization
- Line length enforcement (88 characters)

**Linting** (replaces flake8, isort, pyupgrade, and more):
- Import sorting and organization
- Code style checks (PEP 8)
- Bug detection (flake8-bugbear)
- Modern Python syntax suggestions (pyupgrade)
- Code simplification suggestions

### Enabled Rule Sets

| Code | Description | Purpose |
|------|-------------|---------|
| E, W | pycodestyle | PEP 8 style enforcement |
| F | pyflakes | Basic error detection |
| I | isort | Import sorting |
| N | pep8-naming | Naming conventions |
| UP | pyupgrade | Modern Python syntax |
| B | flake8-bugbear | Common bug patterns |
| C4 | flake8-comprehensions | Better comprehensions |
| SIM | flake8-simplify | Code simplification |
| PIE | flake8-pie | Misc. lints |
| RET | flake8-return | Return statement improvements |
| Q | flake8-quotes | Quote consistency |

### Usage

```bash
# Format all code
make format
# or directly:
python -m ruff format .

# Lint and auto-fix issues
make lint
# or directly:
python -m ruff check . --fix

# Check without fixing (CI/CD)
make lint-strict
# or directly:
python -m ruff check .
```

### Configuration

Ruff is configured in `pyproject.toml`:

```toml
[tool.ruff]
line-length = 88
target-version = "py311"
exclude = [".git", ".venv", "venv", "__pycache__", "alembic/versions"]

[tool.ruff.lint]
select = ["E", "W", "F", "I", "N", "UP", "B", "C4", "SIM", "PIE", "RET", "Q"]
ignore = ["E501", "B008", "RET504", "SIM102"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
```

## mypy - Type Checking

mypy performs static type analysis to catch type-related errors before runtime.

### Usage

```bash
# Run type checking
python -m mypy .

# Or as part of lint
make lint
```

### Configuration

mypy is configured in `pyproject.toml`:

```toml
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
ignore_missing_imports = true
exclude = ["^\\.venv/", "^venv/", "^alembic/versions/"]
```

## Testing with pytest

Run tests with coverage reporting:

```bash
# Run all tests
make test

# Run with coverage
make test-coverage

# Run only unit tests
make test-unit

# Run only integration tests
make test-integration

# Run fast tests (skip slow ones)
make test-fast
```

### Coverage Configuration

Coverage settings are in `pyproject.toml`:

```toml
[tool.coverage.run]
source = ["app", "models", "middleware", "tasks", "storage"]
omit = ["*/tests/*", "*/venv/*", "*/alembic/*"]

[tool.coverage.report]
precision = 2
show_missing = true
```

## Makefile Commands

### Code Quality Commands

| Command | Description | When to Use |
|---------|-------------|-------------|
| `make format` | Format code with Ruff | Before committing |
| `make lint` | Lint and auto-fix with Ruff + mypy | Before committing |
| `make lint-strict` | Lint without auto-fix + mypy | In CI/CD pipelines |
| `make arch-check` | Validate architecture patterns | Before committing |
| `make arch-check-file file="path"` | Check a single file | Quick validation |
| `make arch-check-object name="merchant"` | Check all files for an entity | Entity-wide validation |
| `make check` | Run format + lint | Quick pre-commit check |
| `make ci` | Full CI pipeline (strict lint + coverage) | CI/CD workflows |
| `make qa` | Quality assurance (format + lint + arch-check + coverage + docs) | Before releases |

### Testing Commands

| Command | Description |
|---------|-------------|
| `make test` | Run all tests |
| `make test-unit` | Run unit tests only |
| `make test-integration` | Run integration tests only |
| `make test-coverage` | Run tests with coverage report |
| `make test-fast` | Run fast tests (skip slow ones) |

## Architecture Validation

The architecture validator ensures code follows established patterns and best practices.

### Quick Start

```bash
# Check all files
make arch-check

# Check a single file
make arch-check-file file="app/api/v1/admin/stores.py"

# Check all files related to an entity
make arch-check-object name="merchant"
make arch-check-object name="store"
```

### What It Checks

| Category | Examples |
|----------|----------|
| **API Endpoints** | Pydantic models, no business logic, exception handling, auth |
| **Service Layer** | No HTTPException, proper exceptions, DB session as param |
| **Models** | SQLAlchemy/Pydantic separation |
| **JavaScript** | Centralized logger, apiClient usage |
| **Templates** | Base template inheritance |

### Output

The validator provides a summary table showing pass/fail status per file:

```
📋 FILE SUMMARY:
--------------------------------------------------------------------------------
  File                           Status    Errors   Warnings
  -----------------------------  --------  -------  --------
  app/api/v1/admin/merchants.py  ❌ FAILED  6        9
  app/services/merchant_service.py ✅ PASSED  0        0
--------------------------------------------------------------------------------
  Total: 2 files | ✅ 1 passed | ❌ 1 failed | 6 errors | 9 warnings
```

See [Architecture Rules Reference](architecture-rules.md) for detailed rule documentation.

## Pre-Commit Workflow

Before committing code:

```bash
# 1. Format, lint, and verify critical imports
make check

# 2. Validate architecture patterns
make arch-check

# 3. Run relevant tests
make test-fast

# 4. If all passes, commit
git add .
git commit -m "your message"
```

### Critical Import Verification

The `make check` command includes a critical import verification step that ensures re-export imports haven't been removed by linters.

**What it checks:**
- `models/database/base.py` - Re-exports Base from app.core.database
- `models/__init__.py` - Exports Base for Alembic
- `models/database/__init__.py` - Exports Base from database package

**Why it matters:**
Linters like Ruff may see these imports as "unused" (F401) because they're re-exported, not directly used. Removing them breaks the application.

**Manual verification:**
```bash
make verify-imports
```

If this fails, imports have been removed and must be restored.

## CI/CD Integration

For continuous integration:

```bash
# Use strict mode (no auto-fixes)
make ci
```

This will:
1. Run strict linting (fails on any issues)
2. Run type checking with mypy
3. Run full test suite with coverage

## IDE Integration

### VSCode

Install these extensions:
- Ruff (charliermarsh.ruff)
- Python (ms-python.python)

Add to `.vscode/settings.json`:

```json
{
  "[python]": {
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "charliermarsh.ruff",
    "editor.codeActionsOnSave": {
      "source.fixAll": true,
      "source.organizeImports": true
    }
  },
  "ruff.enable": true,
  "ruff.lint.enable": true
}
```

### PyCharm

1. Go to Settings → Tools → External Tools
2. Add Ruff:
   - Program: `python`
   - Arguments: `-m ruff check $FilePath$ --fix`
   - Working directory: `$ProjectFileDir$`

## Common Issues

### Import Order

Ruff automatically organizes imports into sections:
1. Future imports
2. Standard library
3. Third-party packages
4. First-party packages (app, models, middleware, tasks, storage, scripts)
5. Local folder imports

### Line Length

Default line length is 88 characters (black's default). Long lines are automatically wrapped.

### Type Hints

While not strictly enforced, adding type hints improves code quality:

```python
# Good
def get_user(user_id: int) -> User:
    return db.query(User).get(user_id)

# Better with Optional
from typing import Optional

def get_user(user_id: int) -> Optional[User]:
    return db.query(User).get(user_id)
```

## Ignoring Rules

### Per-File Ignores

Configure in `pyproject.toml`:

```toml
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]  # Allow unused imports
"tests/**/*.py" = ["S101"]  # Allow assert statements
```

### Inline Ignores

Use `# noqa` comments sparingly:

```python
# Ignore specific rule
example = lambda x: x + 1  # noqa: E731

# Ignore all rules for line
long_url = "https://..."  # noqa
```

## Best Practices

1. **Run `make check` before every commit**
2. **Let Ruff auto-fix issues** - don't fight the formatter
3. **Add type hints** for better code quality
4. **Keep test coverage above 80%**
5. **Use meaningful variable names** that pass naming checks
6. **Avoid broad exception catches** - be specific
7. **Use pathlib** instead of os.path when possible
8. **Keep functions focused** - if it's complex, break it down

## Migration from Old Tools

If you were using black, isort, or flake8 before:

| Old Tool | New Tool | Notes |
|----------|----------|-------|
| black | ruff format | 100% compatible |
| isort | ruff check --select I | Built into Ruff linting |
| flake8 | ruff check | Faster, more comprehensive |
| pyupgrade | ruff check --select UP | Built into Ruff |

All configurations have been migrated to `pyproject.toml`.

## Performance

Ruff is **10-100x faster** than the tools it replaces:

- **black**: ~2s → ruff: ~0.1s
- **isort**: ~3s → ruff: ~0.1s
- **flake8**: ~10s → ruff: ~0.2s

This means faster CI/CD pipelines and better developer experience.

## Resources

- [Ruff Documentation](https://docs.astral.sh/ruff/)
- [Ruff Rules](https://docs.astral.sh/ruff/rules/)
- [mypy Documentation](https://mypy.readthedocs.io/)
- [pytest Documentation](https://docs.pytest.org/)
