perf: delete_category uses single API session (v0.4.13)

Previously delete_category made two separate login cycles (accgetallfamily
to verify + taskcategorydelete to delete) = 6 HTTP calls. Now both happen
in one session = 4 HTTP calls. Halves latency and eliminates the risk of
a compounding timeout when the API is temporarily slow.

FW_DEBUG=1 investigation confirmed the API itself is fast (<1s); the
reported 4-min timeout was transient API slowness amplified by the double
session overhead.

Also recreated 'Obst & Gemüse' (emoji 🍎) as taskCategory/23431854_4956806
after it was accidentally consumed by the debug script.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-16 08:26:01 +02:00
parent 0d8036fd4a
commit 1ab410621c
4 changed files with 45 additions and 30 deletions
+42 -27
View File
@@ -502,42 +502,57 @@ def delete_category(category_id: str) -> str:
Returns:
JSON success indicator or an error message.
"""
# Safety check: look up the category and verify it is custom (canDelete=true).
# This prevents accidental deletion of shared system categories.
# Safety check + delete in a SINGLE session to minimise API round-trips.
# Previously two separate sessions were used (accgetallfamily + taskcategorydelete),
# causing 6 HTTP calls. One session = 4 HTTP calls (login, check, delete, logout).
try:
data = _accgetallfamily()
email, password = get_credentials()
except RuntimeError as exc:
return f"Error: {exc}"
cat_obj: dict[str, Any] | None = None
try:
raw_cats: list[dict[str, Any]] = data["a01"]["r"]["r"]["updatedCreated"]
except (KeyError, TypeError):
raw_cats = []
with FamilyWallClient() as client:
client.login(email, password)
cat_obj: dict[str, Any] | None = next(
(c for c in raw_cats if c.get("metaId") == category_id), None
)
# Fetch categories and verify the target is custom (canDelete=true).
data = client.call(
"accgetallfamily",
{"a01call": "taskcategorysync", "a02call": "tasksync"},
)
try:
raw_cats: list[dict[str, Any]] = data["a01"]["r"]["r"]["updatedCreated"]
except (KeyError, TypeError):
raw_cats = []
if cat_obj is None:
return f"Error: Category '{category_id}' not found."
cat_obj = next(
(c for c in raw_cats if c.get("metaId") == category_id), None
)
if cat_obj is None:
client.logout()
return f"Error: Category '{category_id}' not found."
can_delete: str | None = cat_obj.get("rights", {}).get("canDelete")
if can_delete != "true":
return json.dumps(
{
"error": "System categories cannot be deleted.",
"id": category_id,
"name": cat_obj.get("name"),
"hint": "Only custom categories (custom=true in get_categories) can be deleted.",
},
ensure_ascii=False,
indent=2,
)
can_delete: str | None = cat_obj.get("rights", {}).get("canDelete")
if can_delete != "true":
client.logout()
return json.dumps(
{
"error": "System categories cannot be deleted.",
"id": category_id,
"name": cat_obj.get("name"),
"hint": "Only custom categories (custom=true in get_categories) can be deleted.",
},
ensure_ascii=False,
indent=2,
)
try:
_authenticated_call("taskcategorydelete", {"id": category_id})
except RuntimeError as exc:
return f"Error: {exc}"
# Verified — delete in the same session.
client.call("taskcategorydelete", {"id": category_id})
client.logout()
except FamilyWallError as exc:
return f"Error: Family Wall API error: {exc}"
except Exception as exc:
return f"Error: Connection error: {exc}"
return json.dumps(
{"deleted": True, "id": category_id, "name": cat_obj.get("name")},