From b1992c075335b7e2a38f937b8a0e65a62bce79e7 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Tue, 21 Oct 2025 20:38:36 +0200 Subject: [PATCH] llamadas a la api creadas --- .../session/conectarse_api_kanboard.py.json | 281 +++++++++ .../control_kanban_mediante_api.py.json | 77 +++ conectarse_api_kanboard.py | 578 ++++++++++++++++++ 3 files changed, 936 insertions(+) create mode 100644 __marimo__/session/conectarse_api_kanboard.py.json create mode 100644 __marimo__/session/control_kanban_mediante_api.py.json create mode 100644 conectarse_api_kanboard.py diff --git a/__marimo__/session/conectarse_api_kanboard.py.json b/__marimo__/session/conectarse_api_kanboard.py.json new file mode 100644 index 0000000..b08d07d --- /dev/null +++ b/__marimo__/session/conectarse_api_kanboard.py.json @@ -0,0 +1,281 @@ +{ + "version": "1", + "metadata": { + "marimo_version": "0.17.0" + }, + "cells": [ + { + "id": "Hbol", + "code_hash": "1d0db38904205bec4d6f6f6a1f6cec3e", + "outputs": [ + { + "type": "data", + "data": { + "text/plain": "" + } + } + ], + "console": [] + }, + { + "id": "mmzP", + "code_hash": "e53b82f6768020aad4940c56dd2fae07", + "outputs": [ + { + "type": "data", + "data": { + "text/html": "

Listar Proyectos

" + } + } + ], + "console": [] + }, + { + "id": "soEq", + "code_hash": "35bcfe34cc3625a28a154254259b76de", + "outputs": [ + { + "type": "data", + "data": { + "application/json": "{\"jsonrpc\": \"2.0\", \"result\": [{\"id\": 1, \"name\": \"CodeProyects\", \"is_active\": true, \"token\": \"\", \"last_modified\": 1761063849, \"is_public\": false, \"is_private\": false, \"description\": null, \"identifier\": \"\", \"start_date\": \"\", \"end_date\": \"\", \"owner_id\": 2, \"priority_default\": 0, \"priority_start\": 0, \"priority_end\": 3, \"email\": null, \"predefined_email_subjects\": null, \"per_swimlane_task_limits\": false, \"task_limit\": 0, \"enable_global_tags\": true, \"url\": {\"board\": \"http://localhost/board/1\", \"list\": \"http://localhost/list/1\"}}], \"id\": 1}" + } + } + ], + "console": [] + }, + { + "id": "COzz", + "code_hash": "8593497dfb93ffdc4d481231f22ff886", + "outputs": [ + { + "type": "data", + "data": { + "text/html": "

Listar usuarios

" + } + } + ], + "console": [] + }, + { + "id": "fRcd", + "code_hash": "036c95a347f6de634be1a5277fc99daa", + "outputs": [ + { + "type": "data", + "data": { + "text/plain": "" + } + } + ], + "console": [ + { + "type": "stream", + "name": "stdout", + "text": "\u2705 Usuarios encontrados:\nID 1 | Usuario: admin | Nombre: None\nID 2 | Usuario: Egutierrez | Nombre: Enmanuel\n" + } + ] + }, + { + "id": "ucix", + "code_hash": "80ced1b6d20bb63695c909cca312c33c", + "outputs": [ + { + "type": "data", + "data": { + "text/html": "

Listar tareas

" + } + } + ], + "console": [] + }, + { + "id": "fRXL", + "code_hash": "18ea3c992e655e4fb8f64b7d24a41bbb", + "outputs": [ + { + "type": "data", + "data": { + "text/plain": "" + } + } + ], + "console": [ + { + "type": "stream", + "name": "stdout", + "text": "Tarea ID 5: Otra tarea autom\u00e1tica con etiquetas\nTarea ID 6: Otra tarea autom\u00e1tica con etiquetas\nTarea ID 7: Otra tarea autom\u00e1tica con etiquetas\nTarea ID 8: Otra tarea autom\u00e1tica con etiquetas\nTarea ID 9: Tarea con maxima prioridad\nTarea ID 10: Tarea con maxima prioridad\n" + } + ] + }, + { + "id": "jMzO", + "code_hash": "808fe59b3702664ab0b7e4e4a920629a", + "outputs": [ + { + "type": "data", + "data": { + "text/html": "

Listar subtareas

" + } + } + ], + "console": [] + }, + { + "id": "qrHy", + "code_hash": "7a03102141b244eb5054544c39ec0feb", + "outputs": [ + { + "type": "data", + "data": { + "text/plain": "" + } + } + ], + "console": [] + }, + { + "id": "rkGP", + "code_hash": "ed4d72d20e904f8baad87c095b3d3471", + "outputs": [ + { + "type": "data", + "data": { + "text/html": "

Listar tareas columnas

" + } + } + ], + "console": [] + }, + { + "id": "HTUm", + "code_hash": "f8d752251cf677e701b0a7b1ed2cc1cc", + "outputs": [ + { + "type": "data", + "data": { + "text/plain": "" + } + } + ], + "console": [ + { + "type": "stream", + "name": "stdout", + "text": "\n\ud83d\udce6 Backlog (4 tareas):\n \u251c\u2500 [5] Otra tarea autom\u00e1tica con etiquetas\n \u251c\u2500 [8] Otra tarea autom\u00e1tica con etiquetas\n \u251c\u2500 [9] Tarea con maxima prioridad\n \u251c\u2500 [10] Tarea con maxima prioridad\n\n\ud83d\udce6 Ready (1 tareas):\n \u251c\u2500 [7] Otra tarea autom\u00e1tica con etiquetas\n\n\ud83d\udce6 Work in progress (1 tareas):\n \u251c\u2500 [6] Otra tarea autom\u00e1tica con etiquetas\n\n\ud83d\udce6 Done (0 tareas):\n" + } + ] + }, + { + "id": "DHdl", + "code_hash": "557d1157ae68f568602eeb5e5ed09c76", + "outputs": [ + { + "type": "data", + "data": { + "text/html": "

Mover tareas entre columnas

" + } + } + ], + "console": [] + }, + { + "id": "WhTX", + "code_hash": "f88d3bdcbdaf4bbbf6f070b536cd24d6", + "outputs": [ + { + "type": "data", + "data": { + "text/plain": "" + } + } + ], + "console": [ + { + "type": "stream", + "name": "stdout", + "text": "\u26a0\ufe0f No se pudo mover la tarea 6.\n" + } + ] + }, + { + "id": "DBDn", + "code_hash": null, + "outputs": [], + "console": [] + }, + { + "id": "LoIk", + "code_hash": "7590f79b2fa20f3da53fcbbaf31c3e70", + "outputs": [ + { + "type": "data", + "data": { + "text/html": "

Crear tarea

" + } + } + ], + "console": [] + }, + { + "id": "dbns", + "code_hash": "c3260268117f17ff51af0ebe7c5c413b", + "outputs": [ + { + "type": "data", + "data": { + "text/plain": "" + } + } + ], + "console": [ + { + "type": "stream", + "name": "stdout", + "text": "\u2705 Tarea creada correctamente (ID 10) en el proyecto 1\n" + }, + { + "type": "stream", + "name": "stdout", + "text": "\ud83c\udff7\ufe0f Etiquetas a\u00f1adidas: etl, prioridad-alta\n" + } + ] + }, + { + "id": "HDME", + "code_hash": "8c2e1134753947cea243b3a8e020383d", + "outputs": [ + { + "type": "data", + "data": { + "text/html": "

Editar tarea

" + } + } + ], + "console": [] + }, + { + "id": "NQhy", + "code_hash": "29ac83f0938d9e28f09291ac8e870ad7", + "outputs": [ + { + "type": "data", + "data": { + "text/plain": "" + } + } + ], + "console": [ + { + "type": "stream", + "name": "stdout", + "text": "\u2705 Tarea 8 actualizada correctamente en Kanboard.\n" + }, + { + "type": "stream", + "name": "stdout", + "text": "\ud83c\udff7\ufe0f Etiquetas actualizadas: automatizada, revisi\u00f3n, python\n" + } + ] + } + ] +} \ No newline at end of file diff --git a/__marimo__/session/control_kanban_mediante_api.py.json b/__marimo__/session/control_kanban_mediante_api.py.json new file mode 100644 index 0000000..00437c1 --- /dev/null +++ b/__marimo__/session/control_kanban_mediante_api.py.json @@ -0,0 +1,77 @@ +{ + "version": "1", + "metadata": { + "marimo_version": "0.17.0" + }, + "cells": [ + { + "id": "Hbol", + "code_hash": "1d0db38904205bec4d6f6f6a1f6cec3e", + "outputs": [ + { + "type": "data", + "data": { + "text/plain": "" + } + } + ], + "console": [] + }, + { + "id": "MJUe", + "code_hash": "30549382a489a6758d4eda0a633ecec7", + "outputs": [ + { + "type": "error", + "ename": "exception", + "evalue": "401 Client Error: Unauthorized for url: http://127.0.0.1:8080/jsonrpc.php", + "traceback": [] + } + ], + "console": [ + { + "type": "stream", + "name": "stderr", + "text": "
Traceback (most recent call last):\n  File "/tmp/marimo_86378/__marimo__cell_MJUe_.py", line 67, in <module>\n    projects = get_all_projects_with_tasks(API_URL, USERNAME, API_TOKEN)\n  File "/tmp/marimo_86378/__marimo__cell_MJUe_.py", line 33, in get_all_projects_with_tasks\n    projects = rpc("getAllProjects")\n  File "/tmp/marimo_86378/__marimo__cell_MJUe_.py", line 27, in rpc\n    r.raise_for_status()\n    ~~~~~~~~~~~~~~~~~~^^\n  File "/home/lucas/DataProyects/kanboard/.venv/lib/python3.13/site-packages/requests/models.py", line 1026, in raise_for_status\n    raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 401 Client Error: Unauthorized for url: http://127.0.0.1:8080/jsonrpc.php\n
\n
" + } + ] + }, + { + "id": "LMtK", + "code_hash": "b150d3f7475c3d17e5c1bffbdbc6d5fe", + "outputs": [ + { + "type": "error", + "ename": "multiple-defs", + "evalue": "The variable 'API_TOKEN' was defined by another cell", + "traceback": [] + }, + { + "type": "error", + "ename": "multiple-defs", + "evalue": "The variable 'API_URL' was defined by another cell", + "traceback": [] + }, + { + "type": "error", + "ename": "multiple-defs", + "evalue": "The variable 'HTTPBasicAuth' was defined by another cell", + "traceback": [] + }, + { + "type": "error", + "ename": "multiple-defs", + "evalue": "The variable 'USERNAME' was defined by another cell", + "traceback": [] + }, + { + "type": "error", + "ename": "multiple-defs", + "evalue": "The variable 'requests' was defined by another cell", + "traceback": [] + } + ], + "console": [] + } + ] +} \ No newline at end of file diff --git a/conectarse_api_kanboard.py b/conectarse_api_kanboard.py new file mode 100644 index 0000000..67b3963 --- /dev/null +++ b/conectarse_api_kanboard.py @@ -0,0 +1,578 @@ +import marimo + +__generated_with = "0.17.0" +app = marimo.App(width="columns") + + +@app.cell(column=0) +def _(): + import marimo as mo + return (mo,) + + +@app.cell +def _(mo): + mo.md(r"""## Listar Proyectos""") + return + + +@app.cell +def _(): + import requests + from requests.auth import HTTPBasicAuth + import json + + url = "http://localhost:8080/jsonrpc.php" # o http://host.docker.internal:8080/jsonrpc.php desde WSL + auth = HTTPBasicAuth("jsonrpc", "792d8fdd6cbf69b3a32d800beaf48b958e4490dd9185c72c06c56061a591") + + payload = { + "jsonrpc": "2.0", + "method": "getAllProjects", + "id": 1 + } + + response = requests.post( + url, + json=payload, + auth=auth, + headers={"Content-Type": "application/json"}, + timeout=10 + ) + + # 👇 Convierte el cuerpo en JSON (Python dict) + resultado = response.json() + + # Muestra el JSON bonito + resultado + + return HTTPBasicAuth, requests + + +@app.cell +def _(mo): + mo.md(r"""## Listar usuarios""") + return + + +@app.cell +def _(API_URL, HTTPBasicAuth, TOKEN, USER, requests): + def obtener_usuarios(api_url, usuario, token): + """ + Devuelve la lista completa de usuarios disponibles en Kanboard. + + Parámetros: + api_url (str): URL del endpoint JSON-RPC (por ejemplo, http://host.docker.internal:8080/jsonrpc.php) + usuario (str): Usuario con permisos API (ej. 'jsonrpc' o 'admin') + token (str): Token de autenticación del usuario + + Retorna: + list[dict]: Lista de usuarios, cada uno con su 'id', 'username', 'name', etc. + """ + auth = HTTPBasicAuth(usuario, token) + payload = { + "jsonrpc": "2.0", + "method": "getAllUsers", + "id": 1 + } + + response = requests.post( + api_url, + json=payload, + auth=auth, + headers={"Content-Type": "application/json"}, + timeout=10 + ) + + data = response.json() + if "error" in data: + raise Exception(f"❌ Error al obtener usuarios: {data['error']}") + + return data.get("result", []) + + + # 🧪 Ejemplo de uso + if __name__ == "__main__": + + usuarios = obtener_usuarios(API_URL, USER, TOKEN) + print("✅ Usuarios encontrados:") + for u in usuarios: + print(f"ID {u['id']:>2} | Usuario: {u['username']:<15} | Nombre: {u.get('name', '')}") + + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Listar tareas""") + return + + +@app.cell +def _(API_URL, HTTPBasicAuth, PROJECT_ID, TOKEN, USER, requests): + def listar_tareas(api_url, usuario, token, project_id): + """ + Lista todas las tareas de un proyecto en Kanboard. + + Parámetros: + api_url (str): URL del endpoint JSON-RPC + usuario (str): Usuario con permisos API + token (str): Token del usuario + project_id (int): ID del proyecto donde obtener las tareas + + Retorna: + list[dict]: Lista de tareas en el proyecto + """ + auth = HTTPBasicAuth(usuario, token) + payload = { + "jsonrpc": "2.0", + "method": "getAllTasks", + "id": 1, + "params": { + "project_id": project_id + } + } + response = requests.post(api_url, json=payload, auth=auth, headers={"Content-Type": "application/json"}) + data = response.json() + + if "error" in data: + print("❌ Error al obtener tareas:", data["error"]) + return [] + + return data.get("result", []) + + + # 🧪 Ejemplo de uso: + tareas = listar_tareas(API_URL, USER, TOKEN, PROJECT_ID) + for tarea in tareas: + print(f"Tarea ID {tarea['id']}: {tarea['title']}") + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""### Listar subtareas""") + return + + +@app.cell +def _(API_URL, HTTPBasicAuth, TOKEN, USER, requests): + def listar_subtareas(api_url, usuario, token, task_id): + """ + Lista todas las subtareas de una tarea. + + Parámetros: + api_url (str): URL del endpoint JSON-RPC + usuario (str): Usuario con permisos API + token (str): Token del usuario + task_id (int): ID de la tarea principal para obtener sus subtareas + + Retorna: + list[dict]: Lista de subtareas asociadas + """ + auth = HTTPBasicAuth(usuario, token) + payload = { + "jsonrpc": "2.0", + "method": "getAllSubtasks", + "id": 1, + "params": { + "task_id": task_id + } + } + response = requests.post(api_url, json=payload, auth=auth, headers={"Content-Type": "application/json"}) + data = response.json() + + if "error" in data: + print("❌ Error al obtener subtareas:", data["error"]) + return [] + + return data.get("result", []) + + + # 🧪 Ejemplo de uso: + task_id = 10 # ID de la tarea principal + subtareas = listar_subtareas(API_URL, USER, TOKEN, task_id) + for sub in subtareas: + print(f"Subtarea ID {sub['id']}: {sub['title']}") + + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Listar tareas columnas""") + return + + +@app.cell +def _(API_URL, HTTPBasicAuth, PROJECT_ID, TOKEN, USER, requests): + def listar_tareas_por_columna(api_url, usuario, token, project_id): + """ + Devuelve todas las _tareas agrupadas por columna dentro de un proyecto Kanboard. + + Parámetros: + api_url (str): URL del endpoint JSON-RPC + usuario (str): Usuario con permisos API + token (str): Token del usuario + project_id (int): ID del proyecto + + Retorna: + dict: { 'columna': [ {_tarea}, {_tarea} ] } + """ + auth = HTTPBasicAuth(usuario, token) + payload = { + "jsonrpc": "2.0", + "method": "getBoard", + "id": 1, + "params": { + "project_id": project_id + } + } + + response = requests.post(api_url, json=payload, auth=auth, headers={"Content-Type": "application/json"}) + data = response.json() + + if "error" in data: + print("❌ Error al obtener el tablero:", data["error"]) + return {} + + board = data.get("result", []) + tareas_por_columna = {} + + for swimlane in board: + for columna in swimlane.get("columns", []): + nombre_columna = columna["title"] + tareas_por_columna[nombre_columna] = columna.get("tasks", []) + + return tareas_por_columna + + + # 🧪 Ejemplo de uso + if __name__ == "__main__": + + tareas_columna = listar_tareas_por_columna(API_URL, USER, TOKEN, PROJECT_ID) + for columna, _tareas in tareas_columna.items(): + print(f"\n📦 {columna} ({len(_tareas)} tareas):") + for _tarea in _tareas: + print(f" ├─ [{_tarea['id']}] {_tarea['title']}") + + return + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Mover tareas entre columnas""") + return + + +@app.cell +def _(API_URL, HTTPBasicAuth, PROJECT_ID, TOKEN, USER, requests): + def mover_tarea_columna(api_url, usuario, token, project_id, task_id, column_id, position=1, swimlane_id=0): + """ + Mueve una tarea a otra columna dentro de un proyecto Kanboard. + + Parámetros: + api_url (str): URL del endpoint JSON-RPC + usuario (str): Usuario con permisos API (por ejemplo 'jsonrpc' o tu bot) + token (str): Token de autenticación + project_id (int): ID del proyecto + task_id (int): ID de la tarea a mover + column_id (int): ID de la columna destino + position (int): Posición dentro de la columna (por defecto 1) + swimlane_id (int): ID de la swimlane (0 = principal) + + Retorna: + bool: True si el movimiento fue exitoso, False en caso de error. + """ + auth = HTTPBasicAuth(usuario, token) + + payload = { + "jsonrpc": "2.0", + "method": "moveTaskPosition", + "id": 1, + "params": { + "project_id": project_id, + "task_id": task_id, + "column_id": column_id, + "position": position, + "swimlane_id": swimlane_id + } + } + + headers = {"Content-Type": "application/json"} + response = requests.post(api_url, json=payload, auth=auth, headers=headers, timeout=10) + data = response.json() + + if "error" in data: + print("❌ Error al mover tarea:", data["error"]) + return False + + result = data.get("result", False) + if result: + print(f"✅ Tarea {task_id} movida correctamente a la columna {column_id}.") + else: + print(f"⚠️ No se pudo mover la tarea {task_id}.") + return result + + + # 🧪 Ejemplo de uso + if __name__ == "__main__": + # API_URL = "http://host.docker.internal:8080/jsonrpc.php" + # USER = "jsonrpc" + # TOKEN = "792d8fdd6cbf69b3a32d800beaf48b958e4490dd9185c72c06c56061a591" + # PROJECT_ID = 1 + TASK_ID = 6 # ID de la tarea a mover + COLUMN_ID = 3 # Nueva columna (por ejemplo "En progreso") + + mover_tarea_columna(API_URL, USER, TOKEN, PROJECT_ID, TASK_ID, COLUMN_ID) + return + + +@app.cell(column=1) +def _(): + return + + +@app.cell +def _(mo): + mo.md(r"""## Crear tarea""") + return + + +@app.cell +def _(HTTPBasicAuth, requests): + import random + + def crear_tarea(api_url, usuario, token, project_id, titulo, descripcion="", swimlane_id=None, assignee_id=None, tags=None, priority=None): + """ + Crea una tarea en el backlog del proyecto indicado en Kanboard, + con un color aleatorio, etiquetas (tags), prioridad + y (opcionalmente) asignada a un usuario. + + Parámetros: + api_url (str): URL del endpoint JSON-RPC + usuario (str): Usuario con permisos API + token (str): Token del usuario + project_id (int): ID del proyecto donde crear la tarea + titulo (str): Título de la tarea + descripcion (str): Descripción de la tarea + swimlane_id (int): ID de la swimlane (opcional) + assignee_id (int): ID del usuario a quien se asignará la tarea (opcional) + tags (list[str]): Lista de etiquetas a añadir (opcional) + priority (int): Prioridad numérica de la tarea (opcional) + + Retorna: + dict: Respuesta completa de la API + """ + auth = HTTPBasicAuth(usuario, token) + + # 🎨 Colores disponibles + colores = [ + "yellow", "blue", "green", "purple", + "red", "orange", "grey", "brown", + "deep_orange", "dark_grey", "lime", "cyan" + ] + color_aleatorio = random.choice(colores) + + # 🧩 Payload principal + payload = { + "jsonrpc": "2.0", + "method": "createTask", + "id": 1, + "params": { + "title": titulo, + "description": descripcion, + "project_id": project_id, + "color_id": color_aleatorio + } + } + + if swimlane_id is not None: + payload["params"]["swimlane_id"] = swimlane_id + + if assignee_id is not None: + payload["params"]["owner_id"] = assignee_id + + if priority is not None: + payload["params"]["priority"] = priority + + headers = {"Content-Type": "application/json"} + + # 🚀 Crear la tarea + response = requests.post(api_url, json=payload, auth=auth, headers=headers, timeout=10) + data = response.json() + + if "error" in data: + print("❌ Error al crear tarea:", data["error"]) + return data + + task_id = data.get("result") + print(f"✅ Tarea creada correctamente (ID {task_id}) en el proyecto {project_id}") + + # 🏷️ Añadir etiquetas (si se proporcionaron) + if tags: + tag_payload = { + "jsonrpc": "2.0", + "method": "setTaskTags", + "id": 1, + "params": { + "project_id": project_id, + "task_id": task_id, + "tags": tags + } + } + tag_response = requests.post(api_url, json=tag_payload, auth=auth, headers=headers, timeout=10) + tag_data = tag_response.json() + + if "error" in tag_data: + print("⚠️ Error al añadir etiquetas:", tag_data["error"]) + else: + print(f"🏷️ Etiquetas añadidas: {', '.join(tags)}") + + return data + + + # 🧪 Ejemplo de uso: + if __name__ == "__main__": + API_URL = "http://localhost:8080/jsonrpc.php" # desde WSL + USER = "jsonrpc" + TOKEN = "792d8fdd6cbf69b3a32d800beaf48b958e4490dd9185c72c06c56061a591" + PROJECT_ID = 1 + ASSIGNEE_ID = 2 # ID de Egutierrez + + crear_tarea( + api_url=API_URL, + usuario=USER, + token=TOKEN, + project_id=PROJECT_ID, + titulo="Tarea con maxima prioridad", + descripcion="Esta tarea tiene prioridad 3 y etiquetas automáticas.", + assignee_id=ASSIGNEE_ID, + tags=["etl", "prioridad-alta"], + priority=3 + ) + return API_URL, PROJECT_ID, TOKEN, USER + + +@app.cell(hide_code=True) +def _(mo): + mo.md(r"""## Editar tarea""") + return + + +@app.cell +def _(API_URL, HTTPBasicAuth, TOKEN, USER, requests): + def editar_tarea(api_url, usuario, token, task_id, titulo=None, descripcion=None, + swimlane_id=None, assignee_id=None, tags=None, priority=None): + """ + Edita una tarea existente en Kanboard. + Solo se envían los parámetros que se indiquen explícitamente. + + Parámetros: + api_url (str): URL del endpoint JSON-RPC + usuario (str): Usuario con permisos API + token (str): Token del usuario + task_id (int): ID de la tarea a editar + titulo (str): Nuevo título (opcional) + descripcion (str): Nueva descripción (opcional) + swimlane_id (int): Nueva swimlane (opcional) + assignee_id (int): Nuevo responsable (opcional) + tags (list[str]): Lista de etiquetas (opcional) + priority (int): Nueva prioridad (opcional) + + Retorna: + dict: Respuesta completa de la API + """ + auth = HTTPBasicAuth(usuario, token) + headers = {"Content-Type": "application/json"} + + # 🧩 Construir payload con solo los campos que se quieren cambiar + params = {"id": task_id} + + if titulo is not None: + params["title"] = titulo + if descripcion is not None: + params["description"] = descripcion + if swimlane_id is not None: + params["swimlane_id"] = swimlane_id + if assignee_id is not None: + params["owner_id"] = assignee_id + if priority is not None: + params["priority"] = priority + + # Si no hay cambios, no se hace nada + if len(params) == 1: + print("⚠️ No se proporcionaron campos a modificar.") + return {"warning": "sin cambios"} + + payload = { + "jsonrpc": "2.0", + "method": "updateTask", + "id": 1, + "params": params + } + + # 🚀 Ejecutar la actualización + response = requests.post(api_url, json=payload, auth=auth, headers=headers, timeout=10) + data = response.json() + + if "error" in data: + print("❌ Error al editar tarea:", data["error"]) + return data + + print(f"✅ Tarea {task_id} actualizada correctamente en Kanboard.") + + # 🏷️ Actualizar etiquetas si se proporcionan + if tags: + # Para setTaskTags se requiere el project_id, lo obtenemos antes + get_task_payload = { + "jsonrpc": "2.0", + "method": "getTask", + "id": 1, + "params": {"task_id": task_id} + } + task_response = requests.post(api_url, json=get_task_payload, auth=auth, headers=headers, timeout=10) + project_id = task_response.json().get("result", {}).get("project_id") + + if project_id: + tag_payload = { + "jsonrpc": "2.0", + "method": "setTaskTags", + "id": 1, + "params": { + "project_id": project_id, + "task_id": task_id, + "tags": tags + } + } + tag_response = requests.post(api_url, json=tag_payload, auth=auth, headers=headers, timeout=10) + tag_data = tag_response.json() + + if "error" in tag_data: + print("⚠️ Error al actualizar etiquetas:", tag_data["error"]) + else: + print(f"🏷️ Etiquetas actualizadas: {', '.join(tags)}") + + return data + + + # 🧪 Ejemplo de uso: + if __name__ == "__main__": + _TASK_ID = 8 # ID de la tarea existente + + editar_tarea( + api_url=API_URL, + usuario=USER, + token=TOKEN, + task_id=_TASK_ID, + titulo="🔥 Actualización directa desde Python", + descripcion="Descripción actualizada sin afectar otros campos.", + assignee_id=2, + tags=["automatizada", "revisión", "python"], + priority=2 + ) + + return + + +if __name__ == "__main__": + app.run()