128 lines
3.6 KiB
Python
128 lines
3.6 KiB
Python
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/<int:list_id>/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/<int:card_id>", 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/<int:card_id>", 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/<int:card_id>", 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
|