notebookvb
This commit is contained in:
@@ -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())
|
||||
Reference in New Issue
Block a user