Skip to main content
Skip to main content

Contributing

Thank you for your interest in contributing to FilterMate! This guide will help you get started with contributions, from bug reports to new features.

Ways to Contribute​

There are many ways to contribute to FilterMate:

  • πŸ› Report Bugs - Found an issue? Let us know!
  • πŸ’‘ Suggest Features - Have ideas for improvements?
  • πŸ“ Improve Documentation - Help others understand FilterMate
  • πŸ§ͺ Write Tests - Increase code coverage
  • πŸ”§ Fix Bugs - Tackle open issues
  • ✨ Add Features - Implement new capabilities
  • 🌍 Translations - Help localize FilterMate

Getting Started​

Prerequisites​

  1. Git - Version control
  2. Python 3.7+ - FilterMate's language
  3. QGIS 3.x - For testing in real environment
  4. Text Editor/IDE - VS Code recommended

Fork and Clone​

  1. Fork the repository on GitHub
  2. Clone your fork:
git clone https://github.com/YOUR_USERNAME/filter_mate.git
cd filter_mate
  1. Add upstream remote:
git remote add upstream https://github.com/sducournau/filter_mate.git
  1. Create a branch:
git checkout -b feature/your-feature-name
# or
git checkout -b fix/your-bug-fix

Development Setup​

See the Development Setup guide for detailed instructions on setting up your environment.

Contribution Workflow​

Step-by-Step Guide​

Before starting work:

  1. Check if an issue already exists
  2. If not, create one describing your proposed change
  3. Wait for feedback from maintainers
  4. This avoids duplicate work

2. Make Your Changes​

Follow the Code Style Guide:

  • Use PEP 8 conventions
  • Add docstrings to functions/classes
  • Keep lines under 120 characters
  • Use type hints where helpful
def my_new_function(layer: QgsVectorLayer, buffer_distance: float) -> bool:
"""
Brief description of function.

Args:
layer: The QGIS vector layer to process
buffer_distance: Buffer distance in layer units

Returns:
True if operation succeeded, False otherwise

Raises:
ValueError: If buffer_distance is negative
"""
if buffer_distance < 0:
raise ValueError("Buffer distance must be positive")

# Implementation here
return True

3. Write Tests​

Every new feature or bug fix should include tests:

# tests/test_my_feature.py
import pytest
from unittest.mock import Mock

def test_my_new_function():
"""Test the new function works correctly"""
# Arrange
mock_layer = Mock()
buffer_distance = 100.0

# Act
result = my_new_function(mock_layer, buffer_distance)

# Assert
assert result is True

def test_my_new_function_negative_buffer():
"""Test error handling for negative buffer"""
# Arrange
mock_layer = Mock()
buffer_distance = -100.0

# Act & Assert
with pytest.raises(ValueError):
my_new_function(mock_layer, buffer_distance)

See Testing Guide for more details.

4. Run Tests Locally​

# Run all tests
pytest tests/ -v

# Run with coverage
pytest tests/ --cov=modules --cov=filter_mate_app --cov-report=html

# Run specific test
pytest tests/test_my_feature.py -v

All tests must pass before submitting a PR.

5. Commit Your Changes​

Follow Conventional Commits format:

# Feature
git commit -m "feat: add support for negative buffers"

# Bug fix
git commit -m "fix: correct Spatialite field name quoting"

# Documentation
git commit -m "docs: update installation instructions"

# Tests
git commit -m "test: add unit tests for buffer operations"

# Refactor
git commit -m "refactor: extract common DB connection logic"

# Performance
git commit -m "perf: optimize spatial index creation"

Commit Message Format:

<type>: <description>

[optional body]

[optional footer]

Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation changes
  • test: Adding or updating tests
  • refactor: Code refactoring
  • perf: Performance improvements
  • style: Code style changes (formatting, etc.)
  • chore: Maintenance tasks

6. Push to Your Fork​

git push origin feature/your-feature-name

7. Create Pull Request​

  1. Go to your fork on GitHub
  2. Click "Compare & pull request"
  3. Fill out the PR template:
## Description
Brief description of changes

## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Documentation update
- [ ] Performance improvement

## Testing
- [ ] All tests pass locally
- [ ] Added new tests for changes
- [ ] Tested manually in QGIS

## Checklist
- [ ] Code follows style guidelines
- [ ] Documentation updated
- [ ] CHANGELOG.md updated
  1. Submit the PR

8. Code Review​

Maintainers will review your PR:

  • βœ… Approved - Your PR will be merged!
  • πŸ”„ Changes Requested - Address feedback and update PR
  • ❌ Closed - Explanation will be provided

Tips for review:

  • Respond to all comments
  • Ask questions if unclear
  • Be patient and respectful
  • Update your branch if requested

Code Style Guidelines​

Python Standards​

PEP 8 Conventions​

# βœ… Good
def calculate_buffer_distance(layer, percentage):
"""Calculate buffer distance based on layer extent."""
extent = layer.extent()
return extent.width() * (percentage / 100.0)

# ❌ Bad
def CalculateBufferDistance(l,p):
e=l.extent()
return e.width()*(p/100.0)

Naming Conventions​

# Classes: PascalCase
class FilterMateApp:
pass

# Functions/methods: snake_case
def get_datasource_connexion_from_layer(layer):
pass

# Constants: UPPER_SNAKE_CASE
POSTGRESQL_AVAILABLE = True

# Private methods: _prefix
def _internal_helper(self):
pass

Import Order​

# 1. Standard library
import os
import sys
from typing import Optional

# 2. Third-party (QGIS, PyQt5)
from qgis.core import QgsVectorLayer, QgsProject
from qgis.PyQt.QtCore import Qt

# 3. Local application
from .config.config import ENV_VARS
from .modules.appUtils import get_datasource_connexion_from_layer

Critical Patterns​

PostgreSQL Availability Check​

ALWAYS check before using PostgreSQL:

from modules.appUtils import POSTGRESQL_AVAILABLE

if POSTGRESQL_AVAILABLE and provider_type == 'postgresql':
# PostgreSQL-specific code
connexion = psycopg2.connect(...)
else:
# Fallback to Spatialite or OGR
pass

Provider Type Detection​

if layer.providerType() == 'postgres':
layer_provider_type = 'postgresql'
elif layer.providerType() == 'spatialite':
layer_provider_type = 'spatialite'
elif layer.providerType() == 'ogr':
layer_provider_type = 'ogr'
else:
layer_provider_type = 'unknown'

Error Handling​

# βœ… Good - Specific exceptions
try:
connexion = psycopg2.connect(conn_string)
except psycopg2.OperationalError as e:
iface.messageBar().pushCritical("FilterMate", f"Connection failed: {e}")
return None

# ❌ Bad - Bare except
try:
connexion = psycopg2.connect(conn_string)
except:
return None

Documentation Standards​

Docstrings​

Use Google-style docstrings:

def apply_geometric_filter(
layer: QgsVectorLayer,
predicate: str,
reference_geometry: QgsGeometry,
buffer_distance: float = 0.0
) -> bool:
"""
Apply geometric filter to layer using spatial predicate.

This function filters features in the layer based on their spatial
relationship with the reference geometry. An optional buffer can be
applied to the reference geometry before filtering.

Args:
layer: The QGIS vector layer to filter
predicate: Spatial predicate (e.g., 'intersects', 'contains')
reference_geometry: Geometry to filter against
buffer_distance: Optional buffer distance in layer units (default: 0.0)

Returns:
True if filter applied successfully, False otherwise

Raises:
ValueError: If predicate is not supported
RuntimeError: If layer provider doesn't support filtering

Example:
>>> layer = QgsProject.instance().mapLayersByName('cities')[0]
>>> point = QgsGeometry.fromPointXY(QgsPointXY(0, 0))
>>> apply_geometric_filter(layer, 'intersects', point, 1000.0)
True
"""
# Implementation
pass

Code Comments​

# Good comments explain WHY, not WHAT
# Use buffer to account for coordinate precision issues
buffer_distance = 0.0001

# Bad comment - states the obvious
# Set buffer distance to 0.0001
buffer_distance = 0.0001

Testing Requirements​

Test Coverage​

  • Minimum: 80% code coverage
  • Target: 90%+ code coverage
  • Run coverage report: pytest --cov=modules --cov-report=html

Test Requirements​

Every PR should include:

  1. Unit tests for new functions
  2. Integration tests for features
  3. Edge case tests for error handling
  4. Mock external dependencies (QGIS, databases)

Testing Checklist​

  • Tests for happy path
  • Tests for error conditions
  • Tests for edge cases
  • All tests pass
  • Coverage maintained or improved

Pull Request Guidelines​

PR Title​

Use conventional commit format:

feat: add Spatialite backend support
fix: correct buffer distance calculation
docs: update installation guide

PR Description​

Include:

  1. What - What does this PR do?
  2. Why - Why is this change needed?
  3. How - How does it work?
  4. Testing - How was it tested?
  5. Screenshots - If UI changes

PR Size​

Keep PRs focused and manageable:

  • βœ… Small PRs (< 500 lines) - Easy to review
  • ⚠️ Medium PRs (500-1000 lines) - Take longer
  • ❌ Large PRs (> 1000 lines) - Split if possible

Review Process​

  1. Automated checks run first

    • Tests must pass
    • Linting must pass
    • Coverage must meet threshold
  2. Maintainer review

    • Code quality
    • Adherence to style guide
    • Test coverage
    • Documentation
  3. Address feedback

    • Make requested changes
    • Push updates
    • Mark conversations as resolved
  4. Merge

    • Squash and merge (usually)
    • Your commits will be combined
    • PR description becomes commit message

Common Pitfalls​

❌ Don'ts​

  • ❌ Don't import psycopg2 without checking availability
  • ❌ Don't assume PostgreSQL is available
  • ❌ Don't use blocking operations in main thread
  • ❌ Don't forget to close database connections
  • ❌ Don't skip writing tests
  • ❌ Don't ignore warnings or errors
  • ❌ Don't hardcode file paths or credentials

βœ… Do's​

  • βœ… Do use POSTGRESQL_AVAILABLE flag
  • βœ… Do provide Spatialite/OGR fallbacks
  • βœ… Do use QgsTask for heavy operations
  • βœ… Do use try/finally for resources
  • βœ… Do write comprehensive tests
  • βœ… Do update documentation
  • βœ… Do follow coding standards

Getting Help​

Resources​

Where to Ask​

  • Bug reports β†’ GitHub Issues
  • Feature requests β†’ GitHub Issues
  • Usage questions β†’ GitHub Discussions
  • Development questions β†’ GitHub Discussions
  • Security issues β†’ Email maintainers

Code of Conduct​

Be Respectful​

  • Treat all contributors with respect
  • Welcome newcomers
  • Accept constructive criticism
  • Focus on what's best for the project
  • Show empathy

Unacceptable Behavior​

  • Harassment or discrimination
  • Trolling or insulting comments
  • Personal or political attacks
  • Publishing others' private information
  • Other unprofessional conduct

Recognition​

Contributors will be:

  • Listed in README.md
  • Credited in CHANGELOG.md
  • Thanked in release notes
  • Part of the FilterMate community! πŸŽ‰

License​

By contributing, you agree that your contributions will be licensed under the GPL-3.0 License.

Questions?​

Don't hesitate to ask! Open a GitHub Discussion or comment on an issue. We're here to help.


Happy Contributing! πŸš€


Last updated: December 8, 2025