Corp/backend/tasks/templates.py

91 lines
2.9 KiB
Python

"""Capture and restore board layouts without copying tasks or access grants."""
from django.db import transaction
from .models import (
TaskBoard,
TaskBoardAccessScope,
TaskColumn,
TaskColumnRow,
)
def snapshot_board_structure(board: TaskBoard) -> dict:
"""Capture visible columns, nested columns and swimlanes in stable order."""
columns = list(
TaskColumn.objects.filter(board=board, is_archived=False).order_by(
"position", "id"
)
)
rows = list(
TaskColumnRow.objects.filter(
column__board=board, column__is_archived=False
).order_by("position", "id")
)
children_by_parent: dict[int | None, list[TaskColumn]] = {}
for column in columns:
children_by_parent.setdefault(column.parent_id, []).append(column)
rows_by_column: dict[int, list[TaskColumnRow]] = {}
for row in rows:
rows_by_column.setdefault(row.column_id, []).append(row)
seen: set[int] = set()
def serialize(column: TaskColumn) -> dict:
if column.id in seen:
return {}
seen.add(column.id)
return {
"name": column.name,
"position": column.position,
"color": column.color,
"is_done": column.is_done,
"rows": [
{"name": row.name, "position": row.position, "color": row.color}
for row in rows_by_column.get(column.id, [])
],
"subcolumns": [
value
for child in children_by_parent.get(column.id, [])
if (value := serialize(child))
],
}
return {
"columns": [serialize(column) for column in children_by_parent.get(None, [])]
}
@transaction.atomic
def create_board_from_template(template, *, owner, name: str) -> TaskBoard:
"""Instantiate a private board so a shared template cannot leak user data."""
board = TaskBoard.objects.create(
name=name,
description=template.description,
created_by=owner,
access_scope=TaskBoardAccessScope.PRIVATE,
)
def create_column(spec: dict, parent: TaskColumn | None = None) -> None:
column = TaskColumn.objects.create(
board=board,
parent=parent,
name=spec["name"],
position=spec.get("position", 0),
color=spec.get("color", ""),
is_done=bool(spec.get("is_done", False)),
)
for row in spec.get("rows", []):
TaskColumnRow.objects.create(
column=column,
name=row["name"],
position=row.get("position", 0),
color=row.get("color", ""),
)
for child in spec.get("subcolumns", []):
create_column(child, column)
for column_spec in template.layout.get("columns", []):
create_column(column_spec)
return board