Skip to content

Instantly share code, notes, and snippets.

@jamesholcomb
Forked from desrod/build-cse-table.py
Created November 7, 2022 13:36
Show Gist options
  • Save jamesholcomb/c6fd593a33a8c62a3b8cad6f7a173395 to your computer and use it in GitHub Desktop.
Save jamesholcomb/c6fd593a33a8c62a3b8cad6f7a173395 to your computer and use it in GitHub Desktop.
Query Zwift public, upcoming events and build an HTML table of those events (in Python)
There are two parts to this:
1. The main Python code that uses requests + json to parse the events
2. The external Jinja2 template that the data is rendered by, producing the HTML output
# ----------------------------------------------------------------------------------------------------------------
#!/usr/bin/env python3
import json
import pendulum
import requests
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader("."))
event_tpl = env.get_template("events.tmpl")
local_tz = "EST"
event_url = "https://us-or-rly101.zwift.com/api/public/events/upcoming"
###############################################################
def fetch_json(event_url):
now = pendulum.now().to_rfc3339_string()
dt = pendulum.parse(now)
# Only fetch the upstream content once every 8 hours
# May need to fetch more frequently, if event data
# changes more often
if dt.hour % 6 == 0:
print("Fetching new events from upstream API endpoint...")
headers = {
"User-Agent": "Public Events Fetcher",
"From": "[email protected]",
"Content-Type": "application/json",
"Connection": "keep-alive",
}
response = requests.get(event_url, headers=headers)
# DEBUG: Print out headers, which indicate compression, gzip encoding
# print(response.request.headers)
return json.loads(response.text)
else:
print("Skipping upstream fetch, too soon for new events..")
###############################################################
def render_events(events):
for event in events:
event_start = pendulum.parse(event["eventStart"]).in_timezone(local_tz)
event["eventStart"] = event_start.strftime("%F %X%z (%Z) %A")
event["eventType"] = event["eventType"].replace("_", " ").title()
output = event_tpl.render(events=events)
# Write the output after rendeirng to 'events.html'
with open("events.html", "w") as html:
html.write(output)
###############################################################
def main():
events = fetch_json(event_url)
render_events(events)
# Save the incoming events as events.json for offline
# testing and profiling, if needed, else comment out
with open("events.json", "w") as f:
f.write(json.dumps(events))
if __name__ == "__main__":
main()
# ----------------------------------------------------------------------------------------------------------------
<!DOCTYPE html>
<html lang="en">
<head>
<title>Visible to Non-Participants</title>
<style>
table {font-family: "Courier New", monospace; font-size:0.8em; border-collapse: collapse;}
table, th, tr, td {border: 1px solid #ccc; padding:0.3em;}
th {text-align: center;} td {text-align:right;}
td.l {text-align:left;}
tr:nth-child(even) {background: #eee;}
</style>
</head>
<body>
<table>
<thead>
<tr>
<th>Kilometers</th>
<th>Minutes</th>
<th>Laps</th>
<th>Activity</th>
<th>Start time</th>
<th>Link</th></tr>
</thead>
<tbody>
{%- for event in events -%}
{%- if event['invisibleToNonParticipants'] == False %}
{%- if event['eventType'] == 'Group Ride' or (event['sport'] == 'Running' and event['eventType'] == 'Group Workout') %}
<tr>
<td>{{ event['distanceInMeters'] / 1000 }}</td>
<td>{{ event['durationInSeconds'] / 60 }}</td>
<td>{{ event['laps'] }}</td>
{%- if 'CYCLING' in event['sport'] %}
<td class="l">&#128690; Cycling, {{ event['eventType'].title() }}</td>
{%- elif 'RUNNING' in event['sport'] %}
{%- if 'Ride' in event['eventType'] %}
<td class="l">&#127939; Running, Group Run</td>
{%- else %}
<td class="l">&#127939; Running, Group Workout</td>
{%- endif %}
{%- endif %}
<td>{{ event['eventStart'] }}</td>
<td class="l"><a href="https://zwift.com/events/view/{{ event['id'] }}">{{ event['name'] }}</a></td>
</tr>
{%- endif %}
{%- endif %}
{%- endfor %}
</tbody>
</table>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment