\!DOCTYPE html>
Thank you for your interest in contributing to Mail-Rulez! This guide will help you get started with development.
Fork and Clone
# Fork the repository on GitHub, then:
git clone https://github.com/YOUR-USERNAME/mail-rulez-public.git
cd mail-rulez-public
Create Virtual Environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txt
pip install -r requirements-dev.txt # Development dependencies
Set Up Pre-commit Hooks
pre-commit install
Configure Development Environment
cp env-template .env
# Edit .env with your test email accounts
Run Tests
pytest
pytest --cov=. # With coverage
git checkout -b feature/your-feature-name
# or
git checkout -b fix/issue-description
Follow the coding standards and ensure:
# Run unit tests
pytest tests/
# Run specific test
pytest tests/test_rules.py::test_rule_creation
# Run with coverage
pytest --cov=. --cov-report=html
# Run linting
flake8 .
black . --check
# Build local image
docker build -f docker/Dockerfile.local -t mail-rulez:dev .
# Run container
docker-compose -f docker/docker-compose.test.yml up
# Access at http://localhost:5001
git add .
git commit -m "feat: add support for regex in email rules
- Add regex validation
- Update rule builder UI
- Add regex examples in documentation"
Follow conventional commits:
feat:
New featurefix:
Bug fixdocs:
Documentation changesstyle:
Code style changesrefactor:
Code refactoringtest:
Test changeschore:
Build/tooling changesgit push origin feature/your-feature-name
Then create a Pull Request on GitHub.
mail-rulez/
├── web/ # Flask web application
│ ├── routes/ # Route handlers
│ ├── templates/ # Jinja2 templates
│ └── static/ # CSS, JS, images
├── services/ # Background services
│ ├── email_processor.py # Email processing engine
│ ├── scheduler_manager.py # Task scheduling
│ └── task_manager.py # Task coordination
├── retention/ # Retention system
│ ├── manager.py # Retention logic
│ ├── scheduler.py # Retention scheduling
│ └── models.py # Data models
├── tests/ # Test suite
├── docker/ # Docker configurations
└── docs/ # Documentation
# services/email_processor.py
class EmailProcessor:
"""Core email processing logic"""
def process_account(self, account_id: int) -> ProcessingResult:
"""Process emails for a single account"""
def apply_rules(self, email: Email, rules: List[Rule]) -> List[Action]:
"""Apply rules to an email"""
# rules.py
class RuleEngine:
"""Rule matching and execution"""
def match(self, email: Email, rule: Rule) -> bool:
"""Check if email matches rule conditions"""
def execute_actions(self, email: Email, actions: List[Action]) -> None:
"""Execute rule actions on email"""
# web/routes/rules.py
@bp.route('/rules/add', methods=['GET', 'POST'])
@login_required
def add_rule():
"""Add new rule endpoint"""
-- Core tables
CREATE TABLE accounts (
id INTEGER PRIMARY KEY,
email TEXT NOT NULL,
server TEXT NOT NULL,
encrypted_password TEXT NOT NULL,
active BOOLEAN DEFAULT 1
);
CREATE TABLE rules (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
priority INTEGER DEFAULT 10,
conditions TEXT NOT NULL, -- JSON
actions TEXT NOT NULL, -- JSON
active BOOLEAN DEFAULT 1
);
CREATE TABLE processing_log (
id INTEGER PRIMARY KEY,
account_id INTEGER,
timestamp DATETIME,
emails_processed INTEGER,
duration REAL
);
# tests/test_rules.py
def test_rule_matching():
"""Test rule matching logic"""
rule = Rule(
conditions={"from_contains": ["[email protected]"]},
actions={"move_to": "Test"}
)
email = Email(from_addr="[email protected]")
assert rule_engine.match(email, rule) is True
# tests/test_integration.py
def test_email_processing_flow(test_account):
"""Test complete email processing flow"""
processor = EmailProcessor()
result = processor.process_account(test_account.id)
assert result.processed_count > 0
assert result.errors == []
Use Fixtures
@pytest.fixture
def test_account():
return Account(email="[email protected]", server="imap.test.com")
Mock External Services
@patch('imaplib.IMAP4_SSL')
def test_imap_connection(mock_imap):
mock_imap.return_value.login.return_value = ('OK', [])
Test Edge Cases
Follow PEP 8 with these additions:
Imports
# Standard library
import os
import sys
# Third-party
from flask import Flask, request
import pytest
# Local
from web.routes import auth
from services.email_processor import EmailProcessor
Type Hints
def process_emails(
account_id: int,
batch_size: int = 100,
dry_run: bool = False
) -> ProcessingResult:
"""Process emails with type hints"""
Docstrings
def calculate_retention_date(
policy: RetentionPolicy,
email_date: datetime
) -> datetime:
"""
Calculate when an email should be deleted.
Args:
policy: The retention policy to apply
email_date: Original email date
Returns:
datetime: Date when email should be deleted
Raises:
ValueError: If policy is invalid
"""
Modern ES6+
// Use const/let, arrow functions, template literals
const processEmail = async (emailId) => {
const response = await fetch(`/api/emails/${emailId}`);
return response.json();
};
Consistent Naming
// camelCase for variables and functions
const emailCount = 0;
function processNextBatch() {}
// PascalCase for classes
class EmailProcessor {}
Semantic HTML
<article class="email-rule">
<header>
<h3>{{ rule.name }}</h3>
</header>
<section class="conditions">
<!-- Rule conditions -->
</section>
</article>
BEM CSS Naming
.email-rule {}
.email-rule__header {}
.email-rule__condition {}
.email-rule--active {}
Always validate user input:
from werkzeug.security import check_password_hash
from email_validator import validate_email
def validate_account_form(form_data):
"""Validate account creation form"""
errors = []
# Validate email
try:
validate_email(form_data['email'])
except EmailNotValidError:
errors.append("Invalid email address")
# Validate server
if not form_data.get('server'):
errors.append("Server is required")
return errors
Use parameterized queries:
# Good
cursor.execute(
"SELECT * FROM accounts WHERE email = ?",
(email,)
)
# Bad - Never do this!
cursor.execute(
f"SELECT * FROM accounts WHERE email = '{email}'"
)
Escape output in templates:
<!-- Jinja2 auto-escapes by default -->
<p>{{ user_input }}</p>
<!-- For raw HTML, be explicit -->
{{ trusted_html|safe }}
Use Indexes
CREATE INDEX idx_rules_priority ON rules(priority);
CREATE INDEX idx_emails_account_date ON emails(account_id, date);
Batch Operations
# Good - Single query
cursor.executemany(
"INSERT INTO emails (subject, from_addr) VALUES (?, ?)",
email_data
)
# Bad - Multiple queries
for email in emails:
cursor.execute("INSERT INTO emails ...")
from functools import lru_cache
@lru_cache(maxsize=128)
def get_account_folders(account_id: int) -> List[str]:
"""Cache folder list for performance"""
return fetch_folders_from_imap(account_id)
Module Docstrings
"""
Email Processing Module
This module handles the core email processing logic including:
- Fetching emails from IMAP servers
- Applying rules and filters
- Moving emails to appropriate folders
"""
README Files Add README.md files in major directories explaining:
When adding features, update:
/docs
pytest
)## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] Manual testing completed
## Screenshots (if applicable)
Add screenshots for UI changes
## Related Issues
Fixes #123
By contributing, you agree that your contributions will be licensed under the project's dual license (AGPL v3 / Commercial).
Your contributions make Mail-Rulez better for everyone. We appreciate your time and effort!