from datetime import UTC, datetime from flask import request from flask_jwt_extended import jwt_required from flask_pydantic import validate from app import db from app.decorators import load_card_owned, load_list_owned from app.models import Board, Card, CardLabel, Label, List from app.schemas import (CardCreateRequest, CardResponse, CardWithDetailsResponse) from . import kanban_bp @kanban_bp.route("/lists//cards", methods=["POST"]) @jwt_required() @load_list_owned @validate(body=CardCreateRequest) def create_card(list_id, lst, body: CardCreateRequest): """Create a new card in a list""" card = Card( name=body.name, description=body.description, board_id=lst.board_id, list_id=list_id, pos=body.pos, due=body.due, due_complete=body.due_complete, badges=body.badges, cover=body.cover, desc_data=body.desc_data, ) db.session.add(card) db.session.commit() return CardResponse.model_validate(card).model_dump(), 201 @kanban_bp.route("/cards/", methods=["GET"]) @jwt_required() @load_card_owned def get_card(card_id, card): """Get a single card with full details""" from app.models import User card_dict = card.to_dict() # Add labels card_dict["labels"] = [ label.to_dict() for label in ( db.session.query(Label) .join(CardLabel) .filter(CardLabel.card_id == card.id) .all() ) ] # Add checklists card_dict["checklists"] = [ { **checklist.to_dict(), "items": [item.to_dict() for item in checklist.check_items.all()], } for checklist in card.checklists.all() ] # Add comments card_dict["comments"] = [] for comment in card.comments.all(): comment_dict = comment.to_dict() user = db.session.get(User, comment.user_id) comment_dict["user"] = user.to_dict() if user else None card_dict["comments"].append(comment_dict) response = CardWithDetailsResponse(**card_dict) return response.model_dump(), 200 @kanban_bp.route("/cards/", methods=["PUT"]) @jwt_required() @load_card_owned @validate(body=CardCreateRequest) def update_card(card_id, card, body: CardCreateRequest): """Update a card""" card.name = body.name if body.description is not None: card.description = body.description if request.json.get("closed") is not None: card.closed = request.json.get("closed") card.pos = body.pos card.due = body.due card.due_complete = body.due_complete if body.badges is not None: card.badges = body.badges if body.cover is not None: card.cover = body.cover if body.desc_data is not None: card.desc_data = body.desc_data # Handle moving card to different list if "list_id" in request.json: new_list_id = request.json["list_id"] new_list = db.session.get(List, new_list_id) if new_list and new_list.board_id == card.board_id: card.list_id = new_list_id card.date_last_activity = datetime.now(UTC) board = db.session.get(Board, card.board_id) board.date_last_activity = datetime.now(UTC) db.session.commit() return CardResponse.model_validate(card).model_dump(), 200 @kanban_bp.route("/cards/", methods=["DELETE"]) @jwt_required() @load_card_owned def delete_card(card_id, card): """Delete a card""" db.session.delete(card) db.session.commit() return {"message": "Card deleted"}, 200