# Taken exporteren

> Exporteer taken en beoordelingen uit de Tidal Control API met activities_paged en een activityTypes-filter, met inline fragments voor beoordelingsvelden.

Source: https://tidalcontrol.com/nl/docs/api/tasks

Taken (executions) en beoordelingen (assessments) gebruik je allebei via de `activities_paged`-query met een `activityTypes`-filter. Beide typen delen de `Activity`-interface, die alle gemeenschappelijke velden biedt. Gebruik een inline fragment (`... on Assessment`) om beoordelingsspecifieke velden zoals `effectiveness` op te vragen.

## Basisquery: taken (executions)

```graphql
query ExportTasks($first: Int, $after: String, $filter: ActivityFilter) {
  activities_paged(first: $first, after: $after, filter: $filter) {
    edges {
      node {
        id
        sequenceId
        name
        description
        notBefore
        expires
        closedDate
        reviewedDate
        createdDate
        plan {
          id
          name
        }
        controls {
          id
          sequenceId
          name
        }
        assets {
          id
          name
        }
        assignments {
          assignmentType
          user {
            name
            email
          }
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

Variabelen voor de eerste pagina van taken:

```json
{ "first": 50, "after": null, "filter": { "activityTypes": ["EXECUTE"] } }
```

## Belangrijke velden

- `sequenceId`: het leesbare nummer zoals getoond in de UI
- `closedDate`: ingesteld wanneer de taak voltooid is; `null` als nog open
- `reviewedDate`: ingesteld wanneer de taak beoordeeld is door een reviewer
- `plan`: het Plan dat deze taak heeft gegenereerd

## Volledig exportscript (Python)

Dit script authenticeert, doorloopt alle pagina's en schrijft het resultaat naar een CSV-bestand:

```python

GRAPHQL_URL = "https://portal.tidalcontrol.com/graphql"

QUERY = """
query ExportTasks($first: Int, $after: String, $filter: ActivityFilter) {
  activities_paged(first: $first, after: $after, filter: $filter) {
    edges {
      node {
        id
        sequenceId
        name
        description
        notBefore
        expires
        closedDate
        reviewedDate
        createdDate
        plan { id name }
        controls { id sequenceId name }
        assets { id name }
        assignments { assignmentType user { name email } }
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
"""

def graphql(query, variables, access_token):
    r = requests.post(
        GRAPHQL_URL,
        json={"query": query, "variables": variables},
        headers={"Authorization": f"Bearer {access_token}"},
    )
    r.raise_for_status()
    result = r.json()
    if "errors" in result:
        raise RuntimeError(result["errors"])
    return result["data"]

def export_tasks(access_token, closed=None, assignee_ids=None):
    tasks = []
    cursor = None
    task_filter = {"activityTypes": ["EXECUTE"]}
    if closed is not None:
        task_filter["closed"] = closed
    if assignee_ids:
        task_filter["assignees"] = assignee_ids

    while True:
        data = graphql(
            QUERY,
            {"first": 50, "after": cursor, "filter": task_filter},
            access_token,
        )
        page = data["activities_paged"]
        tasks.extend(edge["node"] for edge in page["edges"])

        if not page["pageInfo"]["hasNextPage"]:
            break
        cursor = page["pageInfo"]["endCursor"]

    return tasks

def to_csv(tasks, output_path):
    if not tasks:
        print("Geen taken gevonden.")
        return

    with open(output_path, "w", newline="", encoding="utf-8") as f:
        writer = csv.writer(f)
        writer.writerow([
            "ID", "Volgnummer", "Naam", "Omschrijving",
            "Startdatum", "Vervaldatum", "Aanmaakdatum",
            "Sluitdatum", "Beoordelingsdatum",
            "Plan", "Gekoppelde maatregelen", "Gekoppelde middelen",
            "Eigenaren", "Uitvoerders",
        ])
        for t in tasks:
            owners = [
                a["user"]["email"]
                for a in t["assignments"]
                if a["assignmentType"] == "OWNER"
            ]
            executors = [
                a["user"]["email"]
                for a in t["assignments"]
                if a["assignmentType"] == "EXECUTOR"
            ]
            writer.writerow([
                t["id"],
                t["sequenceId"],
                t["name"],
                t.get("description", ""),
                t.get("notBefore", ""),
                t.get("expires", ""),
                t.get("createdDate", ""),
                t.get("closedDate", "") or "",
                t.get("reviewedDate", "") or "",
                t["plan"]["name"] if t.get("plan") else "",
                ", ".join(c["name"] for c in t["controls"]),
                ", ".join(a["name"] for a in t["assets"]),
                ", ".join(owners),
                ", ".join(executors),
            ])

    print(f"{len(tasks)} taken geëxporteerd naar {output_path}")

if __name__ == "__main__":
    token = get_token()
    access_token = token["access_token"]

    tasks = export_tasks(access_token, closed=False)
    to_csv(tasks, "taken_export.csv")
```

## Filtervoorbeelden

Geef een `filter` mee om de export te beperken. Dit zijn de combinaties waar het meest om gevraagd wordt:

**Alleen open taken:**

```json
{ "filter": { "activityTypes": ["EXECUTE"], "closed": false } }
```

**Verlopen taken:**

```json
{ "filter": { "activityTypes": ["EXECUTE"], "status": ["OVERDUE"] } }
```

**Taken die binnenkort vervallen:**

```json
{ "filter": { "activityTypes": ["EXECUTE"], "status": ["DUE_SOON"] } }
```

**Taken gekoppeld aan een specifieke maatregel:**

```json
{ "filter": { "activityTypes": ["EXECUTE"], "controls": ["control-uuid-hier"] } }
```

**Taken toegewezen aan een specifieke gebruiker:**

```json
{ "filter": { "activityTypes": ["EXECUTE"], "assignees": ["gebruiker-uuid-hier"] } }
```

## Beoordelingen exporteren

Beoordelingen gebruik je via dezelfde `activities_paged`-query met `activityTypes: [ASSESS]`. Gebruik het `... on Assessment`-inline fragment om het veld `effectiveness` op te vragen, dat specifiek is voor beoordelingen.

```graphql
query ExportAssessments($first: Int, $after: String, $filter: ActivityFilter) {
  activities_paged(first: $first, after: $after, filter: $filter) {
    edges {
      node {
        id
        sequenceId
        name
        closedDate
        createdDate
        controls { id name }
        assets { id name }
        assignments { assignmentType user { name email } }
        ... on Assessment {
          effectiveness
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

Variabelen:

```json
{ "first": 50, "after": null, "filter": { "activityTypes": ["ASSESS"] } }
```

Het veld `effectiveness` op beoordelingen is: `UNDETERMINED`, `INEFFECTIVE` of `EFFECTIVE`.

## Alleen tellen

Om te controleren hoeveel taken er zijn voordat je de volledige set exporteert:

```graphql
query {
  taken_open: activities_count(filter: { closed: false, activityTypes: [EXECUTE] })
  taken_gesloten: activities_count(filter: { closed: true, activityTypes: [EXECUTE] })
  beoordelingen_open: activities_count(filter: { closed: false, activityTypes: [ASSESS] })
  beoordelingen_gesloten: activities_count(filter: { closed: true, activityTypes: [ASSESS] })
}
```
