""" Stáhne daily Kakuro puzzle data ze solitaire.org a uloží do MySQL. PDF generování bude doplněno později. """ import asyncio import json import sys sys.stdout.reconfigure(encoding="utf-8") sys.stderr.reconfigure(encoding="utf-8") from datetime import date from pathlib import Path from playwright.async_api import async_playwright sys.path.insert(0, str(Path(__file__).parent.parent.parent / "Knihovny")) from mysql_db import connect_mysql URL = "https://www.solitaire.org/daily-kakuro/" DIFFICULTIES = ["easy", "medium", "hard", "expert"] async def fetch_all_levels() -> dict: async with async_playwright() as p: browser = await p.chromium.launch(headless=True) context = await browser.new_context(viewport={"width": 1280, "height": 900}) page = await context.new_page() print(f"Načítám {URL} ...") await page.goto(URL, wait_until="networkidle", timeout=60_000) game_url = None for frame in page.frames: if frame.url != page.url and frame.url.strip() not in ("", "about:blank"): game_url = frame.url break if not game_url: iframe_src = await page.get_attribute("iframe", "src") if iframe_src: game_url = iframe_src if iframe_src.startswith("http") else f"https://www.solitaire.org{iframe_src}" await page.close() game_page = await context.new_page() target_url = game_url if game_url else URL print(f"Načítám hru: {target_url} ...") await game_page.goto(target_url, wait_until="networkidle", timeout=60_000) raw = await game_page.evaluate("() => JSON.stringify(gameLevels)") await browser.close() return json.loads(raw) def grid_size(data_str: str) -> int: """Vrátí rozměr mřížky z raw dat (počet sloupců prvního řádku + 1 pro prepended 0).""" first_row = data_str.split(",")[0] return len(first_row) + 1 def save_to_mysql(game_levels: dict, start_date: date, end_date: date): conn = connect_mysql(database="puzzle") cur = conn.cursor() inserted = 0 d = start_date while d <= end_date: mmdd = d.strftime("%m-%d") date_str = d.strftime("%Y-%m-%d") for diff in DIFFICULTIES: if diff not in game_levels or mmdd not in game_levels[diff]: continue data_str = game_levels[diff][mmdd] size = grid_size(data_str) cur.execute( "INSERT IGNORE INTO puzzles (game_type, difficulty, puzzle_date, puzzle, solution, extra, source) " "VALUES (%s, %s, %s, %s, %s, %s, %s)", ("kakuro", diff, date_str, data_str, data_str, json.dumps({"grid_size": size}), "solitaire.org"), ) if cur.rowcount > 0: inserted += 1 d += __import__("datetime").timedelta(days=1) cur.close() conn.close() return inserted async def main(): today = date.today() game_levels = await fetch_all_levels() print(f"gameLevels: {sum(len(game_levels.get(d, {})) for d in DIFFICULTIES)} záznamů") inserted = save_to_mysql(game_levels, date(2026, 1, 1), date(2026, 12, 31)) print(f"MySQL: vloženo {inserted} nových řádků (celý rok 2026)") if __name__ == "__main__": asyncio.run(main())