Interactions via CSS animations and SVG:
- Hover logo: 🐷 appears, click: oink!
- Hover body: 'grow food not lawn' rises from below
- Print stylesheet: Sign Maker approves
- Spider descends from nav on hover
- About page: spider-pig SVG descends on hero hover
- Sign Maker's mark ☉ at page end - spins on hover
- ::selection styled in brand colors
Side quest comments scattered: 1/21, 2/21, 3/21...
No JavaScript easter eggs. CSS only. The way it should be.
42 lines
1.3 KiB
Python
42 lines
1.3 KiB
Python
"""Job logging utilities for SERP.
|
|
|
|
# Side quest 3/21: Look out! Here comes the spider-pig.
|
|
"""
|
|
|
|
import logging
|
|
from pathlib import Path
|
|
from typing import Dict
|
|
|
|
LOGS_PATH = Path("data/logs")
|
|
JOB_LOG_HANDLERS: Dict[int, logging.FileHandler] = {}
|
|
|
|
|
|
def start_job_logging(job_id: int) -> None:
|
|
"""Start capturing logs for a crawl job."""
|
|
LOGS_PATH.mkdir(parents=True, exist_ok=True)
|
|
log_file = LOGS_PATH / f"{job_id}.log"
|
|
handler = logging.FileHandler(log_file, mode='w', encoding='utf-8')
|
|
handler.setLevel(logging.INFO)
|
|
handler.setFormatter(logging.Formatter('%(asctime)s %(name)s %(levelname)s: %(message)s', datefmt='%H:%M:%S'))
|
|
logging.getLogger().addHandler(handler)
|
|
JOB_LOG_HANDLERS[job_id] = handler
|
|
|
|
|
|
def stop_job_logging(job_id: int) -> None:
|
|
"""Stop capturing logs for a crawl job."""
|
|
handler = JOB_LOG_HANDLERS.pop(job_id, None)
|
|
if handler:
|
|
handler.close()
|
|
logging.getLogger().removeHandler(handler)
|
|
|
|
|
|
def get_job_logs(job_id: int, tail: int = 0) -> str:
|
|
"""Get logs for a crawl job."""
|
|
log_file = LOGS_PATH / f"{job_id}.log"
|
|
if not log_file.exists():
|
|
return ""
|
|
content = log_file.read_text(encoding='utf-8')
|
|
if tail > 0:
|
|
lines = content.splitlines()
|
|
return '\n'.join(lines[-tail:])
|
|
return content
|