Eksport andmelattu
Analüütikud soovivad sageli hoida Sursa andmeid ettevõtte ülejäänud andmete kõrval: millised kaardid on kontrollitud ja kes need kontrollis, kuidas teadmised meeskondade kaupa kasvavad, millised küsimused jäävad vastuseta. See juhend loob ajastatud töö, mis kopeerib Sursa andmed teie andmelattu ja hoiab koopia ajakohasena.
Töö ühendab kolm API osa:
| Osa | Annab teile |
|---|---|
Kaartide loend parameetriga updated_since ja tervikuna loetavad lühikesed loendid | Pärast viimast käivitust muutunud objektide praeguse oleku. |
| Sündmuste lint | Kustutamised ja toimunu ajaloo 30 päeva kohta. |
| Kursorid | Täielikud lehed ilma lünkade ja kordusteta. |
Vajate võtit ulatustega cards:read ja users:read ning sündmuste lindi jaoks ulatust webhooks:manage. Andke
tööle oma võti rolliga admin: kasutajate täielik loend ja sündmuste lint on avatud ainult sellele. Kitsama rolli ja
andmeprofiiliga võti ekspordib ainult seda, mida teatud rühm näha tohib; see loeb kasutajate lühiloendit
(brief=true) ega loe sündmuste linti, seega saab see kustutamised allpool kirjeldatud iganädalasest täielikust
laadimisest.
Tabelid
Lihtne ülesehitus, kus on üks tabel iga objekti jaoks ja üks tabel sündmuste jaoks:
| Tabel | Võti | Täidetakse päringuga |
|---|---|---|
sursa_cards | id | GET /v1/cards |
sursa_collections | id | GET /v1/collections |
sursa_users | id | GET /v1/users |
sursa_groups | id | GET /v1/groups |
sursa_events | id | GET /v1/webhook-events |
Salvestage iga objekt saabunud kujul JSON-veeruna ning lisaks veerud, mille järgi sageli päringuid teete
(updated_at, collection_id, status, owner_id). API hilisemates versioonides lisandub uusi välju; JSON-veeruga
saate need kätte tööd muutmata.
Esmane laadimine
Lugege iga loend täielikult, ilma parameetrita updated_since, ja kirjutage iga objekt. Jätke meelde hetk, mil
laadimine algas: järgmine käivitus jätkab sealt.
Inkrementaalsed laadimised
Igal käivitusel, näiteks iga 15 minuti järel:
- Jätke meelde selle käivituse algusaeg.
- Lugege kaarte parameetriga
updated_since, mille väärtus on eelmise käivituse algusaeg miinus minut varu, ja küsige iga järgmist lehte kursoriga ja sama parameetrigaupdated_since(kursor ilma esimese lehe filtriteta tagastab400 api.cursor_invalid). Lugege kogud, kasutajad ja rühmad tervikuna: kogudel ja kasutajatel ei ole väljaupdated_atning rühmaupdated_atei muutu, kui muutuvad selle liikmed. Looge või asendage iga objekt väljaidjärgi. - Lugege sündmuste linti eelmisel korral salvestatud kursorist alates. Lisage iga sündmus tabelisse
sursa_eventsja rakendage kustutamised: sündmuscard.deletedeemaldab kaardi tabelistsursa_cardsvõi märgib selle kustutatuks, kui säilitate ajalugu. - Salvestage algusaeg ja lindi uus kursor andmetega samas tehingus, kui teie andmeladu seda toetab.
import json
from datetime import datetime, timedelta, timezone
LISTS = {"cards": "/v1/cards", "collections": "/v1/collections", "users": "/v1/users", "groups": "/v1/groups"}
def read_list(path, since):
"""Every object of a list, changed since `since` (or all of them). The next page keeps the filters of the first."""
params = {"limit": 200}
if since:
params["updated_since"] = (since - timedelta(minutes=1)).isoformat()
while params:
response = session.get(BASE + path, params=params, timeout=120)
response.raise_for_status()
body = response.json()
items = body["data"] if isinstance(body, dict) and "data" in body else body
yield from items
if isinstance(body, dict) and "data" in body:
params = {**params, "cursor": body["next_cursor"]} if body.get("has_more") else None
else:
cursor = response.headers.get("X-Next-Cursor")
params = {**params, "cursor": cursor} if cursor else None
def read_events(cursor):
"""New events of the feed after `cursor` (or from the start of the 30 days), and the cursor to save."""
events = []
params = {"limit": 200, **({"cursor": cursor} if cursor else {})}
while True:
page = api("GET", "/v1/webhook-events?" + requests.compat.urlencode(params))
events += page["data"]
params["cursor"] = page["next_cursor"]
if not page["has_more"]:
return events, page["next_cursor"]
def run(state, warehouse):
started_at = datetime.now(timezone.utc)
since = state.get("since")
for table, path in LISTS.items():
for obj in read_list(path, since if table == "cards" else None):
warehouse.upsert(f"sursa_{table}", obj["id"], json.dumps(obj))
events, feed_cursor = read_events(state.get("feed_cursor"))
for event in events:
warehouse.insert_once("sursa_events", event["id"], json.dumps(event))
if event["type"] == "card.deleted":
warehouse.delete("sursa_cards", event["object"]["id"])
warehouse.save_state({"since": started_at, "feed_cursor": feed_cursor})Lindi kursor tagastatakse igal lehel, ka viimasel. Kui see salvestada, algab järgmine käivitus täpselt pärast viimast salvestatud sündmust, nii et ühtegi sündmust ei loeta kaks korda ega jäeta vahele.
Koopia täpsuse hoidmine
- Üle 30 päeva ilma käivituseta. Lint säilitab sündmusi 30 päeva. Kui töö ei ole kauem käivitunud, tehke uuesti täielik laadimine ja eemaldage oma tabelitest kõik objektid, mida täielik laadimine ei tagastanud.
- Iganädalane täielik laadimine. Isegi korras lindi puhul on kord nädalas tehtud ja teie tabelitega võrreldud täielik laadimine odav viis veenduda, et koopia on täielik.
- Mida võti ei näe, seda ei ekspordita. Töö näeb täpselt seda, mida selle võti näeb. Kui kaart jääb võtme juurdepääsust välja, näiteks seetõttu, et see teisaldati piiratud kogusse, ei tagasta täielik laadimine seda; käsitlege seda eemaldatuna.
- Küsimused. Küsimuste logil on oma eksport:
GET /v1/questions/export.csvtagastab perioodi küsimused ühe CSV-failina koos hinnangute ja allikatega. Selleks on vaja ulatustask.
Piirangute järgimine
Suure organisatsiooni täielik laadimine teeb palju päringuid. Lugege üht loendit korraga väärtusega limit=200 ja
jälgige päiseid RateLimit. Iga 15 minuti järel käivituv töö, mis loeb ainult
muudatusi, jääb igast kvoodist kaugele alla.