Skip to content

Commit 5262f71

Browse files
authored
Merge pull request #19 from arekbauer/trmnl-vct
Add TRMNL service integration with API endpoints and models
2 parents b61faa5 + a4e401d commit 5262f71

10 files changed

Lines changed: 153 additions & 0 deletions

File tree

Personal_Portfolio/urls.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,18 @@
1717
from django.contrib import admin
1818
from django.urls import path
1919
from django.conf.urls.static import static
20+
from django.urls import path, include
2021
from django.conf import settings
2122
from portfolio import views
2223

2324
urlpatterns = [
25+
# Website URLs
2426
path('api/now-playing/', views.get_now_playing, name='now-playing'),
2527
path('secret-admin/', admin.site.urls),
2628
path('', views.home, name = 'home'),
29+
30+
# Include TRMNL service URLs
31+
path('api/trmnl/', include('trmnl_service.urls')),
2732
]
2833

2934
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

trmnl_service/__init__.py

Whitespace-only changes.

trmnl_service/admin.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from django.contrib import admin
2+
3+
# Register your models here.

trmnl_service/apps.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.apps import AppConfig
2+
3+
4+
class TrmnlServiceConfig(AppConfig):
5+
default_auto_field = 'django.db.models.BigAutoField'
6+
name = 'trmnl_service'

trmnl_service/migrations/__init__.py

Whitespace-only changes.

trmnl_service/models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from django.db import models
2+
3+
# Create your models here.

trmnl_service/services.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import requests
2+
from datetime import datetime, timedelta
3+
from zoneinfo import ZoneInfo
4+
5+
class VLRService:
6+
MATCHES_URL = "https://vlr.orlandomm.net/api/v1/matches"
7+
RESULTS_URL = "https://vlr.orlandomm.net/api/v1/results"
8+
WHITELIST = "VCT 2026"
9+
LONDON_TZ = ZoneInfo("Europe/London")
10+
TIME_OFFSET_HOURS = 6
11+
12+
@classmethod
13+
def get_vct_dashboard_data(cls):
14+
"""Main entry point: Orchestrates the data flow."""
15+
now = datetime.now(cls.LONDON_TZ)
16+
17+
# 1. Fetch raw data
18+
raw_matches = cls._fetch_data(cls.MATCHES_URL)
19+
raw_results = cls._fetch_data(cls.RESULTS_URL, params={"page": "1"})
20+
21+
# 2. Process and Filter
22+
# We split these so you can debug 'Matches' and 'Results' independently.
23+
matches = cls._process_matches(raw_matches, now)
24+
results = cls._process_results(raw_results, now)
25+
26+
return {
27+
**results, # Contains y_res and t_res
28+
**matches, # Contains live, t_up, and tom_up
29+
"last_updated": now.strftime("%H:%M")
30+
}
31+
32+
# --- Private Logic: Processing ---
33+
34+
@classmethod
35+
def _process_matches(cls, raw_data, now):
36+
"""Handles LIVE and UPCOMING match logic."""
37+
buckets = {"live": [], "t_up": [], "tom_up": []}
38+
39+
for item in raw_data:
40+
if not cls._is_whitelisted(item): continue
41+
42+
match = cls._normalize_item(item)
43+
utc_date = item.get('utc', '')
44+
status = item.get('status', '').upper()
45+
46+
if cls._is_date(utc_date, now): # Today
47+
if status == "LIVE":
48+
buckets["live"].append(match)
49+
else:
50+
buckets["t_up"].append(match)
51+
elif cls._is_date(utc_date, now + timedelta(days=1)): # Tomorrow
52+
buckets["tom_up"].append(match)
53+
54+
return buckets
55+
56+
@classmethod
57+
def _process_results(cls, raw_data, now):
58+
"""Handles COMPLETED match logic using the 'ago' field."""
59+
buckets = {"y_res": [], "t_res": []}
60+
61+
for item in raw_data:
62+
if not cls._is_whitelisted(item): continue
63+
64+
match = cls._normalize_item(item)
65+
ago_str = item.get('ago', '') # Results use 'ago' instead of 'utc'
66+
67+
if 'd' not in ago_str: # Happened today
68+
buckets["t_res"].append(match)
69+
elif '1d' in ago_str: # Happened yesterday
70+
buckets["y_res"].append(match)
71+
72+
return buckets
73+
74+
# --- Private Logic: Helpers (The "Readable" Part) ---
75+
76+
@staticmethod
77+
def _fetch_data(url, params=None):
78+
"""Simple wrapper for requests to handle the API wrapper key."""
79+
try:
80+
resp = requests.get(url, params=params, timeout=10)
81+
return resp.json().get('data', [])
82+
except Exception:
83+
return []
84+
85+
@staticmethod
86+
def _is_whitelisted(item):
87+
"""Centralized check for your tournament whitelist."""
88+
return VLRService.WHITELIST in item.get('tournament', '')
89+
90+
@staticmethod
91+
def _is_date(utc_string, target_dt):
92+
"""Check if the API's UTC string matches our target date string."""
93+
return target_dt.strftime('%Y-%m-%d') in utc_string
94+
95+
@staticmethod
96+
def _normalize_item(data):
97+
"""Turns messy API objects into clean, display-ready dictionaries."""
98+
ts = data.get('timestamp')
99+
time_str = ""
100+
101+
if ts:
102+
corrected_ts = ts + (VLRService.TIME_OFFSET_HOURS * 3600)
103+
dt_utc = datetime.fromtimestamp(corrected_ts, tz=ZoneInfo("UTC"))
104+
dt_london = dt_utc.astimezone(VLRService.LONDON_TZ)
105+
time_str = dt_london.strftime("%H:%M")
106+
107+
return {
108+
"t1": data['teams'][0]['name'],
109+
"s1": data['teams'][0].get('score'),
110+
"t2": data['teams'][1]['name'],
111+
"s2": data['teams'][1].get('score'),
112+
"tournament": data.get('tournament'),
113+
"event": data.get('event'),
114+
"status": data.get('status'),
115+
"time": time_str
116+
}

trmnl_service/tests.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from django.test import TestCase
2+
3+
# Create your tests here.

trmnl_service/urls.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.urls import path
2+
from .views import vct_ticker_view
3+
4+
urlpatterns = [
5+
path('vct-ticker/', vct_ticker_view, name='trmnl_vct_ticker'),
6+
]

trmnl_service/views.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from django.http import JsonResponse
2+
from .services import VLRService
3+
4+
def vct_ticker_view(request):
5+
"""
6+
Endpoint for the TRMNL device to poll.
7+
"""
8+
data = VLRService.get_vct_dashboard_data()
9+
10+
status_code = 200 if "error" not in data else 502
11+
return JsonResponse(data, status=status_code)

0 commit comments

Comments
 (0)