notebookvb

This commit is contained in:
Vladimir Buzalka
2026-05-09 08:29:35 +02:00
parent 5bfd4176e4
commit 77d12c68d7
28 changed files with 3408 additions and 0 deletions
@@ -0,0 +1,111 @@
"""
Jednorázový import: stáhne všechny Str8ts z gameLevels (celý rok) a uloží do MySQL.
Přeskočí záznamy, které už existují (INSERT IGNORE).
"""
import asyncio
import json
import sys
from datetime import date
from pathlib import Path
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "Knihovny"))
from mysql_db import connect_mysql
from playwright.async_api import async_playwright
URL = "https://www.solitaire.org/daily-str8ts/"
YEAR = date.today().year
DIFFICULTIES = ["easy", "medium", "hard"]
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)
data = await game_page.evaluate("""() => {
const result = {};
for (const diff of ['easy', 'medium', 'hard']) {
if (gameLevels[diff]) {
result[diff] = gameLevels[diff];
}
}
return result;
}""")
await browser.close()
return data
def save_all(data: dict, year: int) -> tuple[int, int]:
conn = connect_mysql(database="puzzle")
cur = conn.cursor()
inserted = 0
skipped = 0
# Všechny MM-DD klíče z libovolné obtížnosti
all_mmdd = set()
for diff_data in data.values():
all_mmdd.update(diff_data.keys())
for mmdd in sorted(all_mmdd):
puzzle_date = f"{year}-{mmdd}"
for diff in DIFFICULTIES:
entry = data.get(diff, {}).get(mmdd)
if not entry:
continue
cur.execute(
"INSERT IGNORE INTO puzzles "
"(game_type, difficulty, puzzle_date, puzzle, solution, extra, source) "
"VALUES (%s, %s, %s, %s, %s, %s, %s)",
(
"str8ts", diff, puzzle_date,
entry["puzzle"], entry["solution"],
json.dumps({"bw": entry["bw"]}),
"solitaire.org",
),
)
if cur.rowcount:
inserted += 1
else:
skipped += 1
conn.commit()
cur.close()
conn.close()
return inserted, skipped
async def main():
data = await fetch_all_levels()
total_days = len(set(k for d in data.values() for k in d.keys()))
print(f"gameLevels obsahuje {total_days} dní, {len(data)} obtížností")
inserted, skipped = save_all(data, YEAR)
print(f"Hotovo — vloženo: {inserted}, přeskočeno (existující): {skipped}")
if __name__ == "__main__":
asyncio.run(main())