#!/usr/bin/env python3
"""
Exporte les taches Mobility Work dans un Excel : Equipment id, Statut (format web
app), assignes, tags, temps planifie/arret/duree, dates. Pagination + dedoublonnage.

Usage :
    $env:MW_API_KEY="ta_cle_api"
    python export_taches_jusqu_aujourdhui.py
"""

import argparse
import json
import os
import sys
from datetime import datetime, timezone

import requests
from openpyxl import Workbook

API_BASE = "https://[Log in to view URL]"
ENDPOINT = "/partners/v1/tasks/search"
NETWORK_NAME = "MonReseau"
PAGE_SIZE = 100
OUTPUT = "export_taches_jusqu_aujourdhui.xlsx"

NOW = datetime.now(timezone.utc)

SCHEDULED_FROM = None               # ex : "2025-01-01T00:00:00+00:00" (UTC)
SCHEDULED_TO = NOW.strftime("%Y-%m-%dT23:59:59+00:00")   # jusqu'a aujourd'hui
TASK_STATES = None                  # planned / ongoing / completed / canceled


# --- Helpers d'extraction mis à jour selon le schéma réel ----------------
def assignees_text(t):
    """Extrait le prenom et le nom depuis la liste 'individuals'."""
    a = t.get("assignees")
    if not a:
        return None
    
    individus = a.get("individuals", [])
    noms = []
    for ind in individus:
        prenom = ind.get("firstName", "")
        nom = ind.get("lastName", "")
        nom_complet = f"{prenom} {nom}".strip()
        if nom_complet:
            noms.append(nom_complet)
            
    return " ; ".join(noms) if noms else None


def tags_text(t):
    """Extrait la valeur 'name' de chaque objet tag."""
    tags = t.get("tags", [])
    if not tags:
        return None
    return ", ".join(tag.get("name", "") for tag in tags if tag.get("name"))


def _dt(s):
    try:
        return datetime.fromisoformat(s) if s else None
    except ValueError:
        return None


def statut_webapp(t):
    state = t.get("taskState")
    if state == "canceled":
        return "Annulée"
    if state == "completed":
        return "Terminée"
    sched = t.get("schedule") or {}
    due = _dt(sched.get("to")) or _dt(sched.get("from"))
    en_retard = due is not None and due < NOW
    if state == "pending":
        return "En retard" if en_retard else "Planifiée"
    if state == "in_progress":
        return "En retard et commencée" if en_retard else "En cours"
    return state
# ---------------------------------------------------------------------------


def _headers(api_key):
    return {"Accept": "application/json", "Api-Key": api_key,
            "User-Agent": f"python-requests {NETWORK_NAME}"}


def fetch_all(api_key):
    params = {"page": 1, "size": PAGE_SIZE, "sort": "scheduledAt.desc"}
    if SCHEDULED_FROM:
        params["scheduledFrom"] = SCHEDULED_FROM
    if SCHEDULED_TO:
        params["scheduledTo"] = SCHEDULED_TO
    if TASK_STATES:
        params["taskStates"] = TASK_STATES

    items, seen, doublons = [], set(), 0
    while True:
        r = requests.get(API_BASE + ENDPOINT, headers=_headers(api_key), params=params, timeout=60)
        if r.status_code != 200:
            print(f"HTTP {r.status_code}\n{r.text}")
            sys.exit("Erreur lors de la recuperation des taches.")
        data = r.json()
        for t in data.get("_items", []):
            tid = t.get("taskId")
            if tid in seen:
                doublons += 1
                continue
            seen.add(tid)
            items.append(t)
        pg = data.get("_pagination", {})
        current, total = pg.get("currentPage", 1), pg.get("totalPageCount", 1)
        print(f"Page {current}/{total} - {len(items)} taches uniques")
        if current >= total:
            break
        params["page"] = current + 1
    return items, doublons


def row_from_task(t):
    assoc = t.get("associatedTo") or {}
    sched = t.get("schedule") or {}
    return [
        t.get("taskShortId"),
        t.get("taskId"),
        t.get("description"),
        t.get("taskState"),
        statut_webapp(t),
        assoc.get("name"),
        assoc.get("externalReference"),                # Equipment id (code article)
        assoc.get("costCenter"),
        assignees_text(t),                             # Assigné (Prénom Nom)
        tags_text(t),                                  # Tags (nom)
        t.get("plannedMaintenanceTimeInMinutes"),      # Temps de maintenance planifié (min)
        t.get("plannedStoppedTimeInMinutes"),          # Temps d'arrêt planifié (min) [Bonus]
        t.get("estimateDurationInMinutes"),            # Durée / Temps passé (min)
        sched.get("from"),
        sched.get("to"),
        t.get("createdAt"),                            # Tâche créée le
        t.get("completedAt"),                          # Terminée le
    ]


HEADER = [
    "Task short id", "Task id", "Description", "Etat", "Statut",
    "Equipement", "Equipment id", "Centre de couts", "Assigné", "Tags",
    "Temps de maintenance planifié (min)", "Temps d'arrêt planifié (min)", "Durée estimée / passée (min)",
    "Planifie du", "Planifie au", "Tâche créée le", "Termine le",
]


def main():
    ap = argparse.ArgumentParser()
    args = ap.parse_args()

    api_key = os.environ.get("MW_API_KEY")
    if not api_key:
        sys.exit("Definis ta cle : $env:MW_API_KEY='...'")

    tasks, doublons = fetch_all(api_key)
    wb = Workbook()
    ws = wb.active
    ws.title = "Taches"
    ws.append(HEADER)
    for t in tasks:
        ws.append(row_from_task(t))
    wb.save(OUTPUT)

    print(f"\n{len(tasks)} taches uniques ecrites dans {OUTPUT}")
    if doublons:
        print(f"{doublons} doublon(s) ecarte(s).")
    manquants = sum(1 for t in tasks if not (t.get("associatedTo") or {}).get("externalReference"))
    if manquants:
        print(f"Note : {manquants} tache(s) sans Equipment id (equipement archive / sans code article).")


if __name__ == "__main__":
    main()

Embed on website

To embed this project on your website, copy the following code and paste it into your website's HTML: