Files
nextcloud-deck-tools/create_board.py

167 lines
4.8 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
import os
import sys
from typing import Iterable
import requests
from common import (
add_domain_and_auth_args,
build_session,
resolve_arg,
resolve_domain_and_auth,
)
DEFAULT_STACKS: list[str] = [
"🐞 Bugs",
"📋 To Do",
"🚧 In Progress",
"✅ Done",
"📄 Backlog",
]
def get_stack_names() -> list[str]:
"""
Get stack names from NEW_BOARD_COLUMNS env var (comma-separated),
or fall back to DEFAULT_STACKS if not set.
"""
columns_env = os.getenv("NEW_BOARD_COLUMNS")
if columns_env:
# Split by comma and strip whitespace from each column name
return [col.strip() for col in columns_env.split(",") if col.strip()]
return DEFAULT_STACKS
def create_board(session: requests.Session, base_url: str, title: str) -> dict:
"""
POST /boards -> { id, title, ... }
"""
url = f"{base_url}/index.php/apps/deck/api/v1.0/boards"
headers = {"OCS-APIRequest": "true", "Content-Type": "application/json"}
payload = {"title": title}
resp = session.post(url, headers=headers, data=json.dumps(payload))
if resp.status_code not in (200, 201):
raise RuntimeError(
f"Deck API error creating board: {resp.status_code} {resp.text}"
)
return resp.json()
def create_stack(
session: requests.Session, base_url: str, board_id: int, title: str, order: int
) -> dict:
"""
POST /boards/{board_id}/stacks -> { id, title, order, ... }
"""
url = f"{base_url}/index.php/apps/deck/api/v1.0/boards/{board_id}/stacks"
headers = {"OCS-APIRequest": "true", "Content-Type": "application/json"}
payload = {"title": title, "order": order}
resp = session.post(url, headers=headers, data=json.dumps(payload))
if resp.status_code not in (200, 201):
raise RuntimeError(
f"Deck API error creating stack '{title}': {resp.status_code} {resp.text}"
)
return resp.json()
def create_stacks_in_order(
session: requests.Session,
base_url: str,
board_id: int,
stacks: Iterable[str],
) -> list[dict]:
created = []
for idx, title in enumerate(stacks):
created.append(create_stack(session, base_url, board_id, title, idx))
print(f" ✔️ Created stack [{idx}]: {title}")
return created
def main() -> None:
parser = argparse.ArgumentParser(
description="Create a Nextcloud Deck board and pre-populate it with standard stacks."
)
# Only domain/username/password (no board-id since we are creating it)
add_domain_and_auth_args(parser)
# Script-specific
parser.add_argument(
"--board-name",
help="New board name/title (e.g., 'Team Kanban')",
)
parser.add_argument(
"--no-default-stacks",
action="store_true",
help="Create the board without the default stacks.",
)
args = parser.parse_args()
# Resolve domain + auth (ENV -> CLI -> prompt)
try:
base_url, username, password = resolve_domain_and_auth(
cli_domain=args.domain,
cli_username=args.username,
cli_password=args.password,
)
except Exception as e:
print(f"{e}", file=sys.stderr)
sys.exit(2)
# Resolve board name
try:
board_name = resolve_arg(
args.board_name,
["NEXTCLOUD_BOARD_NAME", "BOARD_NAME"],
prompt_text="New board name: ",
cast=str,
)
except Exception as e:
print(f"{e}", file=sys.stderr)
sys.exit(2)
session = build_session(username, password)
# Create board
try:
board = create_board(session, base_url, board_name)
board_id = board.get("id")
if board_id is None:
raise RuntimeError(f"Board created but no id returned: {board}")
print(f"✔️ Created board '{board_name}' (id: {board_id}) at {base_url}")
except Exception as e:
print(f"❌ Failed to create board: {e}", file=sys.stderr)
sys.exit(1)
# Create default stacks (unless suppressed)
created_stacks = []
if not args.no_default_stacks:
try:
stack_names = get_stack_names()
print("→ Creating stacks in order:")
created_stacks = create_stacks_in_order(
session, base_url, int(board_id), stack_names
)
except Exception as e:
print(f"❌ Failed to create stacks: {e}", file=sys.stderr)
sys.exit(1)
# Summary
print("\nSummary")
print("-------")
print(f"Board ID: {board_id}")
if created_stacks:
print("Stacks (in order):")
for s in created_stacks:
print(f" - [{s.get('id')}] {s.get('title')}")
if __name__ == "__main__":
main()