# Contributing to intronIC

Thank you for your interest in contributing to intronIC! This document provides guidelines and instructions for contributing to the project.

## Table of Contents

- [Getting Started](#getting-started)
- [Development Setup](#development-setup)
- [Development Workflow](#development-workflow)
- [Code Style](#code-style)
- [Testing](#testing)
- [Submitting Changes](#submitting-changes)
- [Releasing a New Version](#releasing-a-new-version)
- [Project Structure](#project-structure)

---

## Getting Started

### Prerequisites

- **Python 3.10+** (required by biogl dependency)
- **Git** for version control
- One of these package managers:
  - **pixi** (recommended for development) - [Install pixi](https://pixi.sh/)
  - **uv** (fast alternative) - [Install uv](https://docs.astral.sh/uv/)
  - **pip** (standard)

### Quick Start

```bash
# Clone the repository
git clone https://github.com/glarue/intronIC.git
cd intronIC

# Option 1: Using pixi (recommended)
pixi install

# Option 2: Using uv
uv sync

# Option 3: Using pip
pip install -e ".[dev]"
```

---

## Development Setup

### Using pixi (Recommended)

[Pixi](https://pixi.sh/) provides fully reproducible environments with locked dependencies:

```bash
# Install dependencies and create environment
pixi install

# Run intronIC
pixi run intronIC -h

# Run tests
pixi run pytest tests/

# See all available tasks
pixi task list
```

### Using uv

[uv](https://docs.astral.sh/uv/) is a fast Python package manager:

```bash
# Create environment and install dependencies
uv sync

# Activate the environment
source .venv/bin/activate

# Run intronIC
intronIC -h

# Run tests
pytest tests/
```

### Using pip

```bash
# Create virtual environment
python -m venv .venv
source .venv/bin/activate

# Install in editable mode with dev dependencies
pip install -e ".[dev]"
```

---

## Development Workflow

### Using Make (Recommended)

We provide a `Makefile` with common development tasks. Run `make help` to see all available commands:

```bash
make help          # Show all available commands
make install       # Install dependencies (pixi)
make test          # Run test suite
make test-quick    # Run quick tests only
make lint          # Run linters
make format        # Format code
make lock          # Update lock files after dependency changes
make clean         # Clean build artifacts
```

### Dependency Management

**Important**: After modifying `pyproject.toml` or `pixi.toml`, update the lock files:

```bash
make lock
```

This runs both `uv lock` and `pixi install` to keep both lock files in sync.

#### Lock Files Explained

| File | Purpose | Generated By |
|------|---------|--------------|
| `uv.lock` | Pins exact Python package versions for uv/pip users | `uv lock` |
| `pixi.lock` | Pins exact versions for pixi users (includes conda packages) | `pixi install` |

Both should be committed to version control for reproducibility.

### Branch Workflow

1. **Create a feature branch** from `main`:
   ```bash
   git checkout -b feature/my-feature
   ```

2. **Make your changes** with tests

3. **Run tests and linting** before committing:
   ```bash
   make test lint
   ```

4. **Update lock files** if you changed dependencies:
   ```bash
   make lock
   ```

5. **Commit your changes**:
   ```bash
   git add -A
   git commit -m "feat: add my feature"
   ```

6. **Push and create a pull request**

---

## Code Style

### Python Style Guide

- **Type hints**: All functions should have type annotations
- **Docstrings**: Use Google-style docstrings for public functions
- **Line length**: 88 characters (Black default)
- **Imports**: Sorted with `isort`, absolute imports preferred

### Formatting Tools

```bash
# Format code with Black
make format

# Or manually:
black src/ tests/
isort src/ tests/

# Check without modifying
black --check src/ tests/
```

### Linting

```bash
# Run all linters
make lint

# Or manually:
ruff check src/ tests/
mypy src/
```

### Example Function

```python
def score_intron(
    sequence: str,
    pwm: PositionWeightMatrix,
    region: ScoringRegion,
) -> float:
    """Calculate PWM score for an intron sequence region.

    Args:
        sequence: The intron sequence (must be uppercase).
        pwm: Position weight matrix to use for scoring.
        region: The region coordinates to score.

    Returns:
        Log-likelihood ratio score for the region.

    Raises:
        ValueError: If sequence is too short for the region.
    """
    # Implementation...
```

---

## Testing

### Running Tests

```bash
# Run all tests
make test

# Run with coverage
make test-cov

# Run quick tests only (skip slow integration tests)
make test-quick

# Run specific test file
pixi run pytest tests/unit/test_scoring.py -v

# Run tests matching a pattern
pixi run pytest -k "test_pwm" -v
```

### Test Organization

```
tests/
├── unit/                    # Fast, isolated unit tests
│   ├── test_scoring/        # Scoring module tests
│   ├── test_classification/ # Classification tests
│   └── ...
├── integration/             # Slower end-to-end tests
│   ├── test_cli.py          # CLI argument parsing
│   └── test_pipeline.py     # Full pipeline tests
└── conftest.py              # Shared fixtures
```

### Writing Tests

- Place unit tests in `tests/unit/`
- Place integration tests in `tests/integration/`
- Use descriptive test names: `test_pwm_scorer_handles_n_bases`
- Use fixtures from `conftest.py` for shared setup

```python
import pytest
from intronIC.scoring.pwm import PWMScorer

class TestPWMScorer:
    """Tests for PWM scoring functionality."""

    def test_score_valid_sequence(self, sample_pwm):
        """PWM scorer returns valid scores for standard sequences."""
        scorer = PWMScorer(sample_pwm)
        score = scorer.score("GTAAGT")
        assert isinstance(score, float)
        assert not np.isnan(score)

    def test_score_with_n_bases(self, sample_pwm):
        """PWM scorer handles N bases gracefully."""
        scorer = PWMScorer(sample_pwm)
        score = scorer.score("GTANGT")
        assert score is not None  # Should not crash
```

---

## Submitting Changes

### Pull Request Process

1. **Ensure tests pass**: `make test`
2. **Ensure linting passes**: `make lint`
3. **Update documentation** if needed
4. **Update lock files** if dependencies changed: `make lock`
5. **Write a clear PR description** explaining:
   - What the change does
   - Why it's needed
   - How to test it

### Commit Message Convention

We follow [Conventional Commits](https://www.conventionalcommits.org/):

```
<type>: <description>

[optional body]

[optional footer]
```

Types:
- `feat`: New feature
- `fix`: Bug fix
- `docs`: Documentation only
- `test`: Adding or updating tests
- `refactor`: Code change that neither fixes a bug nor adds a feature
- `style`: Formatting, missing semicolons, etc.
- `build`: Changes to build system or dependencies
- `ci`: Changes to CI configuration

Examples:
```
feat: add streaming mode for large genomes
fix: correct z-score normalization to prevent data leakage
docs: update README with new CLI options
test: add integration tests for BED input mode
build: allow numpy 2.0 (compatibility verified)
```

---

## Releasing a New Version

### Version Bumping

We provide a script to update the version across all project files:

```bash
# Bump version to 2.0.2
./scripts/bump_version.sh 2.0.2

# Review changes
git diff

# Commit the version bump
git commit -am "chore: bump version to 2.0.2"
```

The script updates version in:
- `pyproject.toml` (PyPI package version)
- `pixi.toml` (Pixi workspace version)

**Note**: Version strings in source code (`src/intronIC/cli/args.py`, `src/intronIC/cli/progress.py`) are dynamically loaded from package metadata at runtime. The fallback is `"dev"` for development environments.

### Release Process

1. **Update version**:
   ```bash
   ./scripts/bump_version.sh 2.0.2
   git commit -am "chore: bump version to 2.0.2"
   ```

2. **Tag the release**:
   ```bash
   git tag v2.0.2
   ```

3. **Push to GitHub**:
   ```bash
   git push origin main v2.0.2
   ```

4. **Build distribution packages**:
   ```bash
   rm -rf dist/
   python -m build
   ```

5. **Upload to PyPI**:
   ```bash
   python -m twine upload dist/*
   ```

### Release Checklist

- [ ] All tests pass: `make test`
- [ ] Linting passes: `make lint`
- [ ] Lock files updated: `make lock`
- [ ] CHANGELOG updated (if applicable)
- [ ] Version bumped: `./scripts/bump_version.sh X.Y.Z`
- [ ] Changes committed and tagged
- [ ] Pushed to GitHub
- [ ] Distribution built: `python -m build`
- [ ] Uploaded to PyPI: `python -m twine upload dist/*`
- [ ] GitHub release created with release notes

---

## Project Structure

```
intronIC/
├── src/intronIC/           # Main package source
│   ├── cli/                # Command-line interface
│   │   ├── main.py         # Entry point
│   │   ├── args.py         # Argument parsing
│   │   └── config.py       # Configuration management
│   ├── core/               # Core data structures
│   ├── extraction/         # Intron extraction
│   ├── scoring/            # PWM scoring
│   ├── classification/     # SVM training/prediction
│   ├── output/             # File writers
│   ├── visualization/      # Plotting
│   └── utils/              # Utilities
├── tests/                  # Test suite
│   ├── unit/               # Unit tests
│   └── integration/        # Integration tests
├── config/                 # Configuration files
│   ├── config.yaml         # Default configuration
│   └── profiles/           # Config profiles
├── docs/                   # Documentation
├── pyproject.toml          # Python package config & dependencies
├── pixi.toml               # Pixi environment config
├── uv.lock                 # uv lock file (Python packages)
├── pixi.lock               # Pixi lock file (conda packages)
├── Makefile                # Development commands
└── CONTRIBUTING.md         # This file
```

### Key Files

| File | Purpose |
|------|---------|
| `pyproject.toml` | Python package metadata, dependencies, tool configs |
| `pixi.toml` | Pixi tasks and conda channel configuration |
| `uv.lock` | Locked Python dependencies for uv users |
| `pixi.lock` | Locked dependencies for pixi users |
| `config/config.yaml` | Default runtime configuration |
| `Makefile` | Development workflow commands |

---

## Questions?

- **Bug reports**: [GitHub Issues](https://github.com/glarue/intronIC/issues)
- **Feature requests**: [GitHub Issues](https://github.com/glarue/intronIC/issues)
- **Questions**: [GitHub Discussions](https://github.com/glarue/intronIC/discussions)

Thank you for contributing! 🎉
