> ## 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

> Savienojiet Power BI ar Living Security Public API, izmantojot Power Query, izveidojiet zvaigznes shēmas modeli uz /v1 un izvēlieties operatīvās vai atskaišu datu kopas pikšķerēšanas datiem.

Šī rokasgrāmata ir pilnīgs piemērs, kā nolasīt `/v1` Power BI vidē. Tajā aprakstīts
Power Query (M) savienojamības modelis, uz `/v1/activities` veidots zvaigznes shēmas
semantiskais modelis, inkrementālā atsvaidzināšana un gadījumi, kad izmantot **reporting**
priekšskatījuma datu kopas, lai iegūtu pikšķerēšanas rezultātus katrai personai.

Piemēros izmantots ASV produkcijas resursdators
(`https://api.us-east-1.livingsecurity.ai/v1`). EU organizācijai izmantojiet
`https://api.eu-west-1.livingsecurity.ai/v1` gan `Fn_FetchAll`, gan
`Fn_GetJson`, pēc tam piesaistiet datu avota akreditācijas datus šim resursdatoram. Skatiet
[Regions](/lv/developers/v1/regions).

Šajā rokasgrāmatā tiek izmantoti saglabāti Basic akreditācijas dati ar API atslēgu. OAuth klients nav nepieciešams. Par
akreditācijas datu izvēli skatiet [Authentication](/lv/developers/v1/authentication), bet par piekļuvi un iestatīšanu — [Developer Hub](/lv/developers/developer-hub/overview).

## Kam paredzēts šis piemērs

Izmantojiet to, ja nepieciešams:

* ielādēt `/v1` saraksta galapunktus Power BI datu kopā ar pareizu kursora
  lappušu dalīšanu un atkārtotu mēģinājumu pēc ātruma ierobežojuma;
* izveidot semantisko modeli, kurā rādītāji (“active entities”, “activities per
  active entity”) ir dimensionāli, nevis saskaitīti no plakanas tabulas;
* pārslēgt plānoto Power BI atsvaidzināšanu no visas vēstures pilnas apstaigāšanas uz
  nelielu dienas logu, izmantojot inkrementālo atsvaidzināšanu;
* izvēlēties pareizo pikšķerēšanas granularitāti: kampaņu kopsummas (operatīvie dati) pretstatā
  rezultātiem katrai personai un tipizētiem notikumiem (reporting priekšskatījums).

## Priekšnosacījumi

* API atslēga ar atļaujām, kas nepieciešamas jūsu vaicājumiem:
  * Operatīvais piemērs: `activity:read`, `entity:read` un
    `simulation:read`.
  * Pikšķerēšana katrai personai, izmantojot reporting datu kopas: `reporting:read`.
    Skatiet [API keys](/lv/developers/developer-hub/api-keys).
* Power BI Desktop vai Power BI / Fabric darbvieta, kurā varat izveidot
  semantisko modeli un ieplānot atsvaidzināšanu.

## Izvēlieties pareizo granularitāti

`/v1` piedāvā divas savstarpēji saistītas virsmas pikšķerēšanai. Tās nav savstarpēji aizvietojamas.

| Nepieciešamība | Izmantot | Atļauja | Granularitāte |
| - | - | - | - |
| Kampaņas piltuves kopsummas | `GET /v1/simulations/{id}/results` | `simulation:read` | Viena rinda uz simulāciju (delivered, opened, clicked, …) |
| Rezultāti katrai personai (operatīvie) | `GET /v1/simulation_results` | `simulation:read` | Viena rinda uz entītiju × simulāciju |
| Rezultāti katrai personai (BI / reporting) | `GET /v1/reporting/simulation_results` | `reporting:read` | Tā pati granularitāte, BI lauku nosaukumi (`entity_id`, `simulation_id`), `updated_after` ūdenszīme |
| Tipizēti mijiedarbības fakti | `GET /v1/reporting/simulation_events` | `reporting:read` | Viens fakts uz mijiedarbību (`simulation.link_clicked`, …), `occurred_after` |
| Pašreizējā Workforce dimensija | `GET /v1/reporting/entities` | `reporting:read` | Viena pašreizējā rinda uz entītiju. Savienojiet pēc `reporting.simulation_results.entity_id = reporting.entities.id` |
| Pašreizējās entītiju versijas | `GET /v1/reporting/entity_versions` | `reporting:read` | Viens atvērts pašreizējais intervāls uz entītiju. Versiju vēstures tabulas nav |
| Pašreizējā Cohort dalība | `GET /v1/reporting/cohort_memberships` | `reporting:read` | Viens pašreizējais fakts uz `(cohort_id, entity_id)`. Dalības vēstures tabulas nav |
| Savienojuma mugurkauls visiem aktivitāšu veidiem | `GET /v1/activities` | `activity:read` | Viens notikums uz aktivitāti, ligzdots `participants[]` |

