97 lines
3.0 KiB
Python
97 lines
3.0 KiB
Python
"""
|
|
Stáhne daily Calcudoku puzzle data ze solitaire.org a uloží do MySQL.
|
|
"""
|
|
|
|
import asyncio
|
|
import json
|
|
import sys
|
|
|
|
sys.stdout.reconfigure(encoding="utf-8")
|
|
sys.stderr.reconfigure(encoding="utf-8")
|
|
|
|
from datetime import date, timedelta
|
|
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-calcudoku/"
|
|
DIFFICULTIES = ["4x4", "5x5", "6x6", "8x8"]
|
|
|
|
|
|
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 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
|
|
cages, solution = game_levels[diff][mmdd]
|
|
grid_size = int(diff.split("x")[0])
|
|
cur.execute(
|
|
"INSERT IGNORE INTO puzzles (game_type, difficulty, puzzle_date, puzzle, solution, extra, source) "
|
|
"VALUES (%s, %s, %s, %s, %s, %s, %s)",
|
|
("calcudoku", diff, date_str, cages, solution,
|
|
json.dumps({"grid_size": grid_size}), "solitaire.org"),
|
|
)
|
|
if cur.rowcount > 0:
|
|
inserted += 1
|
|
d += timedelta(days=1)
|
|
|
|
cur.close()
|
|
conn.close()
|
|
return inserted
|
|
|
|
|
|
async def main():
|
|
game_levels = await fetch_all_levels()
|
|
total = sum(len(game_levels.get(d, {})) for d in DIFFICULTIES)
|
|
print(f"gameLevels: {total} 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())
|