Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions config/cron.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# This script adds commands to the current crontab
#!/bin/sh

# run the daily script at 1am every morning
# TODO: make sure timezone is PST
crontab -l | { cat; echo "0 1 * * * /home/csss-site/csss-site-backend/src/cron/daily.py"; } | crontab -
# Install the weekly TransLink static schedule refresh without duplicating it.
set -eu

cron_file=$(mktemp)
trap 'rm -f "$cron_file"' EXIT

crontab -l 2>/dev/null | sed \
-e '/# BEGIN CSSS TRANSLINK STATIC/,/# END CSSS TRANSLINK STATIC/d' \
-e '\|scripts.refresh_translink_static|d' > "$cron_file" || true

{
cat "$cron_file"
printf '%s\n' \
'# BEGIN CSSS TRANSLINK STATIC' \
'PATH=/home/csss-site/.local/bin:/usr/local/bin:/usr/bin:/bin' \
'CRON_TZ=America/Vancouver' \
'0 23 * * 5 cd /home/csss-site/csss-site-backend/src && uv run python -m scripts.refresh_translink_static' \
'# END CSSS TRANSLINK STATIC'
} | crontab -
47 changes: 47 additions & 0 deletions src/scripts/refresh_translink_static.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Download, preprocess, and store the TransLink static GTFS schedule."""

import asyncio
import logging

import httpx

import database
from translink.crud import refresh_static_schedule

_logger = logging.getLogger(__name__)


async def refresh() -> None:
await database.setup_database()
if database.sessionmanager is None:
raise RuntimeError("Database has not been initialized")

manager = database.sessionmanager
try:
async with httpx.AsyncClient(timeout=60, follow_redirects=True) as client:
async with manager.session() as session:
cache = await refresh_static_schedule(session, client)
departure_count = sum(len(rows) for rows in cache["departures"].values())
_logger.info(
"Stored TransLink static schedule version %s with %s departures covering %s through %s",
cache["version"],
departure_count,
cache["coverage"]["start_date"],
cache["coverage"]["end_date"],
)
finally:
await manager.close()


def main() -> int:
logging.basicConfig(level=logging.INFO)
try:
asyncio.run(refresh())
except Exception:
_logger.exception("Failed to refresh the TransLink static schedule")
return 1
return 0


if __name__ == "__main__":
raise SystemExit(main())
24 changes: 23 additions & 1 deletion src/translink/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,30 @@ All dates are adjusted for the America/Vancouver timezone.
3. Make sure your database has the correct migrations `alembic upgrade head`. Reload your test database as well `python src/load_test_db.py`
4. Start (or restart) the web server to test the endpoints

## Static schedule refresh

The static GTFS archive is downloaded and preprocessed outside HTTP requests. Before serving the static schedule for
the first time, populate the cache manually from the `src` directory:

```bash
# in ./src
uv run python -m scripts.refresh_translink_static
```

If deploying on the web server run the cron job.
```bash
# in root
sh config/cron.sh
```
Production refreshes it every Friday at 11:00 PM in the America/Vancouver timezone. Install or update that cron entry
by running `sh config/cron.sh` from the repository root. The installer is idempotent.

If a refresh fails, the prior database row is preserved and the command exits unsuccessfully. If no compatible cache
can serve the current date, the static and combined schedule endpoints return HTTP 503; requests never download or
parse the static GTFS archive.

## Endpoints
You can see the exact schemas in the `/docs` page. At the time this was written there are three endpoints:
1. `translink/realtime`: returns realtime data for buses that are at or are approaching SFU
2. `translink/static`: returns the schedule for the current day
2. `translink/static`: returns the preprocessed schedule for the current day
3. `translink/schedule`: combines the realtime and static data to show if a bus is at the loop, is running late, or was cancelled
Loading
Loading