Tālāk sniegtie Power BI piemēru vaicājumi izsauc **operatīvos** maršrutus
(`/v1/simulations`, `/v1/simulations/{id}/results`, `/v1/activities`,
`/v1/entities`). Pikšķerēšanas atskaitēm katrai personai dodiet priekšroku
turpmāk aprakstītajai **reporting** priekšskatījuma saimei. Atslēga, kurai piešķirts tikai `reporting:read`, nevar
izsaukt operatīvo `/v1/simulation_results` (API atgriež `403`).

### Grūtības skala

Simulācijas grūtība ir vesels skaitlis no 1 līdz 5 šādās vietās: `/v1/simulations` (`difficulty_level`),
`/v1/simulation_results` (`difficulty_level`), `/v1/activities` īpašības
(`difficulty_tier`) un `/v1/reporting/simulation_events` (`difficulty_tier`).
`1` ir vieglākā un `5` ir grūtākā. Adaptīvajām un progresīvajām simulācijām nav
vienas kampaņas līmeņa vērtības, tāpēc `/v1/simulations.difficulty_level` ir `null`,
kamēr rezultātiem joprojām var būt iestatīts katra saņēmēja `difficulty_level`. Lai izveidotu atbilstību
vecajiem low/medium/high eksportiem, `1–2` uzskatiet par low, `3` par medium un `4–5` par
high.

<Warning>
  Neveidojiet jaunas BI integrācijas, pamatojoties uz nedokumentētiem Dashboard tīkla izsaukumiem
  vai vecāko kurēto `/api/v1` Public API pārskatu. Autentificējieties
  organizācijas reģionālajā `/v1` resursdatorā ar Developer Hub API atslēgu. Skatiet
  [Regions](/lv/developers/v1/regions).
</Warning>

## Savienojamības modelis (Power Query)

Katram piemēra vaicājumam pamatā ir četri noteikumi. Katru no tiem nosaka
Power BI Service, nevis stila preferences.

<AccordionGroup>
  <Accordion title="Saglabājiet Web.Contents bāzes URL statisku un beidziet to ar /v1">
    Visu dinamisko ievietojiet `RelativePath` un `Query`. Pirmajam argumentam
    `Web.Contents` jābūt burtiskai virknei, kas beidzas organizācijas
    reģionālās `/v1` saknē:

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

    Izsaucēji pēc tam nodod tikai resursu (`"simulations"`, nevis
    `"v1/simulations"`). Neievietojiet ASV resursdatoru EU organizācijai.

    Ja pirmais arguments tiek veidots ar virkņu konkatenāciju, Power BI Service
    nevar statiski identificēt datu avotu un plānotā atsvaidzināšana neizdodas ar
    kļūdu “dynamic data source”, lai gan vaicājums darbojas Power BI
    Desktop.
  </Accordion>

  <Accordion title="Norādiet API atslēgu kā saglabātus Basic akreditācijas datus, nevis vaicājuma tekstā">
    Kad Power BI pieprasa akreditācijas datus `/v1` datu avotam, izvēlieties
    **Basic** un ielīmējiet API atslēgu laukā **username**. Laukā **password** ievietojiet vietturi,
    piemēram, `x`. Power BI Service noraida tukšu
    paroli, lai gan API šo lauku ignorē. Darbojas arī atslēga kā parole ar
    vietturi kā lietotājvārdu.

    Nepievienojiet `Authorization` galveni M kodā. Tā darbojas jūsu darbvirsmā, taču pēc tam
    iekodē atslēgu vaicājuma failā un traucē plānotajai atsvaidzināšanai.
  </Accordion>

  <Accordion title="Ļaujiet akreditācijas datu dialoga savienojuma testam izpildīties">
    `GET /v1` ir autentificēts: ar derīgu atslēgu tas atgriež `200` un `401`
    bez tās. Norādiet datu avotu uz `…/v1` un **nepalaidiet garām** testu —
    derīga atslēga tiek saglabāta, atsaukta vai nederīga atslēga uzreiz neizdodas.

    Vecākos piemēros kā datu avots tika izmantots tikai resursdators un tika norādīts izlaist
    testu, jo `GET /` vienmēr ir `404`. Pārvietojiet šos modeļus uz `…/v1`.
  </Accordion>

  <Accordion title="Katrā saraksta vaicājumā skaidri iestatiet limit=500">
    Noklusējuma lappuses izmērs katram `/v1` saraksta galapunktam ir 25. Atstājot noklusējumu,
    tiem pašiem datiem tiek reizināts pieprasījumu skaits. Vaicājuma vērtībām jābūt tekstam —
    `limit = "500"` M kodā, nevis `limit = 500`.
  </Accordion>
