byceps/byceps

View on GitHub
byceps/blueprints/admin/board/views.py

Summary

Maintainability
A
0 mins
Test Coverage
C
73%
"""
byceps.blueprints.admin.board.views
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

:Copyright: 2014-2024 Jochen Kupperschmidt
:License: Revised BSD (see `LICENSE` file for details)
"""

from dataclasses import dataclass
from uuid import UUID

from flask import abort, request
from flask_babel import gettext

from byceps.services.board import (
    board_category_command_service,
    board_category_query_service,
    board_posting_query_service,
    board_service,
    board_topic_query_service,
)
from byceps.services.board.models import Board, BoardCategory, BoardCategoryID
from byceps.services.brand import brand_service
from byceps.util.framework.blueprint import create_blueprint
from byceps.util.framework.flash import flash_error, flash_success
from byceps.util.framework.templating import templated
from byceps.util.views import (
    permission_required,
    redirect_to,
    respond_no_content,
)

from .forms import BoardCreateForm, CategoryCreateForm, CategoryUpdateForm


blueprint = create_blueprint('board_admin', __name__)


@dataclass(frozen=True)
class BoardStats:
    category_count: int
    topic_count: int
    posting_count: int


# -------------------------------------------------------------------- #
# boards


@blueprint.get('/brands/<brand_id>')
@permission_required('board_category.view')
@templated
def board_index_for_brand(brand_id):
    """List categories for that brand."""
    brand = _get_brand_or_404(brand_id)

    boards = board_service.get_boards_for_brand(brand.id)
    board_ids = [board.id for board in boards]

    stats_by_board_id = {
        board_id: BoardStats(
            board_category_query_service.count_categories_for_board(board_id),
            board_topic_query_service.count_topics_for_board(board_id),
            board_posting_query_service.count_postings_for_board(board_id),
        )
        for board_id in board_ids
    }

    return {
        'boards': boards,
        'brand': brand,
        'stats_by_board_id': stats_by_board_id,
    }


@blueprint.get('/boards/<board_id>')
@permission_required('board_category.view')
@templated
def board_view(board_id):
    """View the board."""
    board = _get_board_or_404(board_id)

    brand = brand_service.find_brand(board.brand_id)

    categories = board_category_query_service.get_categories(board.id)

    return {
        'board_id': board.id,
        'board_brand_id': board.brand_id,
        'brand': brand,
        'categories': categories,
    }


@blueprint.get('/for_brand/<brand_id>/boards/create')
@permission_required('board.create')
@templated
def board_create_form(brand_id, erroneous_form=None):
    """Show form to create a board."""
    brand = _get_brand_or_404(brand_id)

    form = erroneous_form if erroneous_form else BoardCreateForm()

    return {
        'brand': brand,
        'form': form,
    }


@blueprint.post('/for_brand/<brand_id>/boards')
@permission_required('board.create')
def board_create(brand_id):
    """Create a board."""
    brand = _get_brand_or_404(brand_id)

    form = BoardCreateForm(request.form)
    if not form.validate():
        return board_create_form(brand.id, form)

    board_id = form.board_id.data.strip().lower()

    board = board_service.create_board(brand, board_id)

    flash_success(
        gettext(
            'Board with ID "%(board_id)s" has been created.',
            board_id=board.id,
        )
    )
    return redirect_to('.board_view', board_id=board.id)


# -------------------------------------------------------------------- #
# categories


@blueprint.get('/categories/for_board/<board_id>/copy_from/<source_board_id>')
@permission_required('board_category.create')
@templated
def category_copy_from_form(board_id, source_board_id, erroneous_form=None):
    """Show form to copy an existing category to this board."""
    board = _get_board_or_404(board_id)

    brand = brand_service.find_brand(board.brand_id)

    source_board = _get_board_or_404(source_board_id)

    categories = board_category_query_service.get_categories(source_board.id)

    return {
        'board': board,
        'brand': brand,
        'categories': categories,
    }


@blueprint.get('/categories/for_board/<board_id>/create')
@permission_required('board_category.create')
@templated
def category_create_form(board_id, erroneous_form=None):
    """Show form to create a category."""
    board = _get_board_or_404(board_id)

    brand = brand_service.find_brand(board.brand_id)

    brand_boards = board_service.get_boards_for_brand(brand.id)

    source_category = _get_source_category()

    form = (
        erroneous_form
        if erroneous_form
        else CategoryCreateForm(obj=source_category)
    )

    return {
        'board': board,
        'brand': brand,
        'brand_boards': brand_boards,
        'form': form,
    }


def _get_source_category() -> BoardCategory | None:
    source_category_id = request.args.get('source_category_id')
    if not source_category_id:
        return None

    return board_category_query_service.find_category_by_id(
        BoardCategoryID(UUID(source_category_id))
    )


