diff --git a/CLAUDE.md b/CLAUDE.md index e3092c1..709873a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -50,3 +50,6 @@ Import vždy přes `sys.path` na kořen projektu nebo přímou cestou. |--------|---------|-------| | `stahni_str8ts.py` | `SběrDatRůzné/DailyStr8ts/` | Stahuje daily Str8ts puzzle jako PDF, odesílá emailem — viz [NOTES.md](SběrDatRůzné/DailyStr8ts/NOTES.md) | | `10_StahnoutXML.py`, `11_ParseXML.py` | `Recepty/NačteníPředpisuWithClaude/` | Pipeline pro stahování detailů receptů z eRecept SÚKL — viz [NacistPredpis_DOKUMENTACE.md](Recepty/NačteníPředpisuWithClaude/NacistPredpis_DOKUMENTACE.md) | +| `watcher.py` | `Webináře/` | Hlídá nové webináře na praktickylekar.online, přes Telegram potvrdí a přihlásí Buzalkovi — viz [NOTES.md](Webináře/NOTES.md) | +| `stahni_video.py` | `Video/` | Stahuje videa (Vimeo, YouTube…) přes yt-dlp; soukromá/nedostupná sám přeskočí — viz [NOTES.md](Video/NOTES.md) | +| `euni_stahni.py`, `euni_db.py`, `euni_report.py` | `Euni/` | Stahování kurzů z euni.cz (PDF + videa) s trackingem v MongoDB EUNI (idempotentní) — viz [NOTES.md](Euni/NOTES.md) | diff --git a/Euni/.env.example b/Euni/.env.example new file mode 100644 index 0000000..88bde04 --- /dev/null +++ b/Euni/.env.example @@ -0,0 +1,4 @@ +# Přihlašovací údaje k euni.cz — zkopíruj do souboru .env a vyplň. +# (.env je v .gitignore, do gitu se nedostane.) +EUNI_USERNAME=tvoje_prihlasovaci_jmeno +EUNI_PASSWORD=tvoje_heslo diff --git a/Euni/.gitignore b/Euni/.gitignore new file mode 100644 index 0000000..39f5f0e --- /dev/null +++ b/Euni/.gitignore @@ -0,0 +1,3 @@ +# stažený obsah a inventura — do gitu nepatří +stazeno/ +euni_kurzy.json diff --git a/Euni/NOTES.md b/Euni/NOTES.md new file mode 100644 index 0000000..7ef5072 --- /dev/null +++ b/Euni/NOTES.md @@ -0,0 +1,112 @@ +# Euni — stahování a tracking kurzů z euni.cz + +Přihlásí se na euni.cz, projde kurzy, vytěží odkazy + metadata a stahuje obsah +(PDF/prezentace a videa Vimeo/YouTube). Vše se trackuje v **MongoDB EUNI**, takže +stahování je idempotentní — skript ví, co už má, a netahá dvakrát. + +## Soubory + +| Soubor | Popis | +|--------|-------| +| `euni_stahni.py` | hlavní pipeline: login → scrape → ingest do Mongo → stahování → záloha do SeaweedFS | +| `euni_db.py` | připojení a operace nad MongoDB EUNI (kolekce, indexy, upserty) | +| `euni_seaweed.py` | nahrávání/stahování souborů do SeaweedFS (filer HTTP API) | +| `euni_restore.py` | obnova všech souborů ze SeaweedFS na disk (na jakémkoli PC) | +| `euni_report.py` | dashboard: přehled stavu (kolik staženo/čeká/přeskočeno) | +| `.env` | `EUNI_USERNAME`, `EUNI_PASSWORD` (v .gitignore) | +| `euni_kurzy.json` | poslední inventura (záloha; primární zdroj je Mongo) | +| `stazeno/` | stažený obsah, `stazeno/-/{dokumenty,videa}/` | + +## Závislosti + +```bat +python -m pip install -U requests beautifulsoup4 python-dotenv yt-dlp static-ffmpeg pymongo +``` + +Video stahuje sdílený modul `../Video/stahni_video.py` (yt-dlp + static-ffmpeg, +soukromá videa sám přeskočí). + +## MongoDB EUNI + +Server `mongodb://192.168.1.76:27017` (bez hesla), DB `EUNI`. Lze přepsat env +proměnnou `EUNI_MONGO_URI`. + +### Kolekce `kurzy` (1 dokument na kurz) +`_id` = euni ID kurzu. Pole: `slug, nazev, url, profese[], autor, +autor_medailonek_url, datum_publikace, revidovano, akreditace, kredity, +pocet_videi, pocet_dokumentu, first_seen, updated_at`. + +### Kolekce `materialy` (1 dokument na soubor) +Unikátní index `{kurz_id, klic}`. Pole: `kurz_id, kurz_nazev, druh +(video|dokument), platforma (vimeo|youtube), klic (vimeo:ID / youtube:ID / +doc:hash), zdroj_url, watch_url, popis, pripona, stav, duvod, soubor, +velikost_b, pokusy, posledni_chyba, first_seen, updated_at, stazeno_at`. + +**Stavy:** `ceka` → `stazeno` / `preskoceno` (soukromé video) / `chyba`. + +**SeaweedFS reference** (po nahrání kopie): `seaweed_path` (cesta ve filer = +identifikátor pro vyžádání, např. `euni/5618-.../dokumenty/x.pdf`), +`seaweed_fids` (fid chunků = čísla souborů v SeaweedFS), `seaweed_md5`, +`seaweed_size`, `seaweed_at`. + +## SeaweedFS záloha + obnova + +Každý stažený soubor se nahraje do **SeaweedFS** (filer na Unraidu, +default `http://192.168.1.50:8888`, přepíše env `EUNI_FILER`). Do Mongo se k +materiálu uloží `seaweed_path` + `seaweed_fids`, takže soubor lze kdykoli vyžádat. + +- Strukturu na disku zrcadlí cesta: `euni/-//`. +- Filer metadata (mapa cesta→chunky) jsou v Mongo DB `seaweedfs` na 192.168.1.76; + bloby na poli Unraidu. (Setup: `U:\\PythonProject\\Janssen\\SeaweedFS\\`.) +- Pozn.: přímý přístup přes raw fid/volume zvenčí nefunguje (volume se uvnitř + Dockeru jmenuje `seaweed-volume`); proto se čte/zapisuje přes filer. + +**Obnova kdekoliv** (stačí síť na Mongo + filer): +```bat +python euni_restore.py # vše → ./obnoveno +python euni_restore.py --out D:\Euni # jiný cíl +python euni_restore.py --kurz 5618 # jen jeden kurz +python euni_restore.py --dry-run # jen výpis +``` + +**Backfill** (dohrát do SeaweedFS soubory stažené dřív): +```bat +python euni_stahni.py --seaweed-backfill --from-json +``` + +### Idempotence +- Scrape dělá *upsert*: nový materiál → `ceka`; existující si **drží stav** + (nepřepíše stažené). Lze tedy bez obav scrapovat opakovaně. +- Stahování bere jen `stav: ceka` (a volitelně `chyba` pro retry). + +## Použití + +```bat +python euni_stahni.py --scrape-only # jen inventura → Mongo + JSON +python euni_stahni.py --no-videos # scrape + stáhne jen dokumenty +python euni_stahni.py # scrape + dokumenty + videa +python euni_stahni.py --from-json --no-videos # přeskočí scrape, stáhne z Mongo/JSON +python euni_stahni.py --professions all # všechny profese (2,4,5,6,7) +python euni_stahni.py --limit 3 # jen prvních 3 kurzy (test) +python euni_stahni.py --no-mongo # bez zápisu do Mongo +python euni_report.py # přehled stavu +python euni_report.py --soukroma # seznam přeskočených videí +``` + +## Jak to funguje (ověřeno) + +- **Login** `/sign/` — formulář se parsuje (kopírují se skrytá Nette pole `_do`). +- **Seznam kurzů** — signál `studyAreaList-nextPage` vrací JSON snippet, stránkuje + se dokud přibývají kurzy (profese: 2=Lékař, 4=Farmaceut, 5/6=studenti, 7=NLZP). +- **Detail kurzu** — server-rendered HTML; videa z `