</AccordionGroup>

### Kopīgā lappušu dalīšanas funkcija

Katrs piemēra vaicājums izsauc vienu `Fn_FetchAll`, kas apstaigā
`next_page_token`, līdz tas ir `null` (skatiet
[pagination](/lv/developers/v1/pagination)). Tās būtiskākās īpašības:

* **Statisks `/v1` bāzes URL, dinamiski `RelativePath` un `Query`.** Nepieciešams
  plānotajai atsvaidzināšanai un akreditācijas datu testam.
* **`ManualStatusHandling = {429}` ar ierobežotu atkārtotu mēģinājumu skaitu.** Funkcija nolasa
  `Retry-After`, kad Power Query to atklāj, pretējā gadījumā gaida 30 sekundes un
  pēc 5 secīgām `429` atbildēm pārtrauc darbu ar skaidru kļūdu, nevis
  griežas ciklā, līdz atsvaidzināšanai iestājas noildze. `5xx` apzināti netiek
  mēģināts atkārtoti — servera kļūdai jābūt redzamai, nevis jārada
  nepilnīga datu kopa.
* **60 sekunžu `Timeout`.** Lappuses parasti atbild krietni īsāk par sekundi, tāpēc
  minūti ilgs klusums nozīmē, ka pieprasījums ir miris.
* **`Binary.Buffer` pirms `Json.Document`.** Bez buferizācijas mashup
  dzinējs var atkārtoti pieprasīt atbildes straumi, ja to nolasa vairāk nekā vienu reizi.
* **`baseQuery` katrā iterācijā tiek nodots nemainīts.** Lappuses tokeni ir
  necaurredzami, parakstīti kursori, kas piesaistīti organizācijai, akreditācijas datiem, līguma
  versijai, kā arī filtru un kārtošanas pirkstu nospiedumam. Filtra maiņa
  apstaigāšanas laikā tiek noraidīta kā nederīgs tokens, nevis sākta no
  pirmās lappuses.

```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
```

### Tipizēti vaicājumi katram galapunktam

Katrs `Fn_FetchAll` patērētājs ievēro vienu un to pašu shēmu: izsauc galapunktu,
projicē fiksētu kolonnu sarakstu, noņem `object` diskriminatoru un
tipizē laikspiedolus kā `datetimezone`, lai datumu rādītāji nepamanīti nenobīdītos
atkarībā no atsvaidzināšanas jaudas laika joslas.

<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>
  Skaidrs kolonnu saraksts `Table.FromRecords` nodrošina stabilu shēmu, kad
  galapunkts atgriež nulli rindu. Tukšs `Table.FromRecords({})` rada
  tabulu bez kolonnām, un visi nākamie soļi neizdodas trūkstošo lauku dēļ.
</Tip>

## Excel

Tas pats Power Query (M) darbojas Excel sadaļā Get & Transform — tas ir tas pats
dzinējs, kas Power BI Desktop. Izmantojiet to, ja vēlaties darblapas tabulu,
nevis semantisko modeli.

Excel for Windows (Microsoft 365, Excel 2024, 2021, 2019 vai 2016):

1. **Data → Get Data → From Other Sources → Blank Query.**
2. Power Query Editor izvēlieties **Home → Advanced Editor**. Vispirms ielīmējiet
   `Fn_FetchAll`, pēc tam pārdēvējiet šo vaicājumu par `Fn_FetchAll` (Query
   Settings → Name vai ar peles labo pogu Queries rūtī → **Rename**).
