{"id":31647649,"url":"https://github.com/ducks/discourse-transit-tracker","last_synced_at":"2025-10-07T06:44:39.527Z","repository":{"id":318186084,"uuid":"1069989794","full_name":"ducks/discourse-transit-tracker","owner":"ducks","description":"Split-flap departure board plugin for Discourse","archived":false,"fork":false,"pushed_at":"2025-10-05T16:22:03.000Z","size":495,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-05T18:26:05.903Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ducks.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-05T03:08:00.000Z","updated_at":"2025-10-05T16:22:06.000Z","dependencies_parsed_at":"2025-10-05T18:26:07.555Z","dependency_job_id":"f39b570d-3622-4f81-a944-42f5d2d2572c","html_url":"https://github.com/ducks/discourse-transit-tracker","commit_stats":null,"previous_names":["ducks/discourse-transit-tracker"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/ducks/discourse-transit-tracker","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducks%2Fdiscourse-transit-tracker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducks%2Fdiscourse-transit-tracker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducks%2Fdiscourse-transit-tracker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducks%2Fdiscourse-transit-tracker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ducks","download_url":"https://codeload.github.com/ducks/discourse-transit-tracker/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducks%2Fdiscourse-transit-tracker/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278734434,"owners_count":26036404,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-07T02:00:06.786Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-07T06:44:36.360Z","updated_at":"2025-10-07T06:44:39.520Z","avatar_url":"https://github.com/ducks.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Discourse Transit Tracker\n\nA Discourse plugin that transforms your forum into a live departure board for tracking planes, trains, and public transit. Think airport/train station split-flap displays, but powered by Discourse topics.\n\n![Departure Board](discourse-transit-tracker-1.png)\n\n## Why?\n\nDiscourse topics are surprisingly flexible containers for structured data. This plugin proves you can use them to store and display time-sensitive transit information with built-in update history, moderation tools, and a beautiful UI.\n\nIs this practical for real transit tracking? Probably not. But it's a great example of pushing Discourse in unexpected directions to learn the platform deeply.\n\n## Features\n\n### Split-Flap Departure Board UI\n- Classic airport/train station aesthetic with dark gradients and yellow text\n- Real-time countdown timers updating every second\n- Filter by mode: flights, trains, buses, trams, metro, or view all\n- Status indicators: scheduled (green), delayed (orange pulse), departed (gray), canceled (red strikethrough)\n- Responsive design: desktop grid layout, mobile stacked layout\n\n### Expandable Schedule Details\nClick any departure to expand and see:\n- Complete stop-by-stop schedule with arrival/departure times\n- Route and direction information\n- All data rendered from Discourse posts (Post #2 contains the schedule table)\n- Smooth accordion animation\n\n### Topics as Departures\nEach departure is a Discourse topic with:\n- Custom fields for all timing, route, and location data\n- Tags for mode (`flight`, `train`, `bus`) and status (`status:scheduled`, etc.)\n- Post #1: Basic departure info\n- Post #2: Complete schedule table (for multi-stop trips)\n- Post #3+: Delay notifications and status updates\n\n### Multiple Data Sources\n\n**Amtrak (GTFS)**:\n- Downloads and parses Amtrak's GTFS feed\n- Creates departure topics with detailed stop schedules\n- Handles ~2,300 trips, creates ~600 topics (duplicates merged)\n- Includes coordinates for every stop\n\n**NYC MTA Subway (GTFS)**:\n- Downloads and parses MTA's massive GTFS feed (500k+ stop times)\n- Creates metro departure topics with complete schedules\n- Official MTA line colors (red 1/2/3, green 4/5/6, yellow N/Q/R/W, etc.)\n- 6-hour import window creates ~5,000 departure topics\n- Per-route query optimization for fast filtering\n\n**Flights (AviationStack API)**:\n- Flight departure tracking\n- Gate assignments and delay detection\n- Code-share flight detection and merging\n- Multiple airlines on same physical flight merged into one topic\n\n## Installation\n\n### 1. Add the plugin to your Discourse\n\n```bash\ncd /var/discourse\nnano containers/app.yml\n```\n\nAdd to the `hooks.after_code` section:\n\n```yaml\nhooks:\n  after_code:\n    - exec:\n        cd: $home/plugins\n        cmd:\n          - git clone https://github.com/ducks/discourse-transit-tracker.git\n```\n\nRebuild:\n\n```bash\n./launcher rebuild app\n```\n\n### 2. Run the setup task\n\n```bash\ncd /var/discourse\n./launcher enter app\nrake transit_tracker:setup\n```\n\nThis creates:\n- Three categories: Planes, Trains, Public Transit\n- Tag groups for mode and status\n- Updates plugin settings with category IDs\n\n### 3. Enable the plugin\n\nGo to `/admin/site_settings/category/plugins?filter=transit` and set:\n\n- **transit_tracker_enabled**: `true`\n\n### 4. Configure data sources\n\nChoose one or more:\n\n#### Option A: Amtrak GTFS (recommended for testing)\n\n```bash\n./launcher enter app\ncd /var/www/discourse\nRAILS_ENV=production bundle exec rake transit_tracker:import_amtrak\n```\n\nThis downloads the latest Amtrak GTFS feed and creates ~600 departure topics with complete schedules. No API key required.\n\n#### Option B: NYC MTA Subway GTFS\n\n```bash\n./launcher enter app\ncd /var/www/discourse\nRAILS_ENV=production bundle exec rake transit_tracker:import_mta\n```\n\nThis downloads the MTA's GTFS feed and creates subway departure topics for the next 6 hours (~5,000 topics). Includes official line colors. No API key required.\n\n**Note**: MTA GTFS is very large (500k+ stop times). The 6-hour window is intentional to avoid memory issues and topic overload.\n\n#### Option C: Flights (AviationStack API)\n\nGet an API key from [aviationstack.com](https://aviationstack.com/), then configure:\n\n- **transit_tracker_aviationstack_api_key**: Your API key\n- **transit_tracker_monitored_airports**: Comma-separated airport codes (e.g., `SFO,LAX`)\n\n### 5. View the board\n\nNavigate to `/board` to see your departure board in action.\n\n## Routes\n\n- **`/board`** - Full-screen departure board UI\n- **`/board?mode=train`** - Filter by mode (train, flight, bus, tram, metro)\n- **`/transit/board`** - JSON API endpoint for departure data\n- **`/transit/proxy?ids=U297Z1P\u0026minutesAfter=30`** - Proxy for Golemio API (optional, hides API token)\n\n## Data Model\n\nEach departure is stored as a topic with custom fields:\n\n| Field | Description |\n|-------|-------------|\n| `transit_service_date` | Service date (YYYY-MM-DD) |\n| `transit_trip_id` | Unique trip identifier (natural key) |\n| `transit_route_short_name` | Route number/code |\n| `transit_route_color` | Route color hex code (no #) for badge styling |\n| `transit_headsign` | Destination name |\n| `transit_origin` | Origin code (airport/station) |\n| `transit_origin_name` | Origin full name |\n| `transit_dest` | Destination code |\n| `transit_dest_name` | Destination full name |\n| `transit_dep_sched_at` | Scheduled departure (UTC) |\n| `transit_dep_est_at` | Estimated departure (UTC) |\n| `transit_arr_sched_at` | Scheduled arrival (UTC) |\n| `transit_arr_est_at` | Estimated arrival (UTC) |\n| `transit_platform` | Platform/track number |\n| `transit_gate` | Gate number (flights) |\n| `transit_terminal` | Terminal (flights) |\n| `transit_vehicle_id` | Vehicle identifier |\n| `transit_source` | Data source (amtrak, mta, aviationstack) |\n| `transit_stops` | JSON array of stops with coordinates and times |\n\nTags:\n- **Mode**: `flight`, `train`, `bus`, `tram`, `metro`\n- **Status**: `status:scheduled`, `status:delayed`, `status:departed`, `status:canceled`\n\nPosts:\n- **Post #1**: Basic departure info (created with topic)\n- **Post #2**: Complete schedule table (for multi-stop trips)\n- **Post #3+**: Status updates, delay notifications\n\n## How It Works\n\n### Data Flow\n\n1. **Data Import/Polling**:\n   - Amtrak: One-time rake task downloads GTFS ZIP, parses CSVs\n   - Golemio/AviationStack: Scheduled job polls APIs every N minutes\n\n2. **Topic Creation/Update**:\n   - Finds or creates topic using natural key (trip_id + service_date)\n   - Updates custom fields with latest timing/route data\n   - Creates schedule post (Post #2) with stop times table\n\n3. **Status Detection**:\n   - Compares scheduled vs estimated times\n   - Detects delays (threshold configurable, default 2 minutes)\n   - Updates tags: `status:scheduled` → `status:delayed` → `status:departed`\n\n4. **Frontend Display**:\n   - Board fetches `/transit/board` every 30 seconds\n   - Renders rows with time, route, destination, gate, status, countdown\n   - Client-side countdown updates every second (no API calls)\n   - Click to expand → fetches and displays topic posts inline\n\n### Code-Share Detection (Flights)\n\nMultiple airlines can sell seats on the same physical flight. AviationStack provides a `codeshared` field identifying the operating carrier. The plugin uses the operating carrier's flight number as the natural key (trip_id), so marketing carriers automatically merge into the same topic.\n\nExample: American operates flight AA 1234, but British Airways sells it as BA 5678 and Iberia as IB 789. All three point to the same trip_id, creating one topic with merged route codes: `AA 1234 / BA 5678 / IB 789`\n\n### GTFS Parsing (Amtrak)\n\nThe `AmtrakGtfsService`:\n1. Downloads `GTFS.zip` from Amtrak's CDN\n2. Extracts and parses:\n   - `routes.txt` - Route names and types\n   - `stops.txt` - Station names, codes, coordinates\n   - `trips.txt` - Trip IDs, routes, headsigns\n   - `stop_times.txt` - Stop sequences and times\n3. For each trip in the next 24 hours:\n   - Creates topic with all timing/route data\n   - Stores detailed stops array with lat/lon\n   - Creates schedule post with markdown table\n\nEdge cases handled:\n- GTFS times can exceed 24 hours (e.g., \"25:30:00\" = 1:30 AM next day)\n- Stop sequences aren't always sequential\n- All times converted to UTC\n\n### GTFS Parsing (MTA)\n\nThe `MtaGtfsService` follows the same pattern as Amtrak but handles a much larger dataset:\n\n1. Downloads `google_transit.zip` from MTA's developer portal\n2. Parses 500k+ stop_times rows (all trains, all stops, weeks ahead)\n3. **6-hour import window** instead of 24 hours to avoid memory issues\n4. Extracts `route_color` field for authentic MTA line colors\n5. Creates ~5,000 departure topics with schedules\n\nPerformance optimizations for metro filtering:\n- Per-route query limiting: Load 5 departures per route (~125 topics total)\n- Smart refresh: Updates changed items in place, no full page reload\n- Lazy post loading: Schedule rendered from stops JSON, not database posts\n\n## Rake Tasks\n\n```bash\n# Setup: Create categories, tags, update settings\nrake transit_tracker:setup\n\n# Import Amtrak GTFS data\nrake transit_tracker:import_amtrak\n\n# Import NYC MTA Subway GTFS data\nrake transit_tracker:import_mta\n\n# Clean up old departed topics (optional)\nrake transit_tracker:cleanup\n\n# Reprocess existing topics to merge duplicates\nrake transit_tracker:reprocess\n```\n\n## Development\n\n### Running locally\n\n```bash\n# Start Discourse dev server\nbin/ember-cli\nbin/rails server\n\n# Test Amtrak import\nRAILS_ENV=development bundle exec rake transit_tracker:import_amtrak\n\n# Test MTA import (creates ~5,000 topics)\nRAILS_ENV=development bundle exec rake transit_tracker:import_mta\n\n# Check board endpoint\ncurl http://localhost:3000/transit/board\n\n# View board UI\nopen http://localhost:3000/board\n```\n\n### Testing services in Rails console\n\n```ruby\n# Test Amtrak GTFS\namtrak = AmtrakGtfsService.new\nstats = amtrak.import\nputs stats.inspect\n\n# Test MTA GTFS\nmta = MtaGtfsService.new\nstats = mta.import\nputs stats.inspect\n\n# Test AviationStack\naviation = AviationstackService.new\nflights = aviation.fetch_departures\nputs flights.inspect\n```\n\n## Architecture Highlights\n\n### Why Posts for Schedules?\n\nUsing Discourse posts instead of custom data structures provides:\n- **Update history built-in** - Each delay/change is a new post\n- **Moderation tools work** - Edit/delete bad data using Discourse UI\n- **No additional tables** - Posts are just posts, Discourse handles storage\n- **Comments possible** - Could allow users to reply to departures (infrastructure exists)\n\n### CSS Grid with `display: contents`\n\nThe departure board uses a clever CSS Grid trick:\n- `.transit-board` is a single grid with 6 columns\n- `.board-row` uses `display: contents` to become transparent\n- All cells become direct grid children\n- Route column uses `auto` width → automatically sizes to widest badge across ALL rows\n- No JavaScript needed for column width synchronization\n\n## Use Cases Beyond Transit\n\nThis pattern (topics as structured time-sensitive data + posts as updates) could work for:\n- **Package tracking** - Topics = packages, posts = scan events\n- **Event schedules** - Topics = sessions, posts = time/room changes\n- **Server status boards** - Topics = servers, posts = incidents\n- **Release calendars** - Topics = releases, posts = changelogs\n- **Deployment pipelines** - Topics = deploys, posts = stage completions\n- **Support ticket boards** - Topics = tickets, expandable = full history\n\n## Contributing\n\nThis is an experimental plugin built to explore Discourse's flexibility. Contributions welcome, especially:\n- Additional transit API integrations\n- Map UI showing vehicle positions\n- Historical analytics and delay patterns\n- Mobile app or widget versions\n- Documentation improvements\n\n## License\n\nGPLv2 (same as Discourse)\n\n## Acknowledgments\n\nBuilt to learn Discourse plugin development by pushing the platform in an unconventional direction. Special thanks to the Discourse team for creating such a flexible and well-architected system.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fducks%2Fdiscourse-transit-tracker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fducks%2Fdiscourse-transit-tracker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fducks%2Fdiscourse-transit-tracker/lists"}