# Dokumentace propisu VYMI (JIS) dat do datové platformy

Data událostí a infotextů jsou z aplikace VYMI aktivně posílána (push) do datové platformy Golemio, vždy ihned po změně v datech. Dále existuje možnost pasivního načtení (pull) dat z VYMI do Golemia, které je provedeno po manuální akci vývojáře a slouží například pro případ, že by došlo k výpadku aktivního posílání. Oba způsoby jsou zjednodušeně znázorněny na následujících dvou sekvenčních diagramech a podrobněji popsány v dalších sekcích.

Sekvenční diagram znázorňující proces aktivního posílání (push) dat:

```mermaid
sequenceDiagram
    actor user as Uživatel VYMI
    participant vymi as Aplikace VYMI
    participant golemio as Datová platforma Golemio

    user->>vymi: Změna v datech událostí nebo infotextů
    vymi->>golemio: Aktivní zaslání (push) dat událostí nebo infotextů do Golemia
```

Sekvenční diagram znázorňující proces pasivního načítání (pull) dat:

```mermaid
sequenceDiagram
    participant vymi as Aplikace VYMI
    participant golemio as Datová platforma Golemio
    actor developer as Vývojář

    developer->>golemio: Vyžádání načtení dat událostí nebo infotextů
    golemio->>vymi: Pasivní načtení (pull) dat událostí nebo infotextů z VYMI
    vymi->>golemio: Data událostí nebo infotextů
```

## Změny dat ve VYMI

V rámci aplikace VYMI nejprve uživatel provede na front endu změny událostí nebo infotextů, které front end pošle na REST API back endu a ten je zapíše do VYMI PostgreSQL databáze. Mezi front endem a back endem data projdou ještě přes VYMI Ory Oathkeeper proxy (která za pomoci VYMI Ory Kratos a VYMI Ory Keto služeb zajišťuje autentizaci, respektive autorizaci uživatele).

Proces je podrobněji znázorněn na následujícím diagramu (diagram nepopisuje celé VYMI REST API, pouze části nejvíce relevantní pro propisování dat do Golemia; stejně tak jsou vynechány detailnější prvky aplikační struktury).

```mermaid
flowchart LR
    subgraph "VYMI"
        subgraph "VYMI Front End"
            vymi_create_event("`Vytvoření události`");
            vymi_update_event("`Úprava události`");
            vymi_update_event_after_duplicate("`Uložení události po duplikaci`");
            vymi_create_infotext("`Vytvoření infotextu`");
            vymi_update_infotext("`Úprava infotextu`");
            vymi_update_infotext_after_duplicate("`Uložení infotextu po duplikaci`");

            %% invisible unicode char ‎ due to https://github.com/orgs/community/discussions/106690
            vymi_api_post_event["`HTTP POST s Ory Kratos session tokenem na http‎://api-vymi.golemio.cz/v1/events`"];
            vymi_api_put_event["`HTTP PUT s Ory Kratos session tokenem na http‎://api-vymi.golemio.cz/v1/events/{eventId}`"];
            vymi_api_post_infotext["`HTTP POST s Ory Kratos session tokenem na http‎://api-vymi.golemio.cz/v1/events/{eventId}/infotexts`"];
            vymi_api_put_infotext["`HTTP PUT s Ory Kratos session tokenem na http‎://api-vymi.golemio.cz/v1/events/{eventId}/infotexts/{infotextId}`"];
        end

        subgraph "VYMI Back End"
            vymi_events_controller_create["`V1EventsController.createEvent`"];
            vymi_events_controller_update["`V1EventsController.updateEvent`"];
            vymi_infotexts_controller_create["`V1EventInfotextsController.createInfotext`"];
            vymi_infotexts_controller_update["`V1EventInfotextsController.updateInfotext`"];
        end

        vymi_pg[("`VYMI PostgreSQL`")];
    end

    vymi_create_event --> vymi_api_post_event;
    vymi_update_event --> vymi_api_put_event;
    vymi_update_event_after_duplicate --> vymi_api_put_event;
    vymi_create_infotext --> vymi_api_post_infotext;
    vymi_update_infotext --> vymi_api_put_infotext;
    vymi_update_infotext_after_duplicate --> vymi_api_put_infotext;

    vymi_api_post_event -- "`Přes VYMI Ory Oathkeeper`" --> vymi_events_controller_create;
    vymi_api_put_event -- "`Přes VYMI Ory Oathkeeper`" --> vymi_events_controller_update;
    vymi_api_post_infotext -- "`Přes VYMI Ory Oathkeeper`" --> vymi_infotexts_controller_create;
    vymi_api_put_infotext -- "`Přes VYMI Ory Oathkeeper`" --> vymi_infotexts_controller_update;

    vymi_events_controller_create -- "`UPSERT do tabulek vymi-alerts.public.events a vymi-alerts.public.event_routes`" --> vymi_pg;
    vymi_events_controller_update -- "`UPSERT do tabulek vymi-alerts.public.events a vymi-alerts.public.event_routes`" --> vymi_pg;
    vymi_infotexts_controller_create -- "`UPSERT do tabulek vymi-alerts.public.infotexts a vymi-alerts.public.infotext_stops`" --> vymi_pg;
    vymi_infotexts_controller_update -- "`UPSERT do tabulek vymi-alerts.public.infotexts a vymi-alerts.public.infotext_stops`" --> vymi_pg;
```

## Aktivní posílání (push) dat

Po uložení změn událostí a infotextů do VYMI PostgreSQL databáze dojde vždy k aktivnímu poslání (push) "aktuálních" dat z VYMI back end služby do Golemia za pomoci HTTP POST dotazu na Golemio Vehicle Positions (VP) Input Gateway (IG).
- u událostí se posílá pouze konkrétní nově vytvořená nebo upravená událost, nikoliv celý seznam aktuálních událostí.
- všechny infotexty, které mají `status` `PUBLISHED`, `visibility` `PUBLIC` a zároveň `active_period_end` je `NULL` nebo >= aktuální čas, neboli lidsky všechny aktivní, budoucí a publikované (ne-draft) infotexty.

VP Input Gateway data přijme a zašle do odpovídající RabbitMQ fronty, odkud je převezme VP Integration Engine, který data zapíše do VP PostgreSQL databáze.

Proces je podrobněji znázorněn na následujícím diagramu (detailnější prvky aplikační struktury jsou vynechány).

```mermaid
flowchart TB
    subgraph "VYMI"
        subgraph "VYMI Back End"
            vymi_events_controller_create["`V1EventsController.createEvent`"];
            vymi_events_controller_update["`V1EventsController.updateEvent`"];
            vymi_infotexts_controller_create["`V1EventInfotextsController.createInfotext`"];
            vymi_infotexts_controller_update["`V1EventInfotextsController.updateInfotext`"];

            vymi_get_events["`EventFacade.getFilteredCustomFormatList`"];
            vymi_get_infotexts["`InfotextFacade.getFilteredCustomFormatList`"];

            %% invisible unicode char ‎ due to https://github.com/orgs/community/discussions/106690
            golemio_api_post_events["`HTTPS POST s API klíčem na https‎://api.golemio.cz/input-gateway/v1/jis/events`"];
            golemio_api_post_infotexts["`HTTPS POST s API klíčem na https‎://api.golemio.cz/input-gateway/v1/jis/infotexts`"];
        end

        vymi_pg[("`VYMI PostgreSQL`")];
    end

    subgraph "Golemio"
        subgraph "Golemio VP Input Gateway"
            golemio_events_controller["`JISEventsController`"];
            golemio_infotexts_controller["`JisInfotextsController`"];
        end

        subgraph "Golemio RabbitMQ"
            golemio_rabbit_refresh_events["`Fronta vehicle-positions.jis.refreshJISEvents`"];
            golemio_rabbit_refresh_infotexts["`Fronta vehicle-positions.jis.refreshJISInfotexts`"];
        end

        subgraph "Golemio VP Integration Engine"
            golemio_refresh_events_task["`RefreshJISEventsTask`"];
            golemio_refresh_infotexts_task["`RefreshJISInfotextsTask`"];
            golemio_refresh_infotexts_cache_task["`RefreshJISInfotextsCacheTask`"];
        end

        vp_pg[("`VP PostgreSQL`")];
        vp_redis[("`VP Redis (Valkey)`")];
    end

    vymi_events_controller_create --> vymi_get_events;
    vymi_events_controller_update --> vymi_get_events;
    vymi_events_controller_update --> vymi_get_infotexts;
    vymi_infotexts_controller_create --> vymi_get_infotexts;
    vymi_infotexts_controller_update --> vymi_get_infotexts;

    vymi_get_events <-- "`SELECT aktuálních událostí z tabulek vehicle-positions.pid.jis_events a vehicle-positions.pid.jis_events_ropidgtfs_routes`" --> vymi_pg;
    vymi_get_infotexts <-- "`SELECT aktuálních infotextů z tabulek vehicle-positions.pid.jis_infotexts a vehicle-positions.pid.jis_infotexts_ropidgtfs_stops`" --> vymi_pg;

    vymi_get_events --> golemio_api_post_events;
    vymi_get_infotexts --> golemio_api_post_infotexts;

    golemio_api_post_events -- "`Přes Golemio Access Proxy`" --> golemio_events_controller;
    golemio_api_post_infotexts -- "`Přes Golemio Access Proxy`" --> golemio_infotexts_controller;

    golemio_events_controller --> golemio_rabbit_refresh_events;
    golemio_infotexts_controller --> golemio_rabbit_refresh_infotexts;

    golemio_rabbit_refresh_events --> golemio_refresh_events_task;
    golemio_rabbit_refresh_infotexts --> golemio_refresh_infotexts_task;

    golemio_refresh_events_task -- "`UPSERT do tabulek vehicle-positions.pid.jis_events a vehicle-positions.pid.jis_events_ropidgtfs_routes`" --> vp_pg;
    golemio_refresh_infotexts_task -- "`UPSERT do tabulek vehicle-positions.pid.jis_infotexts a vehicle-positions.pid.jis_infotexts_ropidgtfs_stops`" --> vp_pg;
    golemio_refresh_infotexts_task --> golemio_refresh_infotexts_cache_task;

    golemio_refresh_infotexts_cache_task -- "`Aktualizace cache jisCache:infotexts:* a jisCache:stop-infotexts:*`" --> vp_redis;
```

Formát zasílaných dat je dále více popsán v implementační dokumentaci ([události](../implementation_documentation.md#jis-events-1), [infotexty](../implementation_documentation.md#jis-infotexts-1)), stejně tak jako odkazy na [Input Gateway OpenAPI](../implementation_documentation.md#input-api) a popis [uložení dat](../implementation_documentation.md#uložení-dat) včetně databázového schema a popisu retence. Data infotextů jsou dále cachovány - tento proces je popsán v dokumentaci [Redis cache](../cache/types/redis.md#cache-pro-api-přestupních-tabulí-transfer-boards).

## Pasivní načítání (pull) dat

V případě potřeby (např. nedojde-li kvůli chybě k aktivnímu poslání dat) lze využít pasivní načtení (pull) dat z VYMI REST API do Golemia. Pro spuštění načtení může vývojář zaslat prázdnou zprávu do správné RabbitMQ fronty, kterou zkonzumuje VP Integration Engine a za pomoci HTTP GET dotazu získá data z VYMI back end služby.

Kromě manuálního spuštění vývojářem je nyní navíc aktivní cron task, který každou minutu automaticky spouští pasivní načítání dat z VYMI do Golemia. Tento mechanismus zajišťuje vyšší odolnost proti výpadkům v aktivním posílání (push) dat.

Při pasivním načítání dat z VYMI do Golemia se využívá mechanismus ETag, který umožňuje rozpoznat, zda se od posledního načtení data změnila. Díky tomu se při opakovaném dotazu přenášejí pouze nová nebo upravená data, zatímco v případě beze změny se vrací pouze informace o tom, že obsah zůstal stejný. Tento přístup zmenšuje objem přenášených dat a snižuje zátěž na databázi a server. ETagy jsou spravovány zvlášť pro události a pro infotexty, aby se změny každého typu vyhodnocovaly nezávisle.

Při pasivním načítání se z VYMI vždy přenačítají pouze aktuální (aktivní) události a infotexty:
- všechny události, které mají `status` `PUBLISHED`, a zároveň `updated_at` hodnotu z posledních 24 hodin nebo `active_period_end` je `NULL` nebo >= aktuální čas, neboli lidsky všechny publikované (ne-draft) události, které jsou aktivní nebo byly upraveny v posledním dnu;
- všechny infotexty, které mají `status` `PUBLISHED`, `visibility` `PUBLIC` a zároveň `active_period_end` je `NULL` nebo >= aktuální čas, neboli lidsky všechny aktivní, budoucí a publikované (ne-draft) infotexty.

## Chování databáze v Golemio

Události – v Golemio databázi se uchovávají všechny historické i aktuální události. Staré záznamy se nepromazávají.

Infotexty – v Golemio databázi se uchovávají pouze aktivní infotexty. Neaktivní se při každém přenačtení promazávají.

Proces je podrobněji znázorněn na následujícím diagramu (detailnější prvky aplikační struktury jsou vynechány).

```mermaid
flowchart TB
    subgraph "VYMI"
        subgraph "VYMI Back End"
            vymi_exports_controller_get_events["`V1ExportsController.getEventListInCustomFormat`"];
            vymi_exports_controller_get_infotexts["`V1ExportsController.getInfotextListInCustomFormat`"];

            vymi_get_events["`EventFacade.getFilteredCustomFormatList`"];
            vymi_get_infotexts["`InfotextFacade.getFilteredCustomFormatList`"];
        end

        vymi_pg[("`VYMI PostgreSQL`")];
    end

    subgraph "Golemio"
        subgraph "Golemio RabbitMQ"
            golemio_rabbit_fetch_events["`Fronta vehicle-positions.jis.fetchJISEvents`"];
            golemio_rabbit_fetch_infotexts["`Fronta vehicle-positions.jis.fetchJISInfotexts`"];
            golemio_rabbit_refresh_events["`Fronta vehicle-positions.jis.refreshJISEvents`"];
            golemio_rabbit_refresh_infotexts["`Fronta vehicle-positions.jis.refreshJISInfotexts`"];
        end

        subgraph "Golemio VP Integration Engine"
            golemio_fetch_events_task["`FetchJISEventsTask`"];
            golemio_fetch_infotexts_task["`FetchJISInfotextsTask`"];
            golemio_refresh_events_task["`RefreshJISEventsTask`"];
            golemio_refresh_infotexts_task["`RefreshJISInfotextsTask`"];
            golemio_refresh_infotexts_cache_task["`RefreshJISInfotextsCacheTask`"];

            %% invisible unicode char ‎ due to https://github.com/orgs/community/discussions/106690
            golemio_api_get_events["`HTTPS GET s API klíčem na https‎://api-vymi.golemio.cz/v1/exports/events/custom-format`"];
            golemio_api_get_infotexts["`HTTPS GET s API klíčem na https‎://api-vymi.golemio.cz/v1/exports/infotexts/custom-format`"];
        end

        vp_pg[("`VP PostgreSQL`")];
        vp_redis[("`VP Redis (Valkey)`")];
    end

    vymi_get_events <-- "`SELECT aktuálních událostí z tabulek vehicle-positions.pid.jis_events a vehicle-positions.pid.jis_events_ropidgtfs_routes`" --> vymi_pg;
    vymi_get_infotexts <-- "`SELECT aktuálních infotextů z tabulek vehicle-positions.pid.jis_infotexts a vehicle-positions.pid.jis_infotexts_ropidgtfs_stops`" --> vymi_pg;

    golemio_rabbit_fetch_events --> golemio_fetch_events_task;
    golemio_rabbit_fetch_infotexts --> golemio_fetch_infotexts_task;
    golemio_rabbit_refresh_events --> golemio_refresh_events_task;
    golemio_rabbit_refresh_infotexts --> golemio_refresh_infotexts_task;

    golemio_fetch_events_task --> golemio_api_get_events;
    golemio_fetch_infotexts_task --> golemio_api_get_infotexts;

    golemio_api_get_events -- "`Přes VYMI Ory Oathkeeper`" --> vymi_exports_controller_get_events;
    golemio_api_get_infotexts -- "`Přes VYMI Ory Oathkeeper`" --> vymi_exports_controller_get_infotexts;

    vymi_exports_controller_get_events --> vymi_get_events;
    vymi_exports_controller_get_infotexts --> vymi_get_infotexts;

    vymi_get_events --> golemio_rabbit_refresh_events;
    vymi_get_infotexts --> golemio_rabbit_refresh_infotexts;

    golemio_refresh_events_task -- "`UPSERT do tabulek vehicle-positions.pid.jis_events a vehicle-positions.pid.jis_events_ropidgtfs_routes`" --> vp_pg;
    golemio_refresh_infotexts_task -- "`UPSERT do tabulek vehicle-positions.pid.jis_infotexts a vehicle-positions.pid.jis_infotexts_ropidgtfs_stops`" --> vp_pg;
    golemio_refresh_infotexts_task --> golemio_refresh_infotexts_cache_task;

    golemio_refresh_infotexts_cache_task -- "`Aktualizace cache jisCache:infotexts:* a jisCache:stop-infotexts:*`" --> vp_redis;
```

Zdroj a formát dat pro aktivní stahování jsou dále více popsány v implementační dokumentaci ([události](../implementation_documentation.md#jis-events), [infotexty](../implementation_documentation.md#jis-infotexts)), stejně tak jako [worker a tasky](../implementation_documentation.md#jisworker) a popis [uložení dat](../implementation_documentation.md#uložení-dat) včetně databázového schema a popisu retence.

## Datový model událostí — vybraná pole

### `cause_detail`

Volitelné textové upřesnění příčiny události (`cause_detail`). Může být `null`. Dostupné v custom-format exportu (`/v1/jis/events/custom-format`) i v GTFS-RT alert feedu jako rozšíření. Struktura:
```json
{ "cs": "Nehoda na křižovatce", "en": null }
```

### `cause` — anglický překlad

Pole `cause` obsahuje překlad příčiny události do češtiny i do angličtiny. Anglická hodnota je zahrnuta i ve výstupu GTFS-RT alert feedu (pole `Alert.cause` v `pid_feed.pb` i `alerts.pb`).

### `effects` (pole příčin dopadu)

Pole `effects` může obsahovat 1–3 unikátní řetězcové hodnoty. Pouze `effects[0]` se mapuje na standardní GTFS-RT `Alert.effect`; zbývající hodnoty jsou dostupné výhradně v custom-format exportu.
