> ## Documentation Index
> Fetch the complete documentation index at: https://docs.livingsecurity.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Power BI

> Csatlakoztassa a Power BI-t a Living Security nyilvános API-hoz a Power Query segítségével, építsen csillagséma-modellt a /v1 végponton, és válassza ki a phishinghez megfelelő operatív vagy riportolási adathalmazokat.

Ez az útmutató egy teljes körű példa arra, hogyan olvashatja be a `/v1` adatait a Power BI-ba. Bemutatja
a Power Query (M) kapcsolódási mintáját, a `/v1/activities` végpontra épülő csillagséma-alapú szemantikai modellt, a növekményes frissítést, valamint azt, hogy mikor érdemes a személyenkénti phishing-eredményekhez a **reporting** előzetes adathalmazokat használni.

A példák az amerikai éles hostot használják
(`https://api.us-east-1.livingsecurity.ai/v1`). EU-s szervezet esetén használja a
`https://api.eu-west-1.livingsecurity.ai/v1` címet a `Fn_FetchAll` és
`Fn_GetJson` helyén is, majd kösse az adatforrás hitelesítő adatát ehhez a hosthoz. Lásd:
[Régiók](/hu/developers/v1/regions).

Ez az útmutató API-kulccsal tárolt Basic hitelesítő adatokat használ. OAuth-kliensre nincs szüksége. A hitelesítő adatok lehetőségeiről lásd a [Hitelesítés](/hu/developers/v1/authentication) oldalt, a hozzáférésről és a beállításról pedig a [Developer Hub](/hu/developers/developer-hub/overview) oldalt.

## Mire való ez a példa

Akkor használja, ha a következőkre van szüksége:

* A `/v1` lista végpontjainak betöltése egy Power BI adathalmazba helyes kurzoros lapozással és sebességkorlát-újrapróbálkozással.
* Olyan szemantikai modell létrehozása, amelyben a mérőszámok („aktív entitások", „tevékenységek aktív entitásonként") dimenzionálisak, és nem egy lapos táblából számolt darabszámok.
* Az ütemezett Power BI frissítés visszavezetése a teljes előzmények bejárásáról egy kis napi ablakra növekményes frissítéssel.
* A megfelelő phishing-szemcsézettség kiválasztása: kampányösszesítők (operatív) és személyenkénti eredmények, illetve típusos események (reporting előzetes).

## Előfeltételek

* Egy API-kulcs a lekérdezései által igényelt jogosultságokkal:
  * Operatív példa: `activity:read`, `entity:read` és
    `simulation:read`.
  * Személyenkénti phishing a reporting adathalmazokon keresztül: `reporting:read`.
    Lásd: [API-kulcsok](/hu/developers/developer-hub/api-keys).
* Power BI Desktop, vagy egy Power BI / Fabric munkaterület, ahol létrehozhat szemantikai modellt és ütemezhet frissítést.

## A megfelelő szemcsézettség kiválasztása

A `/v1` a phishinghez két kapcsolódó felületet kínál. Ezek nem felcserélhetők.

| Igény | Használandó | Jogosultság | Szemcsézettség |
| - | - | - | - |
| Kampánylefutási összesítők | `GET /v1/simulations/{id}/results` | `simulation:read` | Egy sor szimulációnként (kézbesítve, megnyitva, rákattintva, …) |
| Személyenkénti eredmények (operatív) | `GET /v1/simulation_results` | `simulation:read` | Egy sor entitás × szimuláció párosonként |
| Személyenkénti eredmények (BI / reporting) | `GET /v1/reporting/simulation_results` | `reporting:read` | Azonos szemcsézettség, BI mezőnevek (`entity_id`, `simulation_id`), `updated_after` vízjel |
| Típusos interakciós tények | `GET /v1/reporting/simulation_events` | `reporting:read` | Egy tény interakciónként (`simulation.link_clicked`, …), `occurred_after` |
| Aktuális munkaerő-dimenzió | `GET /v1/reporting/entities` | `reporting:read` | Egy aktuális sor entitásonként. Illesztés: `reporting.simulation_results.entity_id = reporting.entities.id` |
| Entitások aktuális verziói | `GET /v1/reporting/entity_versions` | `reporting:read` | Egy nyitott végű aktuális intervallum entitásonként. Verziótörténeti tábla nem létezik |
| Aktuális kohorsz-tagságok | `GET /v1/reporting/cohort_memberships` | `reporting:read` | Egy aktuális tény `(cohort_id, entity_id)` szerint. Tagsági előzménytábla nem létezik |
| Illesztési gerinc minden tevékenységtípushoz | `GET /v1/activities` | `activity:read` | Egy esemény tevékenységenként, beágyazott `participants[]` |

Az alábbi Power BI példalekérdezések az **operatív** útvonalakat hívják
(`/v1/simulations`, `/v1/simulations/{id}/results`, `/v1/activities`,
`/v1/entities`). A személyenkénti phishing-riportoláshoz inkább az alábbi
**reporting** előzetes családot használja. Az a kulcs, amely csak `reporting:read` jogosultságot kapott, nem hívhatja
az operatív `/v1/simulation_results` végpontot (az API `403` választ ad).

### Nehézségi skála

A szimuláció nehézsége 1–5 közötti egész szám a következőkön: `/v1/simulations` (`difficulty_level`),
`/v1/simulation_results` (`difficulty_level`), `/v1/activities` tulajdonságok
(`difficulty_tier`), valamint `/v1/reporting/simulation_events` (`difficulty_tier`).
`1` a legkönnyebb, `5` a legnehezebb. Az adaptív és a progresszív szimulációknak nincs
egyetlen kampányszintű értéke, ezért a `/v1/simulations.difficulty_level` értéke `null`,
míg az eredményeken a címzettenkénti `difficulty_level` továbbra is beállítható. A korábbi
alacsony/közepes/magas exportokra való leképezéshez a `1–2` értéket tekintse alacsonynak, a `3` értéket közepesnek, a `4–5` értéket pedig
magasnak.

<Warning>
  Ne építsen új BI-integrációkat dokumentálatlan Műszerfal-hálózati hívásokra
  vagy a korábbi, kurált `/api/v1` nyilvános API-áttekintésre. A szervezet regionális `/v1` hostján
  Developer Hub API-kulccsal hitelesítsen. Lásd:
  [Régiók](/hu/developers/v1/regions).
</Warning>

## Kapcsolódási minta (Power Query)

A példa minden lekérdezése négy szabályra épül. Mindegyiket a
Power BI Service érvényesíti, nem stilisztikai preferenciák.

<AccordionGroup>
  <Accordion title="A Web.Contents alap URL-je legyen statikus, és /v1-re végződjön">
    Minden dinamikus elemet a `RelativePath` és `Query` paraméterbe tegyen. A
    `Web.Contents` első argumentuma literális karakterlánc kell legyen, amely a szervezet
    regionális `/v1` gyökerénél ér véget:

    * US: `https://api.us-east-1.livingsecurity.ai/v1`
    * EU: `https://api.eu-west-1.livingsecurity.ai/v1`

    A hívók ezután csak az erőforrást adják meg (`"simulations"`, nem
    `"v1/simulations"`). Ne illesszen be amerikai hostot EU-s szervezethez.

    Ha az első argumentumot karakterlánc-összefűzéssel állítja elő, a Power BI Service
    nem tudja statikusan azonosítani az adatforrást, és az ütemezett frissítés „dynamic data source" hibával meghiúsul, még ha a lekérdezés a Power BI Desktopban működik is.
  </Accordion>

  <Accordion title="Az API-kulcsot tárolt Basic hitelesítő adatként adja meg, ne a lekérdezés szövegében">
    Amikor a Power BI hitelesítő adatokat kér a `/v1` adatforráshoz, válassza a
    **Basic** lehetőséget, és az API-kulcsot adja meg **felhasználónévként**. A **jelszó** mezőbe írjon egy helyőrzőt, például
    `x`. A Power BI Service üres jelszót elutasít, bár az API ezt a mezőt figyelmen kívül hagyja. A kulcs jelszóként, helyőrző felhasználónévvel szintén működik.

    Ne adjon hozzá `Authorization` fejlécet M-ben. Az asztali gépén működik, de
    beleégeti a kulcsot a lekérdezésfájlba, és ütközik az ütemezett frissítéssel.
  </Accordion>

  <Accordion title="Hagyja lefutni a hitelesítő adatok párbeszédpanelének kapcsolatteszt-jét">
    A `GET /v1` hitelesített végpont: érvényes kulccsal `200` választ ad, kulcs nélkül
    `401` választ. Az adatforrást a `…/v1` címre irányítsa, és **ne** hagyja ki a tesztet —
    egy jó kulcs mentésre kerül, egy visszavont vagy hibás kulcs azonnal hibát ad.

    A régebbi példák a puszta hostot használták adatforrásként, és azt mondták, hogy hagyja ki
    a tesztet, mert a `GET /` mindig `404`. Irányítsa át ezeket a modelleket a `…/v1` címre.
  </Accordion>

  <Accordion title="Minden lista-lekérdezésben állítsa be explicit módon a limit=500 értéket">
    Az oldalméret alapértéke minden `/v1` lista végponton 25. Az alapértelmezés meghagyása
    ugyanannyi adathoz megsokszorozza a kérések számát. A lekérdezési értékek szövegek kell legyenek —
    `limit = "500"` M-ben, nem `limit = 500`.
  </Accordion>
</AccordionGroup>

### Közös lapozófüggvény

A példa minden lekérdezése egyetlen `Fn_FetchAll` függvényt hív, amely addig járja be a
`next_page_token` értéket, amíg az `null` nem lesz (lásd a
[lapozást](/hu/developers/v1/pagination)). Jellemző tulajdonságai:

* **Statikus `/v1` alap URL, dinamikus `RelativePath` és `Query`.** Az ütemezett frissítéshez és a hitelesítő adatok teszteléséhez szükséges.
* **`ManualStatusHandling = {429}` korlátozott újrapróbálkozással.** A függvény beolvassa a
  `Retry-After` értéket, ha a Power Query elérhetővé teszi, egyébként 30 másodpercet vár, és
  5 egymást követő `429` válasz után kifejezett hibával feladja, ahelyett hogy addig ismételne, amíg a frissítés időtúllépéssel le nem áll. A `5xx` hibát szándékosan nem
  próbálja újra — a kiszolgálóhibának felszínre kell kerülnie, nem pedig csendben részleges
  adathalmazt eredményeznie.
* **60 másodperces `Timeout`.** Az oldalak jellemzően egy másodpercnél jóval rövidebb idő alatt válaszolnak, így
  egy perc hallgatás azt jelenti, hogy a kérés elveszett.
* **`Binary.Buffer` a `Json.Document` előtt.** Pufferelés nélkül a mashup
  motor újra lekérheti a válaszfolyamot, ha azt egynél többször olvassa.
* **A `baseQuery` minden iterációban változtatás nélkül kerül átadásra.** A lapozótokenek
  átlátszatlan, aláírt kurzorok, amelyek a szervezethez, a hitelesítő adathoz, a szerződésverzióhoz, valamint a szűrők és a rendezés ujjlenyomatához kötöttek. A szűrő menet közbeni módosítását a rendszer érvénytelen tokenként utasítja el, és nem indul újra az első oldaltól.

```m Fn_FetchAll.pq — base and call shape theme={null}
let
    // Literal /v1 root. Callers pass the resource alone ("simulations").
    // US: "https://api.us-east-1.livingsecurity.ai/v1"
    BaseUrl = "https://api.eu-west-1.livingsecurity.ai/v1",

    Fn_FetchAll = (relativePath as text, baseQuery as record) as list =>
        // Walk next_page_token until null. Full pagination + 429 retry lives
        // in examples/powerbi/queries/Fn_FetchAll.pq.
        ...
in
    Fn_FetchAll
```

### Típusos lekérdezések végpontonként

A `Fn_FetchAll` minden fogyasztója ugyanazt a mintát követi: meghívja a végpontot,
rögzített oszloplistát vetít, elhagyja a `object` megkülönböztetőt, és az időbélyegeket
`datetimezone` típusúra állítja, hogy a dátummérőszámok ne csússanak el észrevétlenül a
frissítési kapacitás időzónája miatt.

<CodeGroup>
  ```m Simulations.pq (GET /v1/simulations) theme={null}
  let
      Source = Fn_FetchAll("simulations", [ limit = "500" ]),

      AsTable = Table.FromRecords(
          Source,
          {
              "id", "object", "name", "description", "status", "vectors",
              "difficulty_level", "difficulty_mode",
              "started_at", "completed_at", "created_at", "updated_at"
          },
          MissingField.UseNull),

      Dropped = Table.RemoveColumns(AsTable, { "object" }),

      Flattened = Table.TransformColumns(
          Dropped,
          {
              {
                  "vectors",
                  each if _ = null then null
                       else Text.Combine(List.Transform(_, Text.From), ", "),
                  type text
              }
          }),

      Typed = Table.TransformColumnTypes(
          Flattened,
          {
              { "id",           type text },
              { "name",         type text },
              { "description",  type text },
              { "status",       type text },
              { "started_at",   type datetimezone },
              { "completed_at", type datetimezone },
              { "created_at",   type datetimezone },
              { "updated_at",   type datetimezone }
          })
  in
      Typed
  ```

  ```m Entities.pq (GET /v1/entities) theme={null}
  let
      Source = Fn_FetchAll("entities", [ limit = "500" ]),

      AsTable = Table.FromRecords(
          Source,
          { "id", "object", "type", "status", "display_name", "email",
            "created_at", "updated_at" },
          MissingField.UseNull),

      Dropped = Table.RemoveColumns(AsTable, { "object" }),

      Typed = Table.TransformColumnTypes(
          Dropped,
          {
              { "id",           type text },
              { "type",         type text },
              { "status",       type text },
              { "display_name", type text },
              { "email",        type text },
              { "created_at",   type datetimezone },
              { "updated_at",   type datetimezone }
          })
  in
      Typed
  ```
</CodeGroup>

<Tip>
  A `Table.FromRecords` lépésen megadott explicit oszloplista stabilan tartja a sémát,
  ha egy végpont nulla sort ad vissza. Egy puszta `Table.FromRecords({})` nulla oszlopos táblát eredményez, és minden későbbi lépés meghiúsul a hiányzó mezők miatt.
</Tip>

## Excel

Ugyanez a Power Query (M) fut az Excel Get & Transform szolgáltatásában is — ugyanaz a motor,
mint a Power BI Desktopban. Akkor használja, ha szemantikai modell helyett munkalap-táblára van szüksége.

Excel for Windows esetén (Microsoft 365, Excel 2024, 2021, 2019 vagy 2016):

1. **Data → Get Data → From Other Sources → Blank Query.**
2. A Power Query Editorban **Home → Advanced Editor**. Először illessze be a
   `Fn_FetchAll` lekérdezést, majd nevezze át `Fn_FetchAll` névre (Query
   Settings → Name, vagy kattintson jobb gombbal a Queries panelen → **Rename**).
3. Ismételje meg a `Fn_GetJson` esetében, majd minden riportlekérdezésnél.
4. A **Home → Close & Load → Close & Load To…** megnyitja az **Import Data**
   párbeszédpanelt. A `Fn_FetchAll` és
   `Fn_GetJson` esetében válassza az **Only Create Connection** lehetőséget (ezek függvények, nem táblák). Minden olyan riportlekérdezésnél, amelyet munkalapon szeretne látni, válassza a **Table** lehetőséget.
5. Az első frissítéskor válassza a **Basic** lehetőséget: az API-kulcs legyen a **User name**, egy
   helyőrző, például `x` pedig a **Password**. Az Excel adatforrás-URL-enként egy hitelesítő adatot tárol; a később ugyanarra a `/v1` hostra hivatkozó lekérdezések ezt újrahasználják.

<Note>
  A **Close & Load To…** elszürkülhet, amikor először hoz létre lekérdezést egy
  munkafüzetben. Használja a **Close & Load** lehetőséget, majd az Excelben válassza a **Data →
  Queries & Connections** elemet, kattintson jobb gombbal a lekérdezésre, és válassza a **Load To** lehetőséget.
  Ezt az utat a Microsoft erre az esetre dokumentálja.
</Note>

A Power Queryből hitelesített forráson nem küldhető `Living-Security-Version` — ugyanez a korlátozás érvényes a Power BI-ra is. Rögzítse a szolgáltatásfiókot, vagy adja át a `api_version` értéket a lekérdezés rekordjában. Lásd:
[Szerződésverziók és tárolt hitelesítő adatok](#szerződésverziók-és-tárolt-hitelesítő-adatok).

Kész riportlekérdezések (oszlopleképezések és eltérések egy korábbi szolgáltató exportjától):

<CardGroup cols={3}>
  <Card title="Személyenkénti megfelelés" href="/hu/developers/v1/reports/per-person-compliance">
    Képzési beiratkozások személy × hozzárendelés szemcsézettséggel.
  </Card>

  <Card title="E-mail phishing napló" href="/hu/developers/v1/reports/email-phishing-log">
    Eseményszintű phishing-interakciók, az elmúlt 12 hónapra.
  </Card>

  <Card title="Legérzékenyebb felhasználók" href="/hu/developers/v1/reports/most-susceptible-users">
    Személyenkénti 12 havi kattintási, hitelesítőadat- és jelentési összesítés.
  </Card>
</CardGroup>

## Reporting előzetes adathalmazok (személyenkénti phishing)

A reporting előzetes család adathalmazokat biztosít a személyenkénti phishinghez.
Szükséges hozzá a `reporting:read`, valamint a szervezet reporting hozzáférése.
Az alábbi adathalmaz-katalógussal megtekintheti a sémákat és az adatkezelési szabályokat.

Az élő katalógus lekérdezése:

```text theme={null}
GET /v1/reporting/datasets
GET /v1/reporting/datasets/simulation_results
GET /v1/reporting/datasets/simulation_events
GET /v1/reporting/datasets/entities
GET /v1/reporting/datasets/entity_versions
GET /v1/reporting/datasets/cohort_memberships
```

Minden adathalmaz megadja a szemcsézettségét, elsődleges kulcsát, illesztéseit, vízjelét, javítási és
törlési szabályzatát.

### simulation\_results

* **Útvonal:** `GET /v1/reporting/simulation_results`
* **Szemcsézettség:** entitás × szimuláció
* **Szűrők:** `simulation`, `entity`, `updated_after`, valamint `limit` /
  `page_token`
* **Wire objektum:** `reporting.simulation_result`
* **Illesztési kulcsok:** `entity_id`, `simulation_id`

```m ReportingSimulationResults.pq (sketch) theme={null}
let
    Source = Fn_FetchAll(
        "reporting/simulation_results",
        [ limit = "500" ]),

    AsTable = Table.FromRecords(
        Source,
        {
            "id", "object", "entity_id", "simulation_id", "status",
            "targeted_at", "delivered_at", "opened_at", "clicked_at",
            "credential_submitted_at", "reported_at",
            "email_subject", "sender_email", "difficulty_level",
            "created_at", "updated_at", "deleted_at"
        },
        MissingField.UseNull),

    Dropped = Table.RemoveColumns(AsTable, { "object" }),

    Typed = Table.TransformColumnTypes(
        Dropped,
        {
            { "id",            type text },
            { "entity_id",     type text },
            { "simulation_id", type text },
            { "status",        type text },
            { "targeted_at",   type datetimezone },
            { "updated_at",    type datetimezone }
        })
in
    Typed
```

Használja a `updated_after` értéket zárt alsó vízjelként a friss partíciók frissítésekor. A változtatható eredménydimenziók helyben, ugyanazon `id` alatt íródnak felül.

### simulation\_events

* **Útvonal:** `GET /v1/reporting/simulation_events`
* **Szemcsézettség:** egy típusos tény megfigyelt interakciónként
* **Szűrők:** `simulation`, `entity`, `event_type`, `occurred_after`, valamint
  `limit` / `page_token`
* **Wire objektum:** `reporting.simulation_event`
* **Eseménytípusok:** zárt készletű nyilvános tokenek, például `simulation.delivered`,
  `simulation.opened`, `simulation.link_clicked`,
  `simulation.credentials_submitted`, `simulation.reported` — nem a nyers
  ClickHouse `phishing.simulation.*` tevékenységtípusok
* **Eszköz:** a `device_type` a származtatott osztály (a megfigyelt tokenek közé tartozik a
  `desktop`, `mobile`, `tablet`, `unknown`). A nyers `user_agent` nem szerepel a
  `/v1` objektumon. A nyers IP soha nem szerepel a `/v1` objektumon (soha nem kerül tartós tárolásra; csak sózott `ip_hash`).

```m ReportingSimulationEvents.pq (sketch) theme={null}
let
    Source = Fn_FetchAll(
        "reporting/simulation_events",
        [ limit = "500" ]),

    AsTable = Table.FromRecords(
        Source,
        {
            "id", "object", "entity_id", "simulation_id",
            "event_type", "occurred_at",
            "email_subject", "sender_email", "sender_domain", "difficulty_tier",
            "device_type",
            "deleted_at"
        },
        MissingField.UseNull),

    Dropped = Table.RemoveColumns(AsTable, { "object" }),

    Typed = Table.TransformColumnTypes(
        Dropped,
        {
            { "id",            type text },
            { "entity_id",     type text },
            { "simulation_id", type text },
            { "event_type",    type text },
            { "occurred_at",   type datetimezone }
        })
in
    Typed
```

Használja a `occurred_after` értéket növekményes eseménybetöltéshez. A tények csak hozzáfűzhetők;
a késve érkező adatok új sorok.

### entities

* **Útvonal:** `GET /v1/reporting/entities`
* **Szemcsézettség:** egy aktuális sor entitásonként
* **Szűrők:** `type`, `updated_after`, valamint `limit` / `page_token`
* **Wire objektum:** `reporting.entity`
* **Illesztési kulcs:** `id` / `entity_id` (azonos érték)

A phishing-tényeket a személyekhez így illessze:
`reporting.simulation_results.entity_id = reporting.entities.id`.
Az operatív `GET /v1/entities` a `entity:read` végponton marad, és más
jogosultságot igényel.

```m ReportingEntities.pq theme={null}
let
    Source = Fn_FetchAll("reporting/entities", [ limit = "500" ]),

    AsTable = Table.FromRecords(
        Source,
        {
            "id", "object", "entity_id", "type", "status",
            "display_name", "created_at", "updated_at", "deleted_at"
        },
        MissingField.UseNull),

    Dropped = Table.RemoveColumns(AsTable, { "object" }),

    Typed = Table.TransformColumnTypes(
        Dropped,
        {
            { "id",          type text },
            { "entity_id",   type text },
            { "type",        type text },
            { "status",      type text },
            { "display_name", type text },
            { "created_at",  type datetimezone },
            { "updated_at",  type datetimezone }
        })
in
    Typed
```

### entity\_versions és cohort\_memberships

Ez a két adathalmaz egy névvel jelölt, csak az aktuális állapotot tartalmazó kivételt tartalmaz:

> Nem létezik hiteles verzió- vagy tagságtörténeti tábla. Ez az adathalmaz az aktuális sort egyetlen nyitott végű intervallumként vetíti ki (a `valid_to` értéke null, a `is_current` értéke true). A `valid_from` értéke `created_at`, ennek hiányában `updated_at` az entitásverzióknál, a kohorsz-tagságoknál pedig a kérés megfigyelési ideje. Lezárt történeti intervallumokat nem hoz létre.

* **entity\_versions:** `GET /v1/reporting/entity_versions`. Ugyanazok a szűrők, mint az
  entities esetén. A `valid_from` értéke `created_at`, ennek hiányában `updated_at`. Az az entitás,
  amelynek mindkét időbélyege null, kimarad.
* **cohort\_memberships:** `GET /v1/reporting/cohort_memberships`. Szűrők:
  `cohort`, `entity`, `updated_after`. A `updated_at` a kohorsz utolsó frissítése,
  így a növekményes frissítés pontos marad. A `valid_from` a kérés
  megfigyelési ideje. Illessze a `entity_id` mezőt a `reporting.entities.id` mezőhöz.

```m ReportingEntityVersions.pq theme={null}
let
    Source = Fn_FetchAll("reporting/entity_versions", [ limit = "500" ]),

    AsTable = Table.FromRecords(
        Source,
        {
            "id", "object", "entity_id", "type", "status", "display_name",
            "valid_from", "valid_to", "is_current",
            "created_at", "updated_at", "deleted_at"
        },
        MissingField.UseNull),

    Dropped = Table.RemoveColumns(AsTable, { "object" }),

    Typed = Table.TransformColumnTypes(
        Dropped,
        {
            { "id",         type text },
            { "entity_id",  type text },
            { "valid_from", type datetimezone },
            { "is_current", type logical }
        })
in
    Typed
```

```m ReportingCohortMemberships.pq theme={null}
let
    Source = Fn_FetchAll("reporting/cohort_memberships", [ limit = "500" ]),

    AsTable = Table.FromRecords(
        Source,
        {
            "id", "object", "cohort_id", "entity_id",
            "valid_from", "valid_to", "is_current",
            "updated_at", "deleted_at"
        },
        MissingField.UseNull),

    Dropped = Table.RemoveColumns(AsTable, { "object" }),

    Typed = Table.TransformColumnTypes(
        Dropped,
        {
            { "id",         type text },
            { "cohort_id",  type text },
            { "entity_id",  type text },
            { "valid_from", type datetimezone },
            { "is_current", type logical },
            { "updated_at", type datetimezone }
        })
in
    Typed
```

## Csillagséma-alapú szemantikai modell a /v1/activities végponton

A `/v1/activities` a szervezet időbélyegzett eseményfolyama. Nincsenek numerikus mérőszámai — a riportok minden mennyisége sorszám —, ezért a csillagséma természetes választás.

```text theme={null}
Dates (dimension)
      │  1
      ▼  ∗
  Activities (fact)   1  ∗  ActivityParticipants (bridge)   ∗  1   Entities (dimension)
```

* A **`Activities`** az eseménytény a valódi szemcsézettséggel. Egy sor tevékenységenként.
  A résztvevők itt elhagyásra kerülnek, és külön táblaként épülnek újra.
* A **`ActivityParticipants`** azért létezik, mert a résztvevők valóban egy-a-többhöz kapcsolatban állnak, olyan szerepekkel, mint a `actor` és a `target`. A `Activities` táblára való ellapítás vagy megkettőzné a tevékenységsorokat (minden számot felfújva), vagy elveszítené a további résztvevőket.
  A `Activities` felé mutató kapcsolat a modell egyetlen kétirányú kapcsolata, így egy entitás-szeletelő eléri a ténytáblát.
* A **`Entities`** a résztvevőket megnevező dimenzió. Egy tevékenység résztvevősorai `entity_id` értéket és szerepet hordoznak, de megjelenített nevet nem.
* A **`Dates`** M-ben jön létre (nulla API-hívással), és a modell dátumtáblájaként van megjelölve. Ez teszi szabályossá a DAX időintelligencia-függvényeket
  (`DATESINPERIOD`, `DATEADD`). A hónap- és hétköznapnév-oszlopok a számot tartalmazó párjaikon deklarálják a
  `sortByColumn` beállítást, hogy a vizualizációk ne ábécésorrendben rendezzék a hónapokat.

Minden mérőszám explicit (a modell beállítja: `discourageImplicitMeasures`): összes
tevékenység, aktív entitások, tevékenységek aktív entitásonként, tevékenységgel rendelkező napok,
valamint egy 7 napos mozgó napi átlag.

### A hídtábla felépítése

A `ActivityParticipants` bejárja a `/v1/activities` végpontot, és a `participants`
tömböt résztvevőnként egy sorra bontja:

```m ActivityParticipants.pq theme={null}
let
    WindowStart = DateTime.AddZone(RangeStart, 0),
    WindowEnd   = DateTime.AddZone(RangeEnd, 0),
    ToIso = (value as datetimezone) as text =>
        DateTimeZone.ToText(value,
            [ Format = "yyyy-MM-dd'T'HH:mm:ss'Z'", Culture = "en-US" ]),

    Source = Fn_FetchAll(
        "activities",
        [
            limit      = "100",
            start_date = ToIso(WindowStart - #duration(0, 0, 1, 0)),
            end_date   = ToIso(WindowEnd + #duration(0, 0, 1, 0))
        ]),

    AsTable = Table.FromRecords(
        Source,
        { "id", "object", "activity_type", "event_ts",
          "service_id", "participants" },
        MissingField.UseNull),

    Selected = Table.SelectColumns(AsTable, { "id", "event_ts", "participants" }),
    Renamed  = Table.RenameColumns(Selected, { { "id", "activity_id" } }),
    Expanded = Table.ExpandListColumn(Renamed, "participants"),
    NonNull  = Table.SelectRows(Expanded, each [participants] <> null),
    Fields   = Table.ExpandRecordColumn(
        NonNull, "participants",
        { "role", "entity_id", "display_name" }),

    Typed = Table.TransformColumnTypes(
        Fields,
        {
            { "activity_id",  type text },
            { "event_ts",     type datetimezone },
            { "role",         type text },
            { "entity_id",    type text },
            { "display_name", type text }
        }),

    Filtered = Table.SelectRows(
        Typed,
        each [event_ts] >= WindowStart and [event_ts] < WindowEnd)
in
    Filtered
```

A `ActivityParticipants` a `/v1/activities` végpontot önállóan járja be, ahelyett hogy a `Activities` lekérdezésre hivatkozna. A Power BI-ban két táblaként betöltött két lekérdezés frissítéskor egymástól függetlenül kérdez le adatot. A függetlenség lehetővé teszi, hogy a szolgáltatás mindkét tábla partícióit párhuzamosan frissítse.

## Növekményes frissítés

Mindkét tényszintű tábla (`Activities` és `ActivityParticipants`) Power BI növekményes frissítési szabályzatot deklarál: 5 évnyi éves partíciót tart meg, és minden frissítéskor újraimportálja az utolsó 14 napot.

A mechanizmus egy DateTime paraméterpár, a `RangeStart` és a `RangeEnd`,
amelyeket a Power BI szolgáltatás frissítéskor partíciónként újrakötöz. Minden ténylekérdezés ezeket a paramétereket beépíti az API `start_date` és `end_date`
szűrőibe, így a partíciószintű kérésmennyiség a partícióval arányos, nem a teljes előzményekkel. A lekérdezés mindkét oldalon egy perccel kiszélesíti az API ablakát, és a pontos félig nyitott `[RangeStart, RangeEnd)` határt helyben alkalmazza újra, így a partíció helyessége nem függ attól, hogy a kiszolgáló a határait zártként vagy nyíltként kezeli.

```m Activities.pq — the fold that makes incremental refresh work theme={null}
let
    WindowStart = DateTime.AddZone(RangeStart, 0),
    WindowEnd   = DateTime.AddZone(RangeEnd, 0),
    ToIso = (value as datetimezone) as text =>
        DateTimeZone.ToText(value,
            [ Format = "yyyy-MM-dd'T'HH:mm:ss'Z'", Culture = "en-US" ]),

    Source = Fn_FetchAll(
        "activities",
        [
            limit      = "100",
            start_date = ToIso(WindowStart - #duration(0, 0, 1, 0)),
            end_date   = ToIso(WindowEnd + #duration(0, 0, 1, 0))
        ]),

    AsTable = Table.FromRecords(
        Source,
        { "id", "object", "activity_type", "event_ts",
          "service_id", "participants" },
        MissingField.UseNull),

    Dropped = Table.RemoveColumns(AsTable, { "object", "participants" }),

    Typed = Table.TransformColumnTypes(
        Dropped,
        {
            { "id",            type text },
            { "activity_type", type text },
            { "event_ts",      type datetimezone },
            { "service_id",    type text }
        }),

    Filtered = Table.SelectRows(
        Typed,
        each [event_ts] >= WindowStart and [event_ts] < WindowEnd),

    WithDate = Table.AddColumn(
        Filtered, "event_date", each Date.From([event_ts]), type date)
in
    WithDate
```

A telepítés utáni első frissítés alkalmazza a szabályzatot: teljes történeti betöltés,
lassú. Minden ezt követő frissítés csak a növekményes ablakot érinti.

Mérés \~158 000 tevékenységet és \~2 300 entitást tartalmazó adathalmazon:

| Frissítés | Futási idő |
| - | - |
| Első (szabályzat alkalmazása, teljes történeti betöltés) | \~31 perc |
| Minden további frissítés | **\~15 mp** |

<Warning>
  A klasszikus Power BI frissítési REST végpont (`POST .../refreshes` csak
  `notifyOption` értékkel) csendben figyelmen kívül hagyja a frissítési szabályzatokat, és minden alkalommal teljes bejárást végez. A szabályzat REST API-n keresztüli alkalmazásához használja a továbbfejlesztett frissítést: küldje el a `type`, `commitMode` és `applyRefreshPolicy` értékeket a törzsben. A Power BI Service ütemezője által indított frissítések mindig alkalmazzák a szabályzatokat — ez a sajátosság a REST-hívásra jellemző.
</Warning>

<Note>
  A 14 napos ablak szándékosan bőkezű. A tevékenységek betöltése nem csupán a jelen időponthoz fűz hozzá: az összekötők visszatöltései múltbeli időbélyegű eseményeket hoznak. Egy 1–2 napos ablak ezeket véglegesen elmulasztaná.
</Note>

## A frissítési ablak kiterjesztése az API-ba

A `GET /v1/activities` elfogadja a `start_date` és `end_date` szűrőket, amelyekbe a
Power BI paraméterei beépülnek, és a `event_ts` értéket a két határ között szűri.
A `Entities` dimenzió, ezért ez a példa minden alkalommal teljesen frissíti, nem növekményesen.

A reporting adathalmazok saját vízjeleket használnak (`updated_after`,
`occurred_after`) a tevékenységek dátumablaka helyett.

## Sebességkorlátok frissítés közben

A `/v1` külön kulcsonkénti és IP-címenkénti keretet érvényesít (lásd a
[sebességkorlátokat](/hu/developers/v1/rate-limits)). Vegye figyelembe a `429` + `Retry-After` értékeit; ne
égessen be kérési keretet a modellbe. A Power BI frissítések két tulajdonsága számít:

* **Egyetlen tábla bejárása szekvenciális.** Minden oldalkérés az előző oldal tokenjét igényli, így egy tábla az oldalmérettől függetlenül késleltetésfüggő.
* **A párhuzamos táblafrissítések szorzódnak.** Az a helyzet éri el a korlátot, amikor több nagy tábla frissül párhuzamosan, ezért számít a 429-es újrapróbálkozási út a `Fn_FetchAll` függvényben. Inkább kevesebb párhuzamos táblát használjon, mintsem korlátlan párhuzamosságot feltételezzen.

Az újrapróbálkozást valós, telített korlátozóval is kipróbálták. Az a frissítés, amely alatt egy párhuzamos terhelés szándékosan a korláton tartotta a kulcsot, mégis befejeződött, és ugyanazokat a sorszámokat adta, mint a zavartalan alapmérés — az újrapróbálkozások teljes oldalakat adtak vissza, hézagok nélkül.

## Szerződésverziók és tárolt hitelesítő adatok

A `/v1` elfogad egy `Living-Security-Version` kérésfejlécet egy publikált szerződésverzió rögzítéséhez (lásd az [API gyorsindítót](/hu/developers/v1/quickstart)). A Power Query nem tudja elküldeni ezt a fejlécet, ha az adatforrás tárolt hitelesítő adatokat használ — csak az anonim adatforrások engedélyeznek egyedi fejléceket. Ennek hozzáadása a frissítést a következő hibával meghiúsítja:

```text theme={null}
Expression.Error: The 'Living-Security-Version' header is only supported when
connecting anonymously.
Microsoft.Data.Mashup.ErrorCode = 10348.
```

Tartsa a kulcsot a Power BI tárolt hitelesítő adataiban, és hagyja el az egyedi fejlécet.
Ha a kérésben nincs explicit verzió, az API a szolgáltatásfiók rögzített `contract_version` verzióját használja, rögzítés hiányában pedig a legújabb publikált verziót.
A frissítés publikálása előtt egyeztesse a fiók szerződésverzióját az API-adminisztrátorral. A támogatott verziókat lásd az [API változásnaplóban](/hu/developers/v1/changelog).

Ha a frissítés váratlanul kezd viselkedni, ellenőrizze az API
`Living-Security-Version` válaszfejlécét, hogy melyik szerződés szolgálta ki ténylegesen a kérést.

## A modell frissítése és ellenőrzése

Tegye közzé a szemantikai modellt a Power BI munkaterületén, kösse a tárolt hitelesítő adatát a regionális `/v1` forráshoz, és az ütemezés engedélyezése előtt futtasson egy frissítést. Nézze át a frissítési előzményeket hibák vagy hiányos olvasások szempontjából.

Vesse össze a sorszámokat egy független, azonos szűrőkkel végzett API-olvasással.
A [lapozási útmutató](/hu/developers/v1/pagination) Python- és cURL-példákat tartalmaz a teljes eredményhalmaz olvasásához. Az a frissítés, amely csak az első oldalt olvassa, kis adathalmazon sikeresnek tűnhet; a riportra támaszkodás előtt hasonlítsa össze a darabszámokat.

## Modellezési döntések

* **A kampányösszesítők és a személyenkénti eredmények szemcsézettsége eltér.**
  A `SimulationResults` a `GET /v1/simulations/{id}/results` végpontot hívja
  (egy GET futtatásonként). A személyenkénti phishinghez adjon hozzá lekérdezéseket a
  `GET /v1/reporting/simulation_results` és a
  `GET /v1/reporting/simulation_events` végponthoz (vagy az operatív
  `GET /v1/simulation_results` végponthoz, ha már rendelkezik `simulation:read` jogosultsággal).
* **A `vectors` a `/v1/simulations` objektumon vesszővel elválasztott karakterlánccá van lapítva.**
  Egy lista típusú oszlop nem szeletelhető valódi dimenzióként. Ha egy riportoldalnak vektor szerint kell szűrnie, adjon hozzá egy szimuláció–vektor hídtáblát.

## Következő lépések

<CardGroup cols={2}>
  <Card title="Developer Hub" href="/hu/developers/developer-hub/overview">
    Hozzon létre szolgáltatásfiókot és API-kulcsot a Power BI-hoz.
  </Card>

  <Card title="API gyorsindító" href="/hu/developers/v1/quickstart">
    A `/v1` kérések hitelesítése Basic hitelesítéssel.
  </Card>

  <Card title="Lapozás" href="/hu/developers/v1/pagination">
    A listaboríték, a kurzortokenek és a szűrők stabilitása egy bejáráson belül.
  </Card>

  <Card title="Sebességkorlátok" href="/hu/developers/v1/rate-limits">
    Kulcsonkénti és IP-címenkénti keretek, a 429-es szerződés és az újrapróbálkozási útmutató.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.