Developer Guide¶
Guide for developers contributing to or extending Django Micboard.
Development Setup¶
Requirements¶
- Python 3.9+
- Django 4.2+ or 5.0+
- Poetry or pip for dependency management
- Git
Local Setup¶
- Clone the repository:
git clone https://github.com/justprosound/django-micboard.git
cd django-micboard
- Create virtual environment:
python -m venv venv
source venv/bin/activate # Linux/Mac
# or
venv\Scripts\activate # Windows
- Install development dependencies:
pip install -r dev-requirements.txt
- Run tests:
pytest tests/ -v
Demo and Local Docker¶
The repository includes a demo Docker Compose configuration under demo/docker/docker-compose.yml for local testing. It runs the Django app and a demo database and is configured with a healthcheck. To improve resilience during development, consider enabling a restart: policy for the Django service in the compose file (for example restart: unless-stopped) or using a small watchdog script that probes /api/health/ and restarts the service/container when unhealthy. This is helpful when testing integrations that may cause the process to exit unexpectedly.
Example watchdog (simple):
# Simple loop to restart a container if health endpoint is failing (demo only)
while true; do
if ! curl -fsS http://localhost:8000/api/health/ >/dev/null; then
echo "Service unhealthy - restarting container"
docker-compose -f demo/docker/docker-compose.yml restart micboard-demo
fi
sleep 30
done
Project Structure¶
django-micboard/
├── micboard/ # Main package
│ ├── admin/ # Django admin interfaces
│ │ ├── assignments.py # Assignment/alert admins
│ │ ├── devices.py # Device admins
│ │ └── monitoring.py # Monitoring/config admins
│ ├── models/ # Django models
│ │ ├── assignments.py # User assignments & alerts
│ │ ├── devices.py # Receivers, channels, transmitters
│ │ └── locations.py # Locations & monitoring groups
│ ├── shure/ # Shure API integration
│ │ ├── client.py # HTTP client with pooling
│ │ ├── transformers.py # Data transformers
│ │ └── websocket.py # WebSocket handling
│ ├── views/ # Django views
│ │ ├── api.py # REST API endpoints
│ │ └── dashboard.py # Dashboard views
│ ├── management/commands/ # Management commands
│ │ ├── poll_devices.py # Device polling command
│ │ └── realtime_status.py # Real-time connection monitoring
│ ├── tasks/ # Background tasks
│ │ ├── polling_tasks.py # Device polling tasks
│ │ ├── sse_tasks.py # SSE subscription tasks
│ │ ├── websocket_tasks.py # WebSocket subscription tasks
│ │ └── health_tasks.py # Health monitoring tasks
│ ├── models/ # Django models
│ │ ├── realtime.py # Real-time connection tracking
│ │ ├── assignments.py # User assignments & alerts
│ │ ├── devices.py # Receivers, channels, transmitters
│ │ └── locations.py # Locations & monitoring groups
├── tests/ # Test suite
│ ├── test_models.py # Model tests
│ └── test_package_structure.py # PyPI validation
├── docs/ # Documentation
└── pyproject.toml # Package configuration
Code Organization¶
Package Split Rationale¶
The codebase has been organized into focused modules for maintainability:
Shure Package (micboard/shure/)¶
Purpose: Isolate all Shure System API interaction logic.
- client.py (394 lines)
- HTTP client with connection pooling
- Automatic retry with exponential backoff
- Health tracking and error handling
-
Thread-safe operations
-
transformers.py (286 lines)
- Transform Shure API responses → micboard format
- Channel status computation
- Battery health analysis
-
Signal quality calculations
-
websocket.py (136 lines)
- Real-time WebSocket connections to Shure devices
- Automatic reconnection
- Message parsing and routing
Admin Package (micboard/admin/)¶
Purpose: Organize Django admin by functional area.
- devices.py (189 lines)
- ReceiverAdmin, ChannelAdmin, TransmitterAdmin
- List displays, filters, search
-
Read-only fields for API-sourced data
-
assignments.py (100 lines)
- DeviceAssignmentAdmin, AlertAdmin
- User assignment management
-
Alert preferences
-
monitoring.py (57 lines)
- LocationAdmin, MonitoringGroupAdmin
- Configuration admin
- Group management
Serializers Module (micboard/serializers.py)¶
Purpose: DRY principle for data serialization.
- 8 reusable functions with keyword-only parameters
- Eliminates ~100 lines of duplicate code
- Consistent serialization across views and commands
Django Best Practices¶
Keyword-Only Parameters¶
All functions use keyword-only parameters for clarity:
def serialize_receiver_detail(receiver, *, include_extra=False):
"""
Serialize receiver with keyword-only parameters.
Args:
receiver: Receiver instance
include_extra: Whether to include computed properties (keyword-only)
"""
pass
Benefits:
- Prevents boolean confusion: func(receiver, True) vs func(receiver, include_extra=True)
- Self-documenting code
- Easier to extend without breaking compatibility
Type Hints¶
Modern type hints with __future__ annotations:
from __future__ import annotations
from typing import Optional, Dict, List
from django.db.models import QuerySet
def process_devices(
receivers: QuerySet[Receiver],
*,
include_offline: bool = False
) -> List[Dict[str, Any]]:
"""Process receivers with full type annotations."""
pass
Model Managers¶
Custom managers for common queries:
class ReceiverManager(models.Manager):
def active(self) -> QuerySet[Receiver]:
"""Return active receivers."""
return self.filter(is_active=True)
def online_recently(self, *, minutes: int = 30) -> QuerySet[Receiver]:
"""Return receivers seen in last N minutes."""
threshold = timezone.now() - timedelta(minutes=minutes)
return self.filter(last_seen__gte=threshold)
Testing¶
Running Tests¶
# All tests
pytest tests/ -v
# Specific test file
pytest tests/test_models.py -v
# Specific test
pytest tests/test_models.py::TestReceiver::test_mark_online -v
# With coverage
pytest tests/ --cov=micboard --cov-report=html
Test Structure¶
Model Tests (test_models.py)¶
38 tests covering all models: - Receiver online/offline tracking - Channel-Transmitter relationships - DeviceAssignment validation - Alert creation and preferences - Location and MonitoringGroup hierarchies
Real-Time Tests (test_realtime.py)¶
3 tests covering real-time connection functionality: - Connection status summary - Model constants validation - Health monitoring functions
Writing Tests¶
Follow these conventions:
import pytest
from django.test import TestCase
from micboard.models import Receiver, RealTimeConnection
class TestRealTimeConnection(TestCase):
"""Test RealTimeConnection model."""
def setUp(self):
"""Set up test fixtures."""
self.manufacturer = Manufacturer.objects.create(
name="Test Manufacturer",
code="test",
is_active=True
)
def test_connection_lifecycle(self):
"""Test connection status transitions."""
# Test connection state changes
connection.mark_connecting()
self.assertEqual(connection.status, "connecting")
connection.mark_connected()
self.assertEqual(connection.status, "connected")
connection.mark_disconnected("Test error")
self.assertEqual(connection.status, "disconnected")
Code Quality¶
Style Guide¶
- PEP 8 compliant
- Type hints required for public functions
- Docstrings required for all modules, classes, and public functions
- Keyword-only parameters for boolean/optional arguments
Linting¶
# Run ruff for linting
ruff check micboard/
# Auto-fix issues
ruff check --fix micboard/
# Format code
ruff format micboard/
Type Checking¶
# Run mypy
mypy micboard/
Django Compatibility¶
Supported Versions¶
- Django 4.2 LTS
- Django 5.0+
- Python 3.9+
No Backwards Compatibility¶
This package targets modern Django and Python versions only. We do not maintain backwards compatibility with: - Django < 4.2 - Python < 3.9 - Deprecated Django APIs
Migration Strategy¶
When Django deprecates APIs: 1. Update to new API immediately 2. No transitional compatibility layers 3. Bump minimum Django version requirement
Shure API Integration¶
Client Architecture¶
The Shure System API client uses:
- requests.Session for connection pooling
- urllib3.Retry for automatic retries
- Exponential backoff (0.5s, 1s, 2s)
- Health tracking (consecutive failures)
Adding New Device Types¶
- Add device type to
DEVICE_TYPE_CHOICESin models:
DEVICE_TYPE_CHOICES = [
('ulxd', 'ULX-D'),
('qlxd', 'QLX-D'),
('axient', 'Axient Digital'),
('new_type', 'New Type'), # Add here
]
- Add transformer logic in
shure/transformers.py:
def transform_new_type(device_data: Dict[str, Any]) -> Dict[str, Any]:
"""Transform new device type data."""
pass
- Update client in
shure/client.pyto handle new endpoints
WebSocket Integration¶
For real-time updates from Shure devices:
from micboard.shure import ShureWebSocketClient
client = ShureWebSocketClient(ip='192.168.1.100')
client.connect()
client.subscribe_to_updates(callback=handle_update)
Deployment¶
PyPI Package¶
Build and publish:
# Build package
python -m build
# Check package
twine check dist/*
# Upload to PyPI
twine upload dist/*
Documentation¶
Build documentation:
# Using MkDocs
mkdocs build
# Serve locally
mkdocs serve
Deploy to Read the Docs:
1. Connect GitHub repository at https://readthedocs.org
2. The .readthedocs.yaml configuration file handles the build automatically
3. Documentation auto-builds on push to main branch
4. Visit https://django-micboard.readthedocs.io to view the documentation
Contributing¶
Workflow¶
- Fork the repository
- Create feature branch:
git checkout -b feature/my-feature - Make changes with tests
- Run test suite:
pytest tests/ - Commit with clear messages
- Push and create pull request
Commit Messages¶
Follow conventional commits:
feat: Add support for Axient Digital receivers
fix: Correct battery percentage calculation
docs: Update API reference
test: Add tests for device discovery
refactor: Split shure_api_client into package
chore: Update dependencies
Pull Request Guidelines¶
- Include tests for new features
- Update documentation
- Ensure all tests pass
- Follow code style guidelines
- Keep changes focused and atomic
Getting Help¶
- GitHub Issues: https://github.com/justprosound/django-micboard/issues
- Documentation: https://django-micboard.readthedocs.io
- Community Discussions: Open an issue on GitHub
License¶
AGPL-3.0-or-later - see LICENSE file for details.