Thank you for your interest in contributing to the Course Constraint Scheduler! This document provides guidelines and instructions for developers who want to contribute to the project.
- Getting Started
- Development Setup
- Project Structure
- Development Workflow
- Code Standards
- Testing
- Documentation
- Submitting Changes
- Release Process
- Python 3.12+: The project requires Python 3.12 or higher
- Git: For version control
- uv: Modern Python package manager (recommended)
We strongly recommend using uv for dependency management and virtual environments. It's faster and more reliable than traditional tools.
macOS and Linux:
curl -LsSf https://astral.sh/uv/install.sh | shWindows:
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"Alternative (pip):
pip install uvgit clone https://github.com/mucsci/scheduler.git
cd schedulerRecommended approach using uv:
# Create and activate virtual environment
uv venv
source .venv/bin/activate # On macOS/Linux
# OR
.venv\Scripts\activate # On Windows
# Install dependencies and the package (dev group is included by default; see tool.uv in pyproject.toml)
uv syncAlternative (if uv is not available):
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # On macOS/Linux
# OR
.venv\Scripts\activate # On Windows
# Install the package (editable). There is no checked-in requirements.txt.
pip install -e .
# Dev tools (pytest, ruff, ty, …) are listed under [dependency-groups] dev in pyproject.toml;
# install the ones you need with pip, or prefer uv sync.# Using uv (recommended): `uv sync` installs the dev group by default
uv sync
# If you disabled default-groups, install dev explicitly:
uv sync --group dev
# Using pip: there is no `[project.optional-dependencies]` dev extra; use uv, or install
# tools from the dev list in pyproject.toml manually after `pip install -e .`After uv sync, install hooks so the same checks as CI run on each commit:
uv run prek install
# If you previously used pre-commit and need to replace its hook script:
uv run prek install -fConfiguration lives in .pre-commit-config.yaml (prek is compatible with this format).
# Test the scheduler
uv run python -m scheduler.main --help
# Test the server
uv run python -m scheduler.server --help
# Run tests
uv run pytestsrc/scheduler/
├── __init__.py # Main package exports
├── config.py # Configuration models and validation
├── json_types.py # TypedDict definitions for JSON structures
├── main.py # Command-line interface
├── scheduler.py # Core scheduling logic and Z3 integration
├── server.py # FastAPI REST server
├── logging.py # Logging configuration
├── models/ # Data models
│ ├── __init__.py # Model exports
│ ├── course.py # Course and CourseInstance models
│ ├── day.py # Day enumeration
│ └── time_slot.py # Time-related models (TimeSlot, TimeInstance, etc.)
├── writers/ # Output formatters
│ ├── __init__.py # Writer exports
│ ├── json_writer.py # JSON output writer
│ └── csv_writer.py # CSV output writer
└── time_slot_generator.py # Time slot generation utilities
fern/ # Fern documentation site (published to Fern Cloud)
├── docs.yml # Site config, navigation, branding
├── generators.yml # OpenAPI spec registration
├── openapi.json # Generated from FastAPI (do not edit by hand)
├── fern.config.json # Fern org + CLI version
└── docs/
├── pages/ # MDX guides (configuration, Python, dev, …)
└── assets/ # Logos, favicon, combined-config.schema.json
docs/
└── configuration.md # Redirect pointer to published docs
scripts/
├── export_openapi.py # Refresh fern/openapi.json
├── export_config_schema.py # Refresh JSON Schema asset
└── gen_python_api_mdx.py # Refresh Python API reference from docstrings
# Ensure you're on main and up to date
git checkout main
git pull origin main
# Create and checkout a feature branch
git checkout -b feature/your-feature-name- Write your code following the Code Standards
- Update documentation as needed
- Ensure all tests pass
# Run tests
uv run pytest
# Run linting
uv run ruff check .
# Run type checking
uv run ty check . --ignore unresolved-import
# Or run the full hook suite (matches CI)
uv run prek run --all-files# Stage your changes
git add .
# Commit with a descriptive message
git commit -m "feat: add new optimization algorithm for room packing
- Implemented improved room packing algorithm
- Added configuration option for packing strategy
- Updated tests and documentation
- Performance improvement of 15% for large schedules"git push origin feature/your-feature-nameThen create a Pull Request on GitHub with:
- Clear description of changes
- Reference to any related issues
- Screenshots for UI changes
- Performance impact analysis if applicable
We follow PEP 8 with some modifications enforced by our linting tools.
Key Standards:
- Use 4 spaces for indentation (no tabs)
- Maximum line length: 120 characters (enforced by Ruff format; see
[tool.ruff]inpyproject.toml) - Use descriptive variable and function names
- Add type hints for all function parameters and return values
- Use f-strings for string formatting (Python 3.6+)
# Standard library imports
import json
import logging
from typing import List, Optional
# Third-party imports
import z3
from pydantic import BaseModel
# Local imports
from .models import Course, CourseInstance
from .config import SchedulerConfigDocstrings:
def generate_schedule(config: SchedulerConfig) -> List[CourseInstance]:
"""Generate a course schedule based on configuration.
**Args:**
- config: The scheduler configuration containing courses, faculty, and constraints.
**Returns:**
A list of course instances representing the generated schedule.
**Raises:**
- ValueError: If the configuration is invalid.
- RuntimeError: If no valid schedule can be generated.
**Example:**
>>> from scheduler.config import CombinedConfig
>>> config = load_config_from_file(CombinedConfig, "config.json")
>>> schedule = generate_schedule(config, limit=5)
>>> print(f"Generated {len(schedule)} courses")
"""
passInline Comments:
# Use comments to explain WHY, not WHAT
# Avoid obvious comments like "increment counter"
# Good: "Skip Fridays for labs as they're not available for scheduling"
if day == Day.FRI:
continue# Use specific exception types
try:
result = z3_solver.check()
except z3.Z3Exception as e:
logger.error(f"Z3 solver failed: {e}")
raise RuntimeError(f"Schedule generation failed: {e}") from e
# Provide meaningful error messages
if not faculty_available:
raise ValueError(
f"Faculty member '{faculty_name}' has no available time slots "
f"that match the course requirements"
)- All public functions and classes must have docstrings
- Use Google-style docstrings for consistency
- Include examples for complex functions
- Document exceptions and error conditions
- Update REST API documentation for new endpoints
- Include request/response examples
- Document error codes and messages
- Regenerate
fern/openapi.jsonwithuv run python scripts/export_openapi.pywhenserver.pyor shared models change - Regenerate
fern/docs/pages/python/reference.mdxwithuv run python scripts/gen_python_api_mdx.pywhen public docstrings change
- Update Fern pages under
fern/docs/pages/(configuration, welcome, development) - Update README.md for new features
- Regenerate
fern/docs/assets/combined-config.schema.jsonwithuv run python scripts/export_config_schema.pywhenCombinedConfigchanges - Keep generated Fern artifacts committed when they are used by docs publishing (
fern/openapi.json,fern/docs/assets/combined-config.schema.json,fern/docs/pages/python/reference.mdx) - Preview locally:
npm install -g fern-apithenfern docs dev(after the generate scripts above)
Before submitting a pull request, ensure:
- Code passes linting (
uv run ruff check .) - Type checking passes (
uv run ty check . --ignore unresolved-import) - Tests pass (
uv run pytest) - Documentation is updated
- Breaking changes are documented
- Commit messages follow conventional format
We use Conventional Commits format:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, etc.)refactor: Code refactoringtest: Adding or updating testschore: Maintenance tasks
Examples:
feat: add new optimization algorithm for room packing
fix(scheduler): resolve memory leak in large schedule generation
docs: update configuration guide with new options
test: add performance benchmarks for optimization algorithms
- Automated Checks: CI/CD pipeline runs tests and linting
- Code Review: At least one maintainer must approve
- Testing: Changes are tested in staging environment
- Merge: Changes are merged to main branch
We use Semantic Versioning:
- MAJOR: Breaking changes
- MINOR: New features (backward compatible)
- PATCH: Bug fixes (backward compatible)
- Update Version: Update version in
pyproject.toml - Release Notes: Prepare release notes in the GitHub release description (or update
CHANGELOG.mdif your release includes one) - Tag Release: Create git tag for the version
- Build Package: Build and test the package
- Publish: Publish to PyPI
- Documentation: Update documentation for new version
# Build package
uv run build
# Test package
uv run twine check dist/*
# Publish to PyPI
uv run twine upload dist/*- GitHub Issues: Bug reports and feature requests
- GitHub Discussions: General questions and discussions
- Pull Requests: Code review and collaboration
New contributors are welcome! We're happy to:
- Help you get started
- Review your first pull request
- Pair program on complex features
- Provide guidance on best practices
We are committed to providing a welcoming and inclusive environment for all contributors. Please read our Code of Conduct for details.
By contributing to this project, you agree that your contributions will be licensed under the MIT License. See LICENSE for details.
Thank you for contributing to the Course Constraint Scheduler! Your contributions help make academic scheduling more efficient and accessible for everyone.