Skip to content

Latest commit

 

History

History
529 lines (377 loc) · 12.5 KB

File metadata and controls

529 lines (377 loc) · 12.5 KB

Contributing to Python Ireland Website

Thank you for your interest in contributing to the Python Ireland website! This document provides guidelines and instructions for contributing.

Table of Contents


Code of Conduct

This project follows the Python Community Code of Conduct. Please be respectful and inclusive in all interactions.


Getting Started

Prerequisites

  • Python 3.13 (required)
  • Docker and docker-compose (recommended)
  • Git
  • Task (optional, for running predefined commands)
  • uv and prek (git hooks), pinned in mise.toml (mise install), see Git Hooks (prek)

Repository Structure

website/
├── pythonie/           # Main Django project
│   ├── core/          # Base pages and templates
│   ├── meetups/       # Meetup.com integration
│   ├── sponsors/      # Sponsor management
│   └── pythonie/      # Django settings
├── pyproject.toml      # Dependency declarations (uv)
├── uv.lock             # Locked dependency versions
├── CLAUDE.md          # AI assistant instructions
├── DEVELOPMENT.md     # Detailed development guide
└── CONTRIBUTING.md    # This file

Development Setup

Option 1: Docker (Recommended)

# Clone the repository
git clone <repository-url>
cd website

# Install the git hooks (requires uv and prek, see "Git Hooks (prek)" below;
# the ruff and Django hooks run on the host through `uv run`)
uv sync --all-groups
prek install

# Build Docker image
task docker:build
# or: make docker-build

# Start services
docker-compose up -d postgres redis

# Run migrations
task django:migrate

# Create superuser
task django:createsuperuser

# Start development server
task run

Option 2: Local Development

# Ensure Python 3.13 is installed
python3 --version  # Should show 3.13.x

# Install dependencies (creates and populates a .venv automatically)
uv sync --all-groups

# Install the git hooks (requires prek, see "Git Hooks (prek)" below)
prek install

# Run migrations
uv run python pythonie/manage.py migrate --settings=pythonie.settings.dev

# Create superuser
uv run python pythonie/manage.py createsuperuser --settings=pythonie.settings.dev

# Start server
uv run python pythonie/manage.py runserver --settings=pythonie.settings.dev

Verify Setup

  1. Visit http://localhost:8000 - should show the site
  2. Visit http://localhost:8000/admin/ - should show Wagtail admin
  3. Log in with your superuser credentials

Making Changes

Branch Naming

Use descriptive branch names with prefixes:

feature/add-dark-mode-toggle
bugfix/fix-meetup-sync-error
docs/update-readme
refactor/simplify-sponsor-model

Workflow

  1. Create a branch from master:

    git checkout master
    git pull origin master
    git checkout -b feature/your-feature-name
  2. Make your changes following the code style guidelines

  3. Test your changes:

    task tests
    # or: python pythonie/manage.py test pythonie --settings=pythonie.settings.tests
  4. Format your code:

    task code:format
    # or: python -m ruff format pythonie
  5. Commit your changes with clear messages (the prek git hooks run automatically on the staged files, see Git Hooks (prek)):

    git add .
    git commit -m "Add dark mode toggle feature"
  6. Push and create a Pull Request:

    git push origin feature/your-feature-name

Code Style

Python

  • Python version: 3.13.11 (or any Python 3.13.x - strict requirement)
  • Formatter: Ruff
  • Line length: 88 characters (Ruff default)
  • Imports: Sorted automatically by Ruff

Formatting Commands

# Format all Python files
task code:format
# or: python -m ruff format pythonie

# Lint code and fix issues
task code:lint
# or: python -m ruff check --fix pythonie

# Check code formatting and linting without changes
task code:check

Git Hooks (prek)

The repository uses prek, a fast drop-in replacement for pre-commit that reads the same .pre-commit-config.yaml. Like uv, prek is not a project dependency: it must be installed on your machine. It is pinned in mise.toml, or install it another way:

mise install            # mise: installs the versions pinned in mise.toml
brew install prek       # Homebrew
uv tool install prek    # uv

The ruff and Django hooks run through uv run, so they also need the project environment (uv sync --all-groups).

Then enable the hooks, once per clone (whether you develop with Docker or not):

# Install the git hook once per clone
prek install

# Run every hook on the whole repository
prek run --all-files

# Run a single hook
prek run ruff-check --all-files

The hooks run on every git commit, on the staged files only:

  • Generic checks (pre-commit-hooks): trailing whitespace, end of file newline, line endings, YAML/TOML/JSON syntax, merge conflict markers, large files, leftover debugger imports, private keys
  • uv-lock: keeps uv.lock in sync with pyproject.toml
  • django-upgrade: rewrites deprecated Django idioms (target: Django 6.0)
  • ruff check / ruff format: run through uv run, with the same ruff version as CI
  • Django system checks and missing migrations: manage.py check and manage.py makemigrations --check --dry-run, always against the local SQLite database (DATABASE_URL is ignored)

When a hook fixes files, the commit is aborted: review the changes, git add them and commit again. CI runs prek run --all-files on every push and pull request.

To bypass the hooks in an emergency (CI will still run them):

# Skip one or more hooks by id
SKIP=django-missing-migrations git commit -m "..."

# Skip all hooks
git commit --no-verify -m "..."

Django/Wagtail Conventions

  1. Models: Place in models.py, use explicit Meta classes
  2. Views: Prefer Wagtail page models over custom views
  3. Templates: Use template inheritance, extend base.html
  4. Settings: Never hardcode secrets, use environment variables

