Last active
June 24, 2021 21:20
-
-
Save martenlienen/0fa30800c14c03478eb1d919688bfbd3 to your computer and use it in GitHub Desktop.
Extract CF Munich's schedule from their website into an excel sheet. The userscript extracts the data from their eversports widget into JSON and the python script formats that data into an excel sheet.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import argparse | |
import json | |
from pathlib import Path | |
from openpyxl import Workbook | |
from openpyxl.styles import Alignment, Font | |
WEEKDAYS = [ | |
"Montag", | |
"Dienstag", | |
"Mittwoch", | |
"Donnerstag", | |
"Freitag", | |
"Samstag", | |
"Sonntag", | |
] | |
def minutes_to_time(minutes): | |
hours = minutes // 60 | |
mins = minutes % 60 | |
return f"{hours:02d}:{mins:02d}" | |
def class_category(class_): | |
name = class_["name"].lower() | |
if "bodybuilding" in name: | |
return "Functional Bodybuilding" | |
elif "kids" in name: | |
return "CrossFit Kids" | |
elif "crossfit" in name: | |
return "CrossFit" | |
elif "gymnastics" in name: | |
return "Gymnastics" | |
elif "open" in name: | |
return "Open Gym" | |
elif "olympic" in name: | |
return "Weightlifting" | |
elif "strongman" in name: | |
return "Strongman" | |
elif "hyrox" in name: | |
return "Hyrox" | |
elif "mobility" in name: | |
return "Mobility" | |
elif "yoga" in name: | |
return "Yoga" | |
elif "endurance" in name: | |
if "advanced" in name: | |
return "Endurance Advanced" | |
else: | |
return "Endurance" | |
elif "rookie" in name: | |
return "Rookie" | |
else: | |
return name | |
def main(): | |
parser = argparse.ArgumentParser() | |
parser.add_argument("schedule", help="Exported JSON schedule") | |
parser.add_argument("sheet", help="Schedule excel sheet") | |
args = parser.parse_args() | |
schedule_path = Path(args.schedule) | |
sheet_path = Path(args.sheet) | |
schedule_data = json.loads(schedule_path.read_text()) | |
processed = [ | |
{ | |
start: sorted(list(set(class_category(cls) for cls in classes))) | |
for start, classes in day.items() | |
} | |
for day in schedule_data | |
] | |
earliest_time = min(min(map(int, day.keys())) for day in schedule_data) | |
latest_time = max(max(map(int, day.keys())) for day in schedule_data) | |
wb = Workbook() | |
ws = wb.active | |
ws.title = "CFM Schedule" | |
for time_idx, start in enumerate(range(earliest_time, latest_time + 1, 30)): | |
ws.cell(2 + time_idx, 1, minutes_to_time(start)) | |
start_col = 2 | |
for day_idx, day in enumerate(processed): | |
weekday = WEEKDAYS[day_idx] | |
max_parallel_slots = max(len(v) for v in day.values()) | |
end_col = start_col + max_parallel_slots - 1 | |
cell = ws.cell(1, start_col) | |
cell.value = weekday | |
cell.font = cell.font + Font(bold=True) | |
cell.alignment = cell.alignment + Alignment(horizontal="center") | |
ws.merge_cells( | |
start_row=1, end_row=1, start_column=start_col, end_column=end_col | |
) | |
for start, classes in day.items(): | |
row = (int(start) - earliest_time) // 30 + 2 | |
for cls_idx, name in enumerate(classes): | |
ws.cell(row, start_col + cls_idx, name) | |
start_col = end_col + 1 | |
wb.save(sheet_path) | |
if __name__ == "__main__": | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name CFM schedule extract | |
// @version 1 | |
// @include https://www.eversports.de/widget/w/A9ATBg | |
// @grant none | |
// ==/UserScript== | |
(function() { | |
"use strict"; | |
let schedule_root = document.querySelector(".calendar"); | |
let rows = schedule_root.querySelectorAll(".row.calendar__container"); | |
let schedule = [{}, {}, {}, {}, {}, {}, {}]; | |
for (const row of Array.from(rows)) { | |
let days = row.querySelectorAll(".calendar__day"); | |
for (const [index, day] of Array.from(days).entries()) { | |
let slots = day.querySelectorAll(".calendar__slot"); | |
for (const slot of Array.from(slots)) { | |
let name = slot.querySelector(".session-name").textContent; | |
let [start, duration] = slot.querySelector(".session-time").textContent.split(" ● "); | |
let [hour, min] = start.split(":"); | |
start = 60 * parseInt(hour) + parseInt(min); | |
duration = parseInt(duration.split(" ")[0]); | |
if (!schedule[index].hasOwnProperty(start)) { | |
schedule[index][start] = []; | |
} | |
schedule[index][start].push({name, start, duration}); | |
} | |
} | |
} | |
console.log(JSON.stringify(schedule)); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment