Django application for aggregating and displaying sports events (football, cycling, tennis, motorsports, and more) with TV channel information.
soccertime/
├── compose.yaml # Docker Compose for development
├── compose.production.yaml # Docker Compose for production
├── Dockerfile # Application Docker image
├── Makefile # Deployment and management commands (deprecated)
├── CHANGELOG.md # Project history and versioning
├── pyproject.toml # Python project config (pytest, ruff, coverage)
├── requirements.txt # Python dependencies (pinned versions)
├── .env.example # Environment variables template
├── .env.production.local.example # Local production simulation env template
├── soccertime/ # Django application
│ ├── models.py # Data models (Event, Match, Race, etc.)
│ ├── views.py # View functions
│ ├── admin.py # Django admin configuration
│ ├── static/ # Static assets (CSS, JS)
│ ├── tests/ # Test suite (pytest)
│ ├── fixtures/ # Initial data fixtures (auto-loaded on fresh DB)
│ └── management/commands/ # Custom management commands
├── templates/ # HTML templates
├── media/ # Media files (crests, flags)
└── db/ # SQLite database
- Clone the repository:
git clone <repository-url>
cd soccertime- Create the environment file:
cp .env.example .env- Start the application:
docker compose up -d --build- Apply migrations:
docker compose exec web python manage.py migrateNote: Initial fixtures (sports, competitions, teams, and favorites) are automatically loaded when migrations run on a fresh database.
- Access the application at http://localhost:8000
# Start services
docker compose up -d
# View logs
docker compose logs -f web
# Apply migrations
docker compose exec web python manage.py migrate
# Create migrations
docker compose exec web python manage.py makemigrations soccertime
# Create superuser
docker compose exec web python manage.py createsuperuser
# Collect static files
docker compose exec web python manage.py collectstatic --noinput
# Run data scraper
docker compose exec web python manage.py scrapit
# Run scraper (dry run - show events without saving)
docker compose exec web python manage.py scrapit --dry-run
# List available scraping sources
docker compose exec web python manage.py scrapit --list-sources
# Reset database (delete and recreate with fresh migrations + fixtures)
docker compose exec web python manage.py resetdb
# Stop services
docker compose downThe application includes initial fixtures that are automatically loaded when migrations run on a fresh database:
- Sports: Fútbol, Automovilismo, Motociclismo, Baloncesto
- Competitions: La Liga EA Sports, Champions League, Fórmula 1, MotoGP
- Teams: FC Barcelona, CD Castellón, Barça Basket, FC Barcelona Femenino, Barcelona Atlétic
- Favorites: All teams and competitions above are automatically added as favorites
Fixture files are located in soccertime/fixtures/:
initial_data.json: Sports, competitions, and teamsfavorites.json: Favorite teams and competitions
To manually load fixtures:
# Load all fixtures
docker compose exec web python manage.py loaddata initial_data favorites
# Load specific fixture
docker compose exec web python manage.py loaddata initial_dataFor development purposes, you can reset the entire database:
# Interactive reset (asks for confirmation)
docker compose exec web python manage.py resetdb
# Non-interactive reset (no confirmation)
docker compose exec web python manage.py resetdb --noinputThis command will:
- Delete the SQLite database file
- Run all migrations to recreate the schema
- Automatically load initial fixtures (via post_migrate signal)
The project uses pytest with pytest-django for testing.
# Run all tests
docker compose exec web pytest
# Run tests with verbose output
docker compose exec web pytest -v
# Run tests with coverage report
docker compose exec web pytest --cov --cov-report=term-missing
# Run tests with HTML coverage report
docker compose exec web pytest --cov --cov-report=html
# Then open htmlcov/index.html in your browser
# Run specific test file
docker compose exec web pytest soccertime/tests/test_models.py
# Run specific test class
docker compose exec web pytest soccertime/tests/test_models.py::TestMatch
# Run tests excluding integration tests (faster)
docker compose exec web pytest -m "not integration"The project uses Ruff for linting and code formatting.
# Check for linting errors
docker compose exec web ruff check soccertime/
# Fix auto-fixable linting errors
docker compose exec web ruff check soccertime/ --fix
# Format code
docker compose exec web ruff format soccertime/
# Check formatting without applying changes
docker compose exec web ruff format soccertime/ --checkThis repository includes a local production-like stack in compose.production.local.yaml.
- Create the local production env file from template:
cp .env.production.local.example .env.production.local- Generate a local TLS certificate and private key for
mojon.local:
mkdir -p .docker/traefik/certs
openssl req -x509 -nodes -newkey rsa:2048 \
-keyout .docker/traefik/certs/mojon.local.key \
-out .docker/traefik/certs/mojon.local.crt \
-days 365 \
-subj "/CN=mojon.local"- Start the local production stack:
docker compose -f compose.yaml -f compose.production.yaml -f compose.production.local.yaml up -d --build- Optionally map local hostnames in
/etc/hosts:
127.0.0.1 mojon.local traefik.mojon.local
Security note:
.docker/traefik/certs/mojon.local.keyis intentionally ignored and must never be committed.
Note: The previous deployment method using the
Makefileis considered deprecated. The new workflow will be based on building and deploying a production-ready Docker image. The specific steps (CI/CD pipeline, registry pushes) are to be defined.The Makefile commands documented below remain functional but should not be relied upon for new deployments.
Deployment was previously done through the Makefile which automates the entire process.
ChannelLinkusa ManyToMany conChannelLinkSourcepara que un mismo enlace pertenezca a varias fuentes.ChannelLinkSource: camposname(único),display_name(por defecto al nombre),enabled(bool). Señales eliminanChannelLinkhuérfanos al borrar la última source.- Comando unificado:
docker compose exec web python -m manage addlinksource --source <newera|elcano> --file <path> [--dry]- Soporte de fuentes:
newera: Formato de texto con bloques de 2 líneas (Nombre --> Subcat / Link).elcano: Formato de texto estructurado por secciones (=== CATEGORIA ===).
- Estrategias de Matching:
- Normalización de nombres (
fix_name) para mapear variantes (ej: "Movistar" -> "M+"). - Extracción inteligente de calidad (SD, HD, FHD, UHD, 1080p, 720p).
- Lógica de seguridad para nombres cortos (evita falsos positivos como "La" -> "LaLiga").
- Filtro Anti-Horeca: Evita asociar enlaces residenciales a canales de bares/restaurantes salvo que el enlace lo especifique.
- Normalización de nombres (
- Soporte de fuentes:
- Admin:
ChannelLinkSourceregistrado; enChannelLinkpuedes filtrar/buscar/seleccionar sources.
- SSH access to the production server
.env.productionfile configured locally- Changes committed to git (deploy uses
git archive HEAD)
# Show all available commands
make help| Command | Description |
|---|---|
make deploy-production |
Full deployment (upload code + run on remote) |
make upload-only |
Upload code and configs without running deploy |
make upload-config |
Upload only configuration files |
make remote-restart |
Restart remote services without uploading code |
| Command | Description |
|---|---|
make download-db |
Download database from server |
make upload-db |
Upload database to server |
| Command | Description |
|---|---|
make download-requests-cache |
Download requests cache from server |
make upload-requests-cache |
Upload requests cache to server |
| Command | Description |
|---|---|
make download-media |
Download media directory from server |
make upload-media |
Upload media directory to server |
Note: All upload/download commands create automatic backups before overwriting.
# 1. Make changes to the code
vim soccertime/views.py
# 2. Commit the changes (IMPORTANT: deploy uses git archive HEAD)
git add -p
git commit -m "feat: new feature"
# 3. Deploy to production
make deploy-productionExpected output:
--- Archiving application files ---
git archive --format=tgz -o /tmp/soccertime.tgz HEAD
--- Uploading application archive and configuration files ---
soccertime.tgz 100% 150KB 1.2MB/s 00:00
compose.production.yaml 100% 1882 50KB/s 00:00
.env.production 100% 481 15KB/s 00:00
--- Initiating remote deployment via SSH ---
--- Pulling latest Docker images ---
--- Stopping and removing old services ---
--- Extracting new application code ---
--- Copying compose file to compose.yaml ---
--- Bringing up new services ---
--- Applying database migrations ---
--- Collecting static files ---
Deployment process completed successfully.
The make deploy-production command executes the following steps:
- archive_app: Creates a
.tgzarchive of the code usinggit archive HEAD - upload_files: Uploads the archive,
compose.production.yamland.env.productionto the server - remote_deploy:
- Pulls the latest Nginx image
- Stops current services
- Extracts new code
- Brings up services with
docker compose up -d --build - Runs database migrations
- Collects static files
- clean_local_archive: Removes the local temporary archive
In production, the application runs with:
- Nginx: Reverse proxy to serve static and media files
- Uvicorn: ASGI server for the Django application
- Read-only containers: For security, containers use
read_only: truewithtmpfsfor temporary directories - Shared network: Services communicate through an external Docker network (
shared_network)
Make sure compose.production.yaml has the tmpfs configured for the cache directory:
tmpfs:
- /tmp:size=50M,mode=1777
- /var/tmp/soccertime_cache:size=50M,mode=1777The git archive HEAD command only includes committed changes. Verify your changes are in the commit:
# View uncommitted changes
git status
# Commit changes
git add <files>
git commit -m "description"
# Deploy again
make deploy-productionThis may indicate that the health check is failing. Connect to the server and check the logs:
ssh -p2200 user@hostname
cd docker/soccertime
docker compose -f compose.production.yaml logs webSee .env.example for the complete list of available variables.
| Variable | Development | Production | Description |
|---|---|---|---|
DJANGO_SECRET_KEY |
(auto-generated) | required | Secret key for cryptographic signing |
DJANGO_DEBUG |
true |
false |
Debug mode |
DJANGO_CACHE |
false |
true |
Enable template caching |
DJANGO_ALLOWED_HOSTS |
localhost |
* |
Allowed hosts |
DJANGO_STATIC_URL |
/static/ |
/static/ |
Static files URL |
DJANGO_FORCE_SCRIPT_NAME |
- | (optional) | URL prefix (only when intentionally serving under a subpath) |
DOCKER_UID |
1000 |
1000 |
User ID for application and database files |
DOCKER_GID |
1000 |
1000 |
Group ID for application and database files |
python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"Private project.