===== 3. Miniprojekt ===== Rozšiřte stávající IoT systém o databázi a REST API. Navrhněte a implementujte službu, která průběžně ukládá data z MQTT brokeru do SQLite databáze a zpřístupňuje je prostřednictvím REST API. **1. Databázové schéma (4 body)**: Navrhněte a implementujte SQLite databázi s alespoň 2 tabulkami. Jedna tabulka bude uchovávat záznamy měření (teplota, časová známka ISO 8601), druhá bude sloužit jako registr zařízení. Každé zařízení identifikované ČVUT loginem bude mít v tabulce zařízení právě jeden záznam obsahující identifikátor zařízení, časovou známku první detekované zprávy, časovou známku poslední detekované zprávy, uptime získaný z poslední přijaté zprávy, aktuálně nastavenou periodu měření a celkový počet detekovaných zpráv. Mezi tabulkami musí být definován vztah prostřednictvím cizího klíče. Schéma databáze bude uloženo v samostatném souboru ''schema.sql''. Při inicializaci Flask aplikace musí být automaticky ověřeno a zajištěno, že: databázový soubor existuje, obě tabulky jsou přítomny a mají správně definované sloupce. Pokud databáze nebo schéma neexistuje, aplikace je automaticky vytvoří. Na pozadí aplikace musí běžet MQTT klient odebírající téma ''cvut/nsi/2026/+/telemetry''. Logika zpracování příchozí zprávy musí postupovat následovně: - Ověří se, že payload je validní JSON obsahující všechna povinná pole (teplota, časová známka měření, perioda měření). Zpráva s chybějícím nebo nevalidním polem (například chybějící teplota, časová známka ve špatném formátu) se zahodí a do konzole se vypíše popis chyby – aplikace nesmí havarovat. - Ze struktury tématu se extrahuje identifikátor zařízení (ČVUT login). Pokud pro toto zařízení dosud neexistuje záznam v registru, nový záznam se vytvoří. - Je-li zpráva validní, zpracuje se payload: vloží se nový záznam měření a aktualizuje se záznam zařízení (uptime, časová známka poslední zprávy, perioda měření, počet zpráv). **2. REST API (4 body)**: Implementujte REST API s následujícími endpointy. Každý endpoint musí vracet správný HTTP stavový kód dle situace a tělo odpovědi vždy ve formátu JSON (včetně chybových stavů): ^ **Metoda** ^ **Endpoint** ^ Popis ^ | GET | ''/api/devices'' | Vrátí seznam detekovaných zařízení; 4xx pokud žádné zařízení neexistuje; 2xx při úspěchu. | | GET | ''/api/devices/ '' | Vrátí detail konkrétního zařízení; 4xx pokud neexistuje; 2xx při úspěchu. | | GET | ''/api/telemetry/ '' | Vrátí konkrétní záznam telemetrie (měření); 4xx pokud neexistuje; 2xx při úspěchu. | | DELETE | ''/api/telemetry/ '' | Smaže konkrétní záznam telemetrie (měření); 4xx pokud neexistuje; 2xx při úspěchu. | | DELETE | ''/api/devices/'' | Smaže zařízení včetně všech jeho telemetrických záznamů (kaskádové smazání); 4xx pokud neexistuje, 2xx při úspěchu. | | POST | ''/api/telemetry '' | Umožní vložení záznamu. Nutná validace struktury těla požadavku; při úspěchu vrací 2xx. V těle požadavku se očekává JSON struktura s položkami: ''device'' (povinné), ''timestamp'' (povinné), ''measure-period'' (povinné), ''temperature'' (povinné), ''uptime'' (student musí vyřešit logiku aktualizace záznamu zařízení, pokud uptime není součástí payloadu – viz bod 1) a ''led''. | **3. Pokročilé dotazování (4 body)**: Přidejte endpoint ''GET /api/telemetry'', který bude podporovat následující query parametry a HTTP hlavičky. URL Query parametry: * ''device_id'' - filtrování záznamů pro konkrétní zařízení, * ''from'' a ''to'' - časový rozsah ve formátu ISO 8601. Pokud je ''from'' větší než ''to'', server vrací 4xx s vysvětlujícím JSON tělem. HTTP hlavičky požadavků: * ''X-Sort-Field'' - pole, podle kterého se výsledky řadí; povolené hodnoty: ''timestamp'', ''temperature''; výchozí hodnota: ''timestamp''. Neznámá hodnota musí být odmítnuta stavem 4xx s vysvětlujícím JSON tělem. * ''X-Sort-Order'' - směr řazení; povolené hodnoty: ''asc'', ''desc''; výchozí hodnota: ''desc''. HTTP hlavičky odpovědí: * ''X-Current-Count'' - počet záznamů odpovídajících zadaným filtrům * ''X-Total –Count'' - celkový počet záznamů v tabulce (bez ohledu na filtry) Hodnotí se správnost SQL dotazů generovaných pro filtrování a řazení. Řešení, která načtou veškerá data do paměti Pythonu a teprve poté třídí nebo filtrují, nebudou uznána. **4. Vizualizace historických dat (3 body)**: Implementujte webovou stránku dostupnou na ''/dashboard'', která zobrazuje graf vývoje teploty v čase pro zvolené zařízení a zvolené časové okno. Stránka musí obsahovat HTML formulář s následujícími ovládacími prvky: * Výběr zařízení prostřednictvím vhodného elementu (například ''select'' nebo ''datalist'') generovaného ze seznamu registrovaných zařízení. * Volba časového okna, přičemž musí být podporovány oba režimy: * absolutní – zadání konkrétního rozsahu „od" a „do", * relativní – „do" je aktuální čas a volí se velikost časového okna (např. posledních 30 minut, 2 hodiny, 1 den) s explicitním zohledněním jednotky (sekunda/minuta/hodina/den). Student si může zvolit přístup dle vlastního uvážení – server-side rendering nebo client-side rendering. Bez ohledu na zvolenou technologii se hodnotí kvalita a použitelnost výstupu: graf musí mít popsané osy, název, správně formátované časové popisky na ose X a musí být přehledný i při řádu stovek záznamů. **BONUS (+3 body)**: Implementujte endpoint ''POST /api/telemetry/batch'', který přijme JSON pole obsahující 1–1000 telemetrických záznamů najednou. Celá dávka musí být vložena v rámci jediné transakce – buď se uloží všechny záznamy, nebo žádný. Pokud jakýkoliv záznam v dávce nesplní validaci (chybějící povinné pole, nevalidní datový typ, časová známka ve špatném formátu), transakce se celá zruší a server vrátí 4xx s tělem obsahujícím index prvního chybného záznamu a popis chyby. Při úspěchu vrátí 2xx s počtem vložených záznamů. Součástí řešení musí být testovací skript ''batch_test.py'', který demonstruje obě větve: úspěšné vložení validní dávky a odmítnutí dávky obsahující jeden nevalidní záznam uprostřed. Skript musí po odmítnuté dávce ověřit, že databáze neobsahuje žádná částečně vložená data – tzn. explicitně dotázat databázi a výsledek vypsat do konzole. Hodnoty HTTP stavových kódů musí student pečlivě uvážit a pro každý případ zvolit vhodný kód. Nápověda: určitě všechny 2xx kódy nemají být “200 OK”.