Documentation

  • All code comments must be in English
  • Use docstrings for functions and classes
  • Update DEVELOPMENT.md for significant changes

Example Code Style

"""Module docstring explaining purpose."""

import logging
from django.db import models
from wagtail.models import Page

logger = logging.getLogger(__name__)


class MyModel(models.Model):
    """Model representing something.

    Attributes:
        name: The display name.
        created_at: When the record was created.
    """

    name = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["name"]
        verbose_name = "My Model"
        verbose_name_plural = "My Models"

    def __str__(self):
        return self.name

    def some_method(self):
        """Perform some action.

        Returns:
            bool: True if successful, False otherwise.
        """
        logger.info(f"Processing {self.name}")
        return True

Testing

Running Tests

# All tests
task tests
# or: uv run python pythonie/manage.py test pythonie --settings=pythonie.settings.tests -v 2

# Specific app
uv run python pythonie/manage.py test pythonie.meetups --settings=pythonie.settings.tests

# Specific test file
uv run python pythonie/manage.py test pythonie.meetups.test_meetups --settings=pythonie.settings.tests

# Specific test method
uv run python pythonie/manage.py test pythonie.meetups.test_meetups.TestCase.test_method

Writing Tests

Place tests in test_*.py files within each app:

from django.test import TestCase
from model_mommy import mommy

from pythonie.sponsors.models import Sponsor


class SponsorTestCase(TestCase):
    """Tests for the Sponsor model."""

    def setUp(self):
        """Set up test fixtures."""
        self.sponsor = mommy.make(Sponsor, name="Test Sponsor")

    def test_sponsor_str(self):
        """Test string representation."""
        self.assertEqual(str(self.sponsor), "Test Sponsor")

    def test_sponsor_creation(self):
        """Test sponsor can be created."""
        sponsor = mommy.make(Sponsor)
        self.assertIsNotNone(sponsor.id)

Test Requirements

  • All new features should include tests
  • Bug fixes should include regression tests
  • Maintain or improve code coverage

Submitting Changes

Pull Request Guidelines

  1. Title: Clear, descriptive title

    • Good: "Add sponsor logo upload feature"
    • Bad: "Fix stuff" or "Updates"
  2. Description: Include:

    • What changes were made
    • Why these changes were needed
    • How to test the changes
    • Screenshots (for UI changes)
  3. Size: Keep PRs focused and reasonably sized

    • Split large changes into multiple PRs
    • One logical change per PR

Pull Request Template

## Description
Brief description of changes.

## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Documentation update
- [ ] Refactoring
- [ ] Other (describe)

## How to Test
1. Step one
2. Step two
3. Expected result

## Checklist
- [ ] Code follows project style guidelines
- [ ] Tests pass locally
- [ ] New tests added (if applicable)
- [ ] Documentation updated (if applicable)

Commit Message Guidelines

Follow conventional commit format:

<type>: <short description>

<longer description if needed>

Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation changes
  • refactor: Code refactoring
  • test: Adding or updating tests
  • chore: Maintenance tasks

Examples:

feat: Add dark mode toggle to website header

fix: Resolve meetup sync timezone issue

docs: Update installation instructions for Python 3.13

refactor: Simplify sponsor level ordering logic

Review Process

What Reviewers Look For

  1. Functionality: Does it work as intended?
  2. Code quality: Is it readable and maintainable?
  3. Tests: Are there adequate tests?
  4. Documentation: Is it documented where needed?
  5. Security: No vulnerabilities introduced?

Responding to Feedback

  • Address all reviewer comments
  • Ask questions if feedback is unclear
  • Push additional commits to address feedback
  • Re-request review when ready

Merging

  • PRs require at least one approval
  • All CI checks must pass
  • Squash and merge is preferred for clean history

Common Tasks

Adding a New Django App

# Create app
cd pythonie
uv run python ../pythonie/manage.py startapp myapp --settings=pythonie.settings.dev

# Add to INSTALLED_APPS in pythonie/pythonie/settings/base.py
INSTALLED_APPS = [
    # ...
    "pythonie.myapp",
]

# Create migrations
uv run python pythonie/manage.py makemigrations myapp --settings=pythonie.settings.dev
uv run python pythonie/manage.py migrate --settings=pythonie.settings.dev

Adding a New Wagtail Page Type

# In myapp/models.py
from wagtail.models import Page
from wagtail.fields import StreamField
from wagtail.admin.panels import FieldPanel

class MyPage(Page):
    """Description of this page type."""

    body = StreamField([...], use_json_field=True)

    content_panels = Page.content_panels + [
        FieldPanel("body"),
    ]

    # Specify allowed parent/child page types
    parent_page_types = ["core.HomePage"]
    subpage_types = []

Adding a New Management Command

# In myapp/management/commands/mycommand.py
from django.core.management.base import BaseCommand


class Command(BaseCommand):
    """Description of what this command does."""

    help = "Does something useful"

    def add_arguments(self, parser):
        parser.add_argument("--option", type=str, help="Option description")

    def handle(self, *args, **options):
        self.stdout.write("Running command...")
        # Command logic here
        self.stdout.write(self.style.SUCCESS("Done!"))

Getting Help

  • Documentation: See DEVELOPMENT.md for detailed technical docs
  • Issues: Check existing issues or create a new one
  • Community: Reach out via Python Ireland channels

Recognition

Contributors are recognized in release notes. Thank you for helping improve the Python Ireland website!