91 lines
2.9 KiB
Python
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
|