fix(edu): read diary event times directly from DOM, drop 25 AJAX clicks
ci / lint-prettier (push) Successful in 9s
ci / lint-ruff (push) Successful in 6s
ci / lint-yaml (push) Successful in 10s
ci / lint-dockerfiles (push) Successful in 7s
ci / validate (push) Successful in 6s
ci / build (push) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
ci / lint-prettier (push) Successful in 9s
ci / lint-ruff (push) Successful in 6s
ci / lint-yaml (push) Successful in 10s
ci / lint-dockerfiles (push) Successful in 7s
ci / validate (push) Successful in 6s
ci / build (push) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
_collect_event_times re-clicked every a.event-link and waited ~3s per event for a visible span.data, but the calendar embeds all times in div.event-full-info[data-event-full-info-id] span.data already. The old loop took ~109s for 25 events and collected 0 (original divs stay sf-hidden), effectively hanging /diary. Now a single evaluate reads all times (~3.7s), parsing HH:MM from p.date span.data.
This commit is contained in:
@@ -555,69 +555,40 @@ def _parse_calendar_html(table_html: str) -> tuple:
|
||||
|
||||
|
||||
async def _collect_event_times(page) -> dict:
|
||||
"""Click every calendar `a.event-link` and read the time from the AJAX popup.
|
||||
"""Read event times straight from the rendered calendar DOM.
|
||||
|
||||
The site shows event details only after a click fills the fancybox container
|
||||
(`div.event-full-info span.data`). Returns {event_id: 'HH:MM'} for events
|
||||
that have a date; events without one are simply skipped. A failed click on a
|
||||
single event never breaks the whole diary parse.
|
||||
The diary page embeds `div.event-full-info[data-event-full-info-id]`
|
||||
containing `span.data` (e.g. "2026-09-02 16:30:00") for every event, so no
|
||||
AJAX popup clicks are needed. Returns {event_id: 'HH:MM'} for events that
|
||||
have a date; events without one are simply skipped.
|
||||
"""
|
||||
times_by_id: dict[str, str] = {}
|
||||
try:
|
||||
links = await page.query_selector_all('a.event-link')
|
||||
raw_times = await page.evaluate(
|
||||
"""() => {
|
||||
const out = {};
|
||||
for (const div of document.querySelectorAll(
|
||||
'div.event-full-info[data-event-full-info-id]'
|
||||
)) {
|
||||
const id = div.getAttribute('data-event-full-info-id');
|
||||
const date_span = div.querySelector('p.date span.data');
|
||||
if (id && date_span) {
|
||||
out[id] = date_span.textContent.trim();
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}"""
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f'Failed to list diary event links: {e}')
|
||||
logger.warning(f'Failed to read diary event times from DOM: {e}')
|
||||
return times_by_id
|
||||
|
||||
for link in links:
|
||||
event_id = None
|
||||
event_deadline = time.monotonic() + 10
|
||||
try:
|
||||
event_id = await link.get_attribute('data-event-id')
|
||||
if not event_id or event_id in times_by_id:
|
||||
continue
|
||||
await link.evaluate('(el) => el.click()')
|
||||
await page.wait_for_selector(
|
||||
f'div.event-full-info[data-event-full-info-id="{event_id}"] span.data',
|
||||
timeout=3000,
|
||||
state='visible',
|
||||
)
|
||||
if time.monotonic() > event_deadline:
|
||||
break
|
||||
time_text = await page.evaluate(
|
||||
"""(id) => {
|
||||
const nodes = document.querySelectorAll(
|
||||
'div.event-full-info[data-event-full-info-id="' + id + '"] span.data'
|
||||
);
|
||||
for (const el of nodes) {
|
||||
if (el.getClientRects().length > 0 || el.offsetParent !== null) {
|
||||
return el.textContent.trim();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}""",
|
||||
event_id,
|
||||
)
|
||||
if time_text:
|
||||
m = re.search(r'(\d{1,2}:\d{2})', time_text)
|
||||
if m:
|
||||
times_by_id[event_id] = m.group(1)
|
||||
except Exception as e:
|
||||
logger.debug(f'Failed to read time for diary event {event_id}: {e}')
|
||||
if time.monotonic() > event_deadline:
|
||||
break
|
||||
finally:
|
||||
# Close the fancybox popup (click the close button, else Escape).
|
||||
closed = False
|
||||
with contextlib.suppress(Exception):
|
||||
close_btn = await page.query_selector('#fancybox-close')
|
||||
if close_btn is not None:
|
||||
await close_btn.click(timeout=2000)
|
||||
closed = True
|
||||
if not closed:
|
||||
with contextlib.suppress(Exception):
|
||||
await page.keyboard.press('Escape')
|
||||
await page.wait_for_timeout(150)
|
||||
for event_id, time_text in (raw_times or {}).items():
|
||||
if not time_text:
|
||||
continue
|
||||
m = re.search(r'(\d{1,2}:\d{2})', time_text)
|
||||
if m:
|
||||
times_by_id[event_id] = m.group(1)
|
||||
|
||||
logger.info(f'Diary event times collected for {len(times_by_id)} events')
|
||||
return times_by_id
|
||||
|
||||
Reference in New Issue
Block a user