feat: Add highlighing for upcoming week
All checks were successful
Build Docker / BuildImage (push) Successful in 1m44s

This commit is contained in:
2026-04-01 12:38:12 +11:00
parent 659bc5940e
commit 1e990fcb0c
3 changed files with 123 additions and 4 deletions

View File

@@ -12,6 +12,7 @@ import requests
import dotenv import dotenv
import csv import csv
from io import StringIO from io import StringIO
from datetime import date, datetime
dotenv.load_dotenv() dotenv.load_dotenv()
@@ -86,6 +87,69 @@ SCHEDULE_DATA = load_schedule_data()
app = Flask(__name__) app = Flask(__name__)
def parse_schedule_date(raw_date: str) -> date | None:
"""Parse a schedule date string using common formats."""
if not raw_date:
return None
cleaned = raw_date.strip()
# Try ISO date first.
try:
return date.fromisoformat(cleaned)
except ValueError:
pass
formats = [
"%d/%m/%Y",
"%m/%d/%Y",
"%d/%m/%y",
"%m/%d/%y",
"%d-%m-%Y",
"%m-%d-%Y",
"%d %b %Y",
"%d %B %Y",
"%b %d, %Y",
"%B %d, %Y",
"%a %d %b %Y",
"%A %d %B %Y",
]
for fmt in formats:
try:
return datetime.strptime(cleaned, fmt).date()
except ValueError:
continue
month_day_formats = [
"%B %d",
"%b %d",
"%B %d,",
"%b %d,",
]
current_year = date.today().year
for fmt in month_day_formats:
try:
# Add current year to the end of the string for parsing
date_str_with_year = f"{cleaned} {current_year}"
return datetime.strptime(date_str_with_year, fmt + " %Y").date()
except ValueError:
continue
return None
def find_upcoming_week_index(schedule: list[dict]) -> int | None:
"""Return the index of the next schedule item that is today or later."""
today = date.today()
for index, item in enumerate(schedule):
parsed_date = parse_schedule_date(item.get("date", ""))
if parsed_date and parsed_date >= today:
return index
return None
def find(name, path): def find(name, path):
for root, dirs, files in os.walk(path): for root, dirs, files in os.walk(path):
if name in files: if name in files:
@@ -140,7 +204,12 @@ def wellknown(path):
# region Main routes # region Main routes
@app.route("/") @app.route("/")
def index(): def index():
return render_template("schedule.html", schedule=SCHEDULE_DATA) upcoming_week_index = find_upcoming_week_index(SCHEDULE_DATA)
return render_template(
"schedule.html",
schedule=SCHEDULE_DATA,
upcoming_week_index=upcoming_week_index,
)
@app.route("/<path:path>") @app.route("/<path:path>")
@@ -196,4 +265,4 @@ def not_found(e):
# endregion # endregion
if __name__ == "__main__": if __name__ == "__main__":
app.run(debug=True, port=5000, host="0.0.0.0") app.run(debug=True, port=5000, host="127.0.0.1")

View File

@@ -16,6 +16,9 @@
--empty-leader-color: #666; --empty-leader-color: #666;
--select-bg: rgba(50, 50, 50, 0.8); --select-bg: rgba(50, 50, 50, 0.8);
--select-border: #666; --select-border: #666;
--upcoming-accent: #7dd3fc;
--upcoming-row-bg: rgba(125, 211, 252, 0.14);
--upcoming-glow: rgba(125, 211, 252, 0.28);
} }
[data-theme="pink"] { [data-theme="pink"] {
@@ -34,6 +37,9 @@
--empty-leader-color: #996677; --empty-leader-color: #996677;
--select-bg: rgba(80, 40, 60, 0.8); --select-bg: rgba(80, 40, 60, 0.8);
--select-border: #a55577; --select-border: #a55577;
--upcoming-accent: #f9a8d4;
--upcoming-row-bg: rgba(249, 168, 212, 0.16);
--upcoming-glow: rgba(249, 168, 212, 0.32);
} }
body { body {
@@ -207,6 +213,36 @@ h1 {
background-color: var(--special-event-hover); background-color: var(--special-event-hover);
} }
.upcoming-week {
background: linear-gradient(90deg, var(--upcoming-row-bg) 0%, transparent 72%);
box-shadow: inset 4px 0 0 var(--upcoming-accent), inset 0 0 0 1px var(--upcoming-glow);
}
.upcoming-week:hover {
background: linear-gradient(90deg, var(--upcoming-row-bg) 0%, var(--row-hover-bg) 72%);
}
.upcoming-date {
position: relative;
padding-left: 12px;
font-weight: 700;
}
.upcoming-date::before {
content: none;
}
.upcoming-date::after {
content: "next up";
display: block;
margin-top: 2px;
font-size: 10px;
line-height: 1;
letter-spacing: 1.2px;
text-transform: uppercase;
color: var(--upcoming-accent);
}
.date-cell { .date-cell {
font-weight: 600; font-weight: 600;
color: var(--date-color); color: var(--date-color);
@@ -330,6 +366,15 @@ h1 {
font-weight: 700; font-weight: 700;
} }
.upcoming-date {
padding-left: 8px;
}
.upcoming-date::after {
font-size: 9px;
letter-spacing: 1px;
}
.leader-cell { .leader-cell {
width: 30%; width: 30%;
font-size: 12px; font-size: 12px;
@@ -378,6 +423,11 @@ h1 {
.date-cell { .date-cell {
width: 30%; width: 30%;
font-size: 14px; font-size: 14px;
padding-left: 10px !important;
}
.upcoming-date::after {
display: none;
} }
.topic-cell { .topic-cell {

View File

@@ -43,8 +43,8 @@
</thead> </thead>
<tbody> <tbody>
{% for item in schedule %} {% for item in schedule %}
<tr class="{% if not item.leaders %}special-event{% endif %}"> <tr class="{% if not item.leaders %}special-event{% endif %} {% if upcoming_week_index is not none and loop.index0 == upcoming_week_index %}upcoming-week{% endif %}">
<td class="date-cell">{{ item.date }}</td> <td class="date-cell{% if upcoming_week_index is not none and loop.index0 == upcoming_week_index %} upcoming-date{% endif %}">{{ item.date }}</td>
<td class="leader-cell">{% if item.leaders %}{% if item.leaders|length > 1 %}{{ item.leaders[:-1]|join(', ') }} & {{ item.leaders[-1] }}{% else %}{{ item.leaders[0] }}{% endif %}{% endif %}</td> <td class="leader-cell">{% if item.leaders %}{% if item.leaders|length > 1 %}{{ item.leaders[:-1]|join(', ') }} & {{ item.leaders[-1] }}{% else %}{{ item.leaders[0] }}{% endif %}{% endif %}</td>
<td class="topic-cell" {% if item.leaders %}data-leaders="Leaders: {% if item.leaders|length > 1 %}{{ item.leaders[:-1]|join(', ') }} & {{ item.leaders[-1] }}{% else %}{{ item.leaders[0] }}{% endif %}"{% endif %}>{{ item.topic }}</td> <td class="topic-cell" {% if item.leaders %}data-leaders="Leaders: {% if item.leaders|length > 1 %}{{ item.leaders[:-1]|join(', ') }} & {{ item.leaders[-1] }}{% else %}{{ item.leaders[0] }}{% endif %}"{% endif %}>{{ item.topic }}</td>
</tr> </tr>