3. Atkārtojiet darbības ar `Fn_GetJson`, pēc tam ar katru atskaites vaicājumu.
4. **Home → Close & Load → Close & Load To…** atver dialogu **Import Data**.
   Attiecībā uz `Fn_FetchAll` un
   `Fn_GetJson` izvēlieties **Only Create Connection** (tās ir funkcijas, nevis tabulas). Katram
   atskaites vaicājumam, kuru vēlaties darblapā, izvēlieties **Table**.
5. Pirmajā atsvaidzināšanā izvēlieties **Basic**: API atslēgu kā **User name**, vietturi,
   piemēram, `x`, kā **Password**. Excel saglabā vienus akreditācijas datus
   katram datu avota URL; nākamie vaicājumi, kas izmanto to pašu `/v1` resursdatoru, tos izmanto atkārtoti.

<Note>
  **Close & Load To…** var būt pelēks, kad darbgrāmatā pirmoreiz izveidojat vaicājumu.
  Izmantojiet **Close & Load**, pēc tam programmā Excel izvēlieties **Data →
  Queries & Connections**, ar peles labo pogu noklikšķiniet uz vaicājuma un izvēlieties **Load To**.
  Microsoft šo ceļu dokumentē tieši šādam gadījumam.
</Note>

No Power Query autentificētā avotā nevar nosūtīt `Living-Security-Version` —
tāds pats ierobežojums kā Power BI. Piesaistiet servisa kontu vai nododiet `api_version` vaicājuma ierakstā. Skatiet
[Contract versions and stored credentials](#līguma-versijas-un-saglabātie-akreditācijas-dati).

Gatavi atskaišu vaicājumi (kolonnu atbilstības un atšķirības no iepriekšējā
nodrošinātāja eksporta):

<CardGroup cols={3}>
  <Card title="Per-person compliance" href="/lv/developers/v1/reports/per-person-compliance">
    Training reģistrācijas personas × uzdevuma granularitātē.
  </Card>

  <Card title="Email phishing log" href="/lv/developers/v1/reports/email-phishing-log">
    Notikumu līmeņa pikšķerēšanas mijiedarbības pēdējos 12 mēnešos.
  </Card>

  <Card title="Most susceptible users" href="/lv/developers/v1/reports/most-susceptible-users">
    Katras personas 12 mēnešu klikšķu, akreditācijas datu un ziņošanas kopsavilkums.
  </Card>
</CardGroup>

## Reporting priekšskatījuma datu kopas (pikšķerēšana katrai personai)

Reporting priekšskatījuma saime nodrošina datu kopas pikšķerēšanai katrai personai.
Tai nepieciešams `reporting:read` un reporting piekļuve jūsu organizācijai.
Izmantojiet tālāk norādīto datu kopu katalogu, lai pārbaudītu to shēmas un datu politikas.

Atklājiet aktīvo katalogu:

```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
```

Katra datu kopa deklarē granularitāti, primāro atslēgu, savienojumus, ūdenszīmi, labojumu un
dzēšanas politiku.

### simulation\_results

* **Path:** `GET /v1/reporting/simulation_results`
* **Grain:** entity × simulation
* **Filters:** `simulation`, `entity`, `updated_after`, kā arī `limit` /
  `page_token`
* **Wire object:** `reporting.simulation_result`
* **Join keys:** `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
```

Izmantojiet `updated_after` kā ieskaitošu apakšējo ūdenszīmi, kad atsvaidzināt nesenās
partīcijas. Mainīgās rezultātu dimensijas tiek pārrakstītas uz vietas ar to pašu `id`.

### simulation\_events

* **Path:** `GET /v1/reporting/simulation_events`
* **Grain:** viens tipizēts fakts uz novērotu mijiedarbību
* **Filters:** `simulation`, `entity`, `event_type`, `occurred_after`, kā arī
  `limit` / `page_token`
* **Wire object:** `reporting.simulation_event`
* **Event types:** slēgti publiski tokeni, piemēram, `simulation.delivered`,
  `simulation.opened`, `simulation.link_clicked`,
  `simulation.credentials_submitted`, `simulation.reported` — nevis neapstrādāti
  ClickHouse `phishing.simulation.*` aktivitāšu veidi
* **Device:** `device_type` ir atvasinātā klase (novērotie tokeni ir
  `desktop`, `mobile`, `tablet`, `unknown`). Neapstrādāts `user_agent` nav pieejams
  `/v1`. Neapstrādāta IP adrese nekad nav pieejama `/v1` (nekad netiek saglabāta; tikai ar sāli apstrādāts `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
```

Izmantojiet `occurred_after` inkrementālai notikumu ielādei. Fakti ir tikai papildināmi;
vēlu saņemtie dati ir jaunas rindas.

### entities

* **Path:** `GET /v1/reporting/entities`
* **Grain:** viena pašreizējā rinda uz entītiju
* **Filters:** `type`, `updated_after`, kā arī `limit` / `page_token`
* **Wire object:** `reporting.entity`
* **Join key:** `id` / `entity_id` (tā pati vērtība)

Savienojiet pikšķerēšanas faktus ar personām, izmantojot
`reporting.simulation_results.entity_id = reporting.entities.id`.
Operatīvais `GET /v1/entities` paliek uz `entity:read`, un tā ir cita
atļauja.

```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 un cohort\_memberships

Šīm divām datu kopām ir nosaukts izņēmums — tikai pašreizējie dati:

> Nepastāv autoritatīva versiju vai dalības vēstures tabula. Šī datu kopa projicē pašreizējo rindu kā vienu atvērtu intervālu (`valid_to` ir null, `is_current` ir true). `valid_from` ir `created_at` vai citādi `updated_at` entītiju versijām un pieprasījuma novērošanas laiks Cohort dalībām. Slēgti vēsturiski intervāli netiek safabricēti.

* **entity\_versions:** `GET /v1/reporting/entity_versions`. Tādi paši filtri kā
  entities. `valid_from` ir `created_at` vai citādi `updated_at`. Entītija,
  kurai abi laikspiedoli ir null, tiek izlaista.
* **cohort\_memberships:** `GET /v1/reporting/cohort_memberships`. Filtri:
  `cohort`, `entity`, `updated_after`. `updated_at` ir Cohort pēdējais
  atjauninājums, lai inkrementālā atsvaidzināšana paliktu precīza. `valid_from` ir pieprasījuma
  novērošanas laiks. Savienojiet `entity_id` ar `reporting.entities.id`.

```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
```

## Zvaigznes shēmas semantiskais modelis uz /v1/activities

`/v1/activities` ir organizācijas notikumu straume ar laikspiedoliem. Tai nav
skaitlisku rādītāju — katrs atskaites lielums ir rindu skaits —, tāpēc
zvaigznes shēma ir dabiska izvēle.

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

* **`Activities`** ir notikumu fakts patiesajā granularitātē. Viena rinda uz aktivitāti.
  Dalībnieki šeit tiek izņemti un pārbūvēti kā sava tabula.
* **`ActivityParticipants`** pastāv tāpēc, ka dalībnieki patiesi ir
  “viens pret daudziem”, ar lomām, piemēram, `actor` un `target`. Saplacināšana uz
  `Activities` vai nu dublētu aktivitāšu rindas (uzpūšot katru skaitli),
  vai arī zaudētu papildu dalībniekus.
  Attiecība ar `Activities` ir vienīgā modeļa divvirzienu
  attiecība, tāpēc entītiju griezums sasniedz faktu tabulu.
* **`Entities`** ir dimensija, kas nosauc dalībniekus. Dalībnieku rindas
  aktivitātē satur `entity_id` un lomu, bet ne parādāmo vārdu.
* **`Dates`** tiek ģenerēta M kodā (nulle API izsaukumu) un atzīmēta kā modeļa
  datumu tabula. Tas padara likumīgu DAX laika intelektu
  (`DATESINPERIOD`, `DATEADD`). Mēneša un nedēļas dienas nosaukumu kolonnas deklarē
  `sortByColumn` uz savām skaitliskajām kolonnām, lai vizualizācijas nekārtotu mēnešus
  alfabētiski.

Visi rādītāji ir skaidri (modelī iestatīts `discourageImplicitMeasures`): kopējās
aktivitātes, aktīvās entītijas, aktivitātes uz aktīvo entītiju, dienas ar aktivitāti
un 7 dienu slīdošais dienas vidējais.

### Tilta tabulas struktūra

`ActivityParticipants` apstaigā `/v1/activities` un izvērš `participants`
masīvu līdz vienai rindai uz dalībnieku:

```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
```

`ActivityParticipants` apstaigā `/v1/activities` neatkarīgi, nevis
atsaucoties uz `Activities` vaicājumu. Power BI divi vaicājumi, kas ielādēti kā divas
tabulas, atsvaidzināšanas laikā katrs tiek nolasīts neatkarīgi. Neatkarība
ļauj pakalpojumam paralēli atsvaidzināt abu tabulu partīcijas.

## Inkrementālā atsvaidzināšana

Abas faktu granularitātes tabulas (`Activities` un `ActivityParticipants`) deklarē
Power BI inkrementālās atsvaidzināšanas politiku: glabāt 5 gadus gada partīciju,
katrā atsvaidzināšanā atkārtoti importēt pēdējās 14 dienas.

Mehānisms ir DateTime parametru pāris `RangeStart` un `RangeEnd`,
ko Power BI pakalpojums atsvaidzināšanas laikā pārsien katrai partīcijai. Katrs fakta
vaicājums šos parametrus ievieto API `start_date` un `end_date`
filtros, tāpēc pieprasījumu apjoms uz partīciju ir proporcionāls partīcijai,
nevis visai vēsturei. Vaicājums paplašina API logu par minūti katrā
pusē un lokāli atkārtoti piemēro precīzo pusatvērto `[RangeStart, RangeEnd)` robežu,
tāpēc partīcijas pareizība nav atkarīga no tā, vai serveris
savas robežas uzskata par ieskaitošām vai izslēdzošām.

```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
```

Pirmā atsvaidzināšana pēc izvietošanas piemēro politiku: pilna vēsturiskā ielāde,
lēna. Katra nākamā atsvaidzināšana skar tikai inkrementālo logu.

Mērīts datu kopā ar \~158 000 aktivitāšu un \~2 300 entītiju:

| Atsvaidzināšana | Sienas laiks |
| - | - |
| Pirmā (politikas piemērošana, pilna vēsturiskā ielāde) | \~31 min |
| Katra nākamā | **\~15 s** |

<Warning>
  Power BI klasiskais atsvaidzināšanas REST galapunkts (`POST .../refreshes` tikai ar
  `notifyOption`) klusējot ignorē atsvaidzināšanas politikas un katru reizi veic pilnu apstaigāšanu.
  Lai politiku piemērotu, izmantojot REST API, izmantojiet uzlaboto atsvaidzināšanu: pieprasījuma pamatā nosūtiet
  `type`, `commitMode` un `applyRefreshPolicy`. Atsvaidzināšanas,
  ko aktivizē Power BI Service plānotājs, vienmēr piemēro politikas — šī
  īpatnība attiecas tikai uz REST izsaukumu.
</Warning>

<Note>
  14 dienu logs ir apzināti devīgs. Aktivitāšu uzņemšana nav tikai
  papildinoša “tagad”: savienotāju aizpildīšana ienes notikumus ar pagātnes laikspiedoliem.
  1–2 dienu logs tos neatgriezeniski palaistu garām.
</Note>

## Atsvaidzināšanas loga paplašināšana uz API

`GET /v1/activities` pieņem `start_date` un `end_date` filtrus, kuros
Power BI parametri tiek ievietoti, filtrējot `event_ts` starp robežām.
`Entities` ir dimensija, tāpēc šajā piemērā tā katru reizi tiek atsvaidzināta pilnībā,
nevis inkrementāli.

Reporting datu kopas izmanto savas ūdenszīmes (`updated_after`,
`occurred_after`), nevis aktivitāšu datumu logu.

## Ātruma ierobežojumi atsvaidzināšanas laikā

`/v1` piemēro atsevišķus budžetus katrai atslēgai un katrai IP adresei (skatiet
[rate limits](/lv/developers/v1/rate-limits)). Ievērojiet `429` + `Retry-After`;
neiekodējiet pieprasījumu budžetu savā modelī. Svarīgas ir divas Power BI
atsvaidzināšanas īpašības:

* **Vienas tabulas apstaigāšana ir secīga.** Katram lappuses pieprasījumam nepieciešams
  iepriekšējās lappuses tokens, tāpēc viena tabula ir atkarīga no latentuma neatkarīgi no lappuses
  izmēra.
* **Vienlaicīgas tabulu atsvaidzināšanas reizinās slodzi.** Vairākas lielas tabulas, kas atsvaidzinās
  paralēli, ir gadījums, kad tiek sasniegta robeža, tāpēc 429
  atkārtošanas ceļš `Fn_FetchAll` ir svarīgs. Dodiet priekšroku mazākam paralēlo tabulu skaitam,
  nevis pieņemiet neierobežotu vienlaicīgumu.

Atkārtošana ir pārbaudīta pret īstu piesātinātu ierobežotāju. Atsvaidzināšana,
kas noritēja, kamēr paralēla slodze apzināti noturēja atslēgu pie tās maksimuma,
tomēr tika pabeigta un radīja tādu pašu rindu skaitu kā nekonkurējošs references gadījums —
atkārtotie mēģinājumi atgrieza pilnas lappuses, nevis caurumus.

## Līguma versijas un saglabātie akreditācijas dati

`/v1` pieņem `Living-Security-Version` pieprasījuma galveni, lai piesaistītu publicētu
līguma versiju (skatiet [API quickstart](/lv/developers/v1/quickstart)). Power
Query nevar nosūtīt šo galveni, ja datu avots izmanto saglabātus akreditācijas datus —
pielāgotas galvenes atļauj tikai anonīmi datu avoti. Tās pievienošana izraisa atsvaidzināšanas kļūdu:

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

Glabājiet atslēgu Power BI saglabātajos akreditācijas datos un izlaidiet pielāgoto galveni.
Ja pieprasījumā nav skaidras versijas, API izmanto servisa konta
piesaistīto `contract_version` vai jaunāko publicēto versiju, ja piesaiste nav iestatīta.
Pirms atsvaidzināšanas publicēšanas pie sava API administratora apstipriniet konta līguma versiju. Atbalstītās versijas skatiet
[API changelog](/lv/developers/v1/changelog).

Ja atsvaidzināšana sāk darboties negaidīti, pārbaudiet API
`Living-Security-Version` atbildes galveni, lai redzētu, kurš līgums
faktiski apkalpoja pieprasījumu.

## Modeļa atsvaidzināšana un pārbaude

Publicējiet semantisko modeli savā Power BI darbvietā, piesaistiet tā saglabātos
akreditācijas datus reģionālajam `/v1` avotam un pirms grafika iespējošanas veiciet atsvaidzināšanu.
Pārskatiet atsvaidzināšanas vēsturi, meklējot kļūmes vai nepilnīgas nolasīšanas.

Salīdziniet rindu skaitu ar neatkarīgu API nolasījumu, izmantojot tos pašus filtrus.
[Pagination guide](/lv/developers/v1/pagination) ietver Python un cURL
piemērus pilnas rezultātu kopas nolasīšanai. Atsvaidzināšana, kas nolasa tikai
pirmo lappusi, nelielā datu kopā var izskatīties veiksmīga; pirms paļaušanās uz atskaiti salīdziniet skaitļus.

## Modeļa izvēles

* **Kampaņu kopsummām un rezultātiem katrai personai ir dažādas granularitātes.**
  `SimulationResults` izsauc `GET /v1/simulations/{id}/results`
  (viens GET uz izpildi). Pikšķerēšanai katrai personai pievienojiet vaicājumus
  `GET /v1/reporting/simulation_results` un
  `GET /v1/reporting/simulation_events` (vai operatīvajam
  `GET /v1/simulation_results`, ja jums jau ir `simulation:read`).
* **`vectors` `/v1/simulations` tiek saplacināts līdz ar komatiem atdalītai virknei.**
  Saraksta tipa kolonnu nevar griezt kā īstu dimensiju. Ja atskaites lapai
  nepieciešams filtrēt pēc vektora, pievienojiet simulācijas–vektora tilta tabulu.

## Nākamie soļi

<CardGroup cols={2}>
  <Card title="Developer Hub" href="/lv/developers/developer-hub/overview">
    Izveidojiet servisa kontu un API atslēgu Power BI.
  </Card>

  <Card title="API quickstart" href="/lv/developers/v1/quickstart">
    Autentificējiet `/v1` pieprasījumus ar Basic autentifikāciju.
  </Card>

  <Card title="Pagination" href="/lv/developers/v1/pagination">
    Saraksta apvalks, kursora tokeni un filtru stabilitāte apstaigāšanas laikā.
  </Card>

  <Card title="Rate limits" href="/lv/developers/v1/rate-limits">
    Budžeti katrai atslēgai un katrai IP adresei, 429 līgums un norādījumi par atkārtošanu.
  </Card>
</CardGroup>


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