feat(v4.4.0): Tasks, Sub-items & Dependencies
FlowDeck CI / test (push) Failing after 4s
FlowDeck CI / docker (push) Has been skipped

- Add is_task flag on collections (toggle-task API)
- New page_dependencies table with auto_shift config (blocks/blocked_by/related)
- API: GET/POST/DELETE page dependencies
- API: POST auto-shift dates based on blocking dependencies (skip_weekends option)
- 5 new tests (171 total)
This commit is contained in:
2026-07-21 22:26:24 -04:00
parent 122ccfb3c3
commit db4329eee2
3 changed files with 294 additions and 1 deletions
+118 -1
View File
@@ -2451,4 +2451,121 @@ def test_view_unknown_falls_back_to_table(client):
coll_id = resp.json()["id"]
resp = client.get(f"/db/{coll_id}/view/unknown_view_type")
assert resp.status_code == 200
assert "<table>" in resp.text
assert "<table>" in resp.text
# ── v4.4.0: Tasks & Dependencies ──
def test_toggle_task_flag(client):
"""Toggle is_task on a collection."""
resp = client.post("/db/api", json={"name": "Task DB"})
coll_id = resp.json()["id"]
resp = client.put(f"/db/{coll_id}/toggle-task/api")
assert resp.status_code == 200
assert resp.json()["is_task"] is True
assert resp.json()["mode"] == "tasks"
resp = client.get(f"/db/{coll_id}/api")
assert resp.json()["collection"]["is_task"] == 1
# Toggle back
resp = client.put(f"/db/{coll_id}/toggle-task/api")
assert resp.json()["is_task"] is False
assert resp.json()["mode"] == "standard"
def test_page_dependencies_crud(client):
"""Add, list, and remove page dependencies."""
resp = client.post("/db/api", json={"name": "Dep DB"})
coll_id = resp.json()["id"]
p1 = client.post(f"/db/{coll_id}/pages/api", json={"title": "Task A"})
p2 = client.post(f"/db/{coll_id}/pages/api", json={"title": "Task B"})
pid1 = p1.json()["id"]
pid2 = p2.json()["id"]
# Add dependency: Task A blocks Task B
resp = client.post(f"/db/{coll_id}/pages/{pid2}/dependencies/api", json={
"dependency_id": pid1,
"dependency_type": "blocks",
"auto_shift": "overlap",
})
assert resp.status_code == 200
assert resp.json()["status"] == "added"
dep_id = resp.json()["id"]
# List dependencies
resp = client.get(f"/db/{coll_id}/pages/{pid2}/dependencies/api")
deps = resp.json()["dependencies"]
assert len(deps) == 1
assert deps[0]["dependency_type"] == "blocks"
assert deps[0]["dependency_title"] == "Task A"
# Remove dependency
resp = client.delete(f"/db/{coll_id}/pages/{pid2}/dependencies/{dep_id}/api")
assert resp.status_code == 200
assert resp.json()["status"] == "removed"
resp = client.get(f"/db/{coll_id}/pages/{pid2}/dependencies/api")
assert len(resp.json()["dependencies"]) == 0
def test_page_dependencies_duplicate_rejected(client):
"""Duplicate dependency returns 409."""
resp = client.post("/db/api", json={"name": "Dup Dep DB"})
coll_id = resp.json()["id"]
p1 = client.post(f"/db/{coll_id}/pages/api", json={"title": "A"})
p2 = client.post(f"/db/{coll_id}/pages/api", json={"title": "B"})
client.post(f"/db/{coll_id}/pages/{p2.json()['id']}/dependencies/api", json={
"dependency_id": p1.json()["id"],
})
resp = client.post(f"/db/{coll_id}/pages/{p2.json()['id']}/dependencies/api", json={
"dependency_id": p1.json()["id"],
})
assert resp.status_code == 409
def test_auto_shift_dates(client):
"""Auto-shift dates when a blocking task is completed."""
resp = client.post("/db/api", json={"name": "Shift DB"})
coll_id = resp.json()["id"]
# Blocking task with end date July 10
p1 = client.post(f"/db/{coll_id}/pages/api", json={
"title": "Blocker",
"properties": {"Timeline": "2026-07-01...2026-07-10"},
})
# Blocked task
p2 = client.post(f"/db/{coll_id}/pages/api", json={
"title": "Dependent",
"properties": {"Timeline": "2026-07-11...2026-07-20"},
})
# Add dependency
client.post(f"/db/{coll_id}/pages/{p2.json()['id']}/dependencies/api", json={
"dependency_id": p1.json()["id"],
"dependency_type": "blocks",
})
# Auto-shift
resp = client.post(f"/db/{coll_id}/pages/{p2.json()['id']}/auto-shift/api", json={
"skip_weekends": False,
})
assert resp.status_code == 200
data = resp.json()
assert data["shifted"] is True
assert data["new_start"] == "2026-07-11" # day after blocker ends
def test_auto_shift_no_blockers(client):
"""Auto-shift with no blocking dependencies returns shifted=False."""
resp = client.post("/db/api", json={"name": "NoBlock DB"})
coll_id = resp.json()["id"]
p = client.post(f"/db/{coll_id}/pages/api", json={"title": "Lone Task"})
resp = client.post(f"/db/{coll_id}/pages/{p.json()['id']}/auto-shift/api")
assert resp.status_code == 200
assert resp.json()["shifted"] is False