@blueprint.post('/categories/for_board/<board_id>')
@permission_required('board_category.create')
def category_create(board_id):
    """Create a category."""
    board = _get_board_or_404(board_id)

    form = CategoryCreateForm(request.form)
    if not form.validate():
        return category_create_form(board.id, form)

    slug = form.slug.data.strip().lower()
    title = form.title.data.strip()
    description = form.description.data.strip()

    category = board_category_command_service.create_category(
        board.id, slug, title, description
    )

    flash_success(
        gettext('Category "%(title)s" has been created.', title=category.title)
    )
    return redirect_to('.board_view', board_id=board.id)


@blueprint.get('/categories/<uuid:category_id>/update')
@permission_required('board_category.update')
@templated
def category_update_form(category_id, erroneous_form=None):
    """Show form to update the category."""
    category = _get_category_or_404(category_id)

    board = board_service.find_board(category.board_id)
    brand = brand_service.find_brand(board.brand_id)

    form = (
        erroneous_form if erroneous_form else CategoryUpdateForm(obj=category)
    )

    return {
        'category': category,
        'brand': brand,
        'form': form,
    }


@blueprint.post('/categories/<uuid:category_id>')
@permission_required('board_category.update')
def category_update(category_id):
    """Update the category."""
    category = _get_category_or_404(category_id)

    form = CategoryUpdateForm(request.form)
    if not form.validate():
        return category_update_form(category_id, form)

    slug = form.slug.data
    title = form.title.data
    description = form.description.data

    category = board_category_command_service.update_category(
        category.id, slug, title, description
    )

    flash_success(
        gettext('Category "%(title)s" has been updated.', title=category.title)
    )
    return redirect_to('.board_view', board_id=category.board_id)


@blueprint.post('/categories/<uuid:category_id>/flags/hidden')
@permission_required('board_category.update')
@respond_no_content
def category_hide(category_id):
    """Hide the category."""
    category = _get_category_or_404(category_id)

    board_category_command_service.hide_category(category.id)

    flash_success(
        gettext('Category "%(title)s" has been hidden.', title=category.title)
    )


@blueprint.delete('/categories/<uuid:category_id>/flags/hidden')
@permission_required('board_category.update')
@respond_no_content
def category_unhide(category_id):
    """Un-hide the category."""
    category = _get_category_or_404(category_id)

    board_category_command_service.unhide_category(category.id)

    flash_success(
        gettext(
            'Category "%(title)s" has been made visible.', title=category.title
        )
    )


@blueprint.post('/categories/<uuid:category_id>/up')
@permission_required('board_category.update')
@respond_no_content
def category_move_up(category_id):
    """Move the category upwards by one position."""
    category = _get_category_or_404(category_id)

    result = board_category_command_service.move_category_up(category.id)

    if result.is_err():
        flash_error(
            gettext(
                'Category "%(title)s" is already at the top.',
                title=category.title,
            )
        )
        return

    flash_success(
        gettext(
            'Category "%(title)s" has been moved upwards by one position.',
            title=category.title,
        )
    )


@blueprint.post('/categories/<uuid:category_id>/down')
@permission_required('board_category.update')
@respond_no_content
def category_move_down(category_id):
    """Move the category downwards by one position."""
    category = _get_category_or_404(category_id)

    result = board_category_command_service.move_category_down(category.id)

    if result.is_err():
        flash_error(
            gettext(
                'Category "%(title)s" is already at the bottom.',
                title=category.title,
            )
        )
        return

    flash_success(
        gettext(
            'Category "%(title)s" has been moved downwards by one position.',
            title=category.title,
        )
    )


@blueprint.delete('/categories/<uuid:category_id>')
@permission_required('board_category.create')
@respond_no_content
def category_delete(category_id):
    """Delete a category."""
    category = _get_category_or_404(category_id)

    result = board_category_command_service.delete_category(category.id)

    if result.is_err():
        flash_error(
            gettext(
                'Category "%(title)s" could not be deleted.',
                title=category.title,
            )
        )
        return

    flash_success(
        gettext('Category "%(title)s" has been deleted.', title=category.title)
    )


# -------------------------------------------------------------------- #
# helpers


def _get_brand_or_404(brand_id):
    brand = brand_service.find_brand(brand_id)

    if brand is None:
        abort(404)

    return brand


def _get_board_or_404(board_id) -> Board:
    board = board_service.find_board(board_id)

    if board is None:
        abort(404)

    return board


def _get_category_or_404(category_id: UUID) -> BoardCategory:
    category = board_category_query_service.find_category_by_id(
        BoardCategoryID(category_id)
    )

    if category is None:
        abort(404)

    return category