byceps.blueprints.admin.board.views   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 399
Duplicated Lines 0 %

Test Coverage

Coverage 73.68%

Importance

Changes 0
Metric Value
eloc 236
dl 0
loc 399
ccs 126
cts 171
cp 0.7368
rs 9.92
c 0
b 0
f 0
wmc 31

18 Functions

Rating   Name   Duplication   Size   Complexity  
A _get_board_or_404() 0 7 2
A category_copy_from_form() 0 17 1
A board_create() 0 21 2
A category_delete() 0 20 2
A category_move_up() 0 22 2
A category_create_form() 0 24 2
A category_update_form() 0 18 2
A _get_category_or_404() 0 9 2
A board_index_for_brand() 0 23 1
A category_create() 0 22 2
A category_move_down() 0 22 2
A board_create_form() 0 12 2
A category_unhide() 0 12 1
A board_view() 0 16 1
A category_update() 0 22 2
A category_hide() 0 11 1
A _get_source_category() 0 7 2
A _get_brand_or_404() 0 7 2
1
"""
2
byceps.blueprints.admin.board.views
3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5
:Copyright: 2014-2024 Jochen Kupperschmidt
6
:License: Revised BSD (see `LICENSE` file for details)
7
"""
8
9 1
from dataclasses import dataclass
10
from uuid import UUID
11 1
12
from flask import abort, request
13 1
from flask_babel import gettext
14 1
15
from byceps.services.board import (
16 1
    board_category_command_service,
17
    board_category_query_service,
18
    board_posting_query_service,
19
    board_service,
20
    board_topic_query_service,
21
)
22
from byceps.services.board.models import Board, BoardCategory, BoardCategoryID
23 1
from byceps.services.brand import brand_service
24 1
from byceps.util.framework.blueprint import create_blueprint
25 1
from byceps.util.framework.flash import flash_error, flash_success
26 1
from byceps.util.framework.templating import templated
27 1
from byceps.util.views import (
28 1
    permission_required,
29
    redirect_to,
30
    respond_no_content,
31
)
32
33
from .forms import BoardCreateForm, CategoryCreateForm, CategoryUpdateForm
34 1
35
36
blueprint = create_blueprint('board_admin', __name__)
37 1
38
39
@dataclass(frozen=True)
40 1
class BoardStats:
41 1
    category_count: int
42 1
    topic_count: int
43 1
    posting_count: int
44 1
45
46
# -------------------------------------------------------------------- #
47
# boards
48
49
50
@blueprint.get('/brands/<brand_id>')
51 1
@permission_required('board_category.view')
52 1
@templated
53 1
def board_index_for_brand(brand_id):
54 1
    """List categories for that brand."""
55
    brand = _get_brand_or_404(brand_id)
56 1
57
    boards = board_service.get_boards_for_brand(brand.id)
58 1
    board_ids = [board.id for board in boards]
59 1
60
    stats_by_board_id = {
61 1
        board_id: BoardStats(
62
            board_category_query_service.count_categories_for_board(board_id),
63
            board_topic_query_service.count_topics_for_board(board_id),
64
            board_posting_query_service.count_postings_for_board(board_id),
65
        )
66
        for board_id in board_ids
67
    }
68
69
    return {
70 1
        'boards': boards,
71
        'brand': brand,
72
        'stats_by_board_id': stats_by_board_id,
73
    }
74
75
76
@blueprint.get('/boards/<board_id>')
77 1
@permission_required('board_category.view')
78 1
@templated
79 1
def board_view(board_id):
80 1
    """View the board."""
81
    board = _get_board_or_404(board_id)
82 1
83
    brand = brand_service.find_brand(board.brand_id)
84 1
85
    categories = board_category_query_service.get_categories(board.id)
86 1
87
    return {
88 1
        'board_id': board.id,
89
        'board_brand_id': board.brand_id,
90
        'brand': brand,
91
        'categories': categories,
92
    }
93
94
95
@blueprint.get('/for_brand/<brand_id>/boards/create')
96 1
@permission_required('board.create')
97 1
@templated
98 1
def board_create_form(brand_id, erroneous_form=None):
99 1
    """Show form to create a board."""
100
    brand = _get_brand_or_404(brand_id)
101 1
102
    form = erroneous_form if erroneous_form else BoardCreateForm()
103 1
104
    return {
105 1
        'brand': brand,
106
        'form': form,
107
    }
108
109
110
@blueprint.post('/for_brand/<brand_id>/boards')
111 1
@permission_required('board.create')
112 1
def board_create(brand_id):
113 1
    """Create a board."""
114
    brand = _get_brand_or_404(brand_id)
115 1
116
    form = BoardCreateForm(request.form)
117 1
    if not form.validate():
118 1
        return board_create_form(brand.id, form)
119
120
    board_id = form.board_id.data.strip().lower()
121 1
122
    board = board_service.create_board(brand, board_id)
123 1
124
    flash_success(
125 1
        gettext(
126
            'Board with ID "%(board_id)s" has been created.',
127
            board_id=board.id,
128
        )
129
    )
130
    return redirect_to('.board_view', board_id=board.id)
131 1
132
133
# -------------------------------------------------------------------- #
134
# categories
135
136
137
@blueprint.get('/categories/for_board/<board_id>/copy_from/<source_board_id>')
138 1
@permission_required('board_category.create')
139 1
@templated
140 1
def category_copy_from_form(board_id, source_board_id, erroneous_form=None):
141 1
    """Show form to copy an existing category to this board."""
142
    board = _get_board_or_404(board_id)
143
144
    brand = brand_service.find_brand(board.brand_id)
145
146
    source_board = _get_board_or_404(source_board_id)
147
148
    categories = board_category_query_service.get_categories(source_board.id)
149
150
    return {
151
        'board': board,
152
        'brand': brand,
153
        'categories': categories,
154
    }
155
156
157
@blueprint.get('/categories/for_board/<board_id>/create')
158 1
@permission_required('board_category.create')
159 1
@templated
160 1
def category_create_form(board_id, erroneous_form=None):
161 1
    """Show form to create a category."""
162
    board = _get_board_or_404(board_id)
163 1
164
    brand = brand_service.find_brand(board.brand_id)
165 1
166
    brand_boards = board_service.get_boards_for_brand(brand.id)
167 1
168
    source_category = _get_source_category()
169 1
170
    form = (
171 1
        erroneous_form
172
        if erroneous_form
173
        else CategoryCreateForm(obj=source_category)
174
    )
175
176
    return {
177 1
        'board': board,
178
        'brand': brand,
179
        'brand_boards': brand_boards,
180
        'form': form,
181
    }
182
183
184
def _get_source_category() -> BoardCategory | None:
185 1
    source_category_id = request.args.get('source_category_id')
186 1
    if not source_category_id:
187 1
        return None
188 1
189
    return board_category_query_service.find_category_by_id(
190
        BoardCategoryID(UUID(source_category_id))
191
    )
192
193 1
194 1
@blueprint.post('/categories/for_board/<board_id>')
195 1
@permission_required('board_category.create')
196
def category_create(board_id):
197 1
    """Create a category."""
198
    board = _get_board_or_404(board_id)
199 1
200 1
    form = CategoryCreateForm(request.form)
201
    if not form.validate():
202
        return category_create_form(board.id, form)
203 1
204 1
    slug = form.slug.data.strip().lower()
205 1
    title = form.title.data.strip()
206
    description = form.description.data.strip()
207 1
208
    category = board_category_command_service.create_category(
209
        board.id, slug, title, description
210
    )
211 1
212
    flash_success(
213
        gettext('Category "%(title)s" has been created.', title=category.title)
214 1
    )
215
    return redirect_to('.board_view', board_id=board.id)
216
217 1
218 1
@blueprint.get('/categories/<uuid:category_id>/update')
219 1
@permission_required('board_category.update')
220 1
@templated
221
def category_update_form(category_id, erroneous_form=None):
222 1
    """Show form to update the category."""
223
    category = _get_category_or_404(category_id)
224 1
225 1
    board = board_service.find_board(category.board_id)
226
    brand = brand_service.find_brand(board.brand_id)
227 1
228
    form = (
229
        erroneous_form if erroneous_form else CategoryUpdateForm(obj=category)
230
    )
231 1
232
    return {
233
        'category': category,
234
        'brand': brand,
235
        'form': form,
236
    }
237
238 1
239 1
@blueprint.post('/categories/<uuid:category_id>')
240 1
@permission_required('board_category.update')
241
def category_update(category_id):
242
    """Update the category."""
243
    category = _get_category_or_404(category_id)
244
245
    form = CategoryUpdateForm(request.form)
246
    if not form.validate():
247
        return category_update_form(category_id, form)
248
249
    slug = form.slug.data
250
    title = form.title.data
251
    description = form.description.data
252
253
    category = board_category_command_service.update_category(
254
        category.id, slug, title, description
255
    )
256
257
    flash_success(
258
        gettext('Category "%(title)s" has been updated.', title=category.title)
259
    )
260
    return redirect_to('.board_view', board_id=category.board_id)
261
262 1
263 1
@blueprint.post('/categories/<uuid:category_id>/flags/hidden')
264 1
@permission_required('board_category.update')
265 1
@respond_no_content
266
def category_hide(category_id):
267
    """Hide the category."""
268
    category = _get_category_or_404(category_id)
269
270
    board_category_command_service.hide_category(category.id)
271
272
    flash_success(
273
        gettext('Category "%(title)s" has been hidden.', title=category.title)
274
    )
275
276 1
277 1
@blueprint.delete('/categories/<uuid:category_id>/flags/hidden')
278 1
@permission_required('board_category.update')
279 1
@respond_no_content
280
def category_unhide(category_id):
281
    """Un-hide the category."""
282
    category = _get_category_or_404(category_id)
283
284
    board_category_command_service.unhide_category(category.id)
285
286
    flash_success(
287
        gettext(
288
            'Category "%(title)s" has been made visible.', title=category.title
289
        )
290
    )
291
292 1
293 1
@blueprint.post('/categories/<uuid:category_id>/up')
294 1
@permission_required('board_category.update')
295 1
@respond_no_content
296
def category_move_up(category_id):
297
    """Move the category upwards by one position."""
298
    category = _get_category_or_404(category_id)
299
300
    result = board_category_command_service.move_category_up(category.id)
301
302
    if result.is_err():
303
        flash_error(
304
            gettext(
305
                'Category "%(title)s" is already at the top.',
306
                title=category.title,
307
            )
308
        )
309
        return
310
311
    flash_success(
312
        gettext(
313
            'Category "%(title)s" has been moved upwards by one position.',
314
            title=category.title,
315
        )
316
    )
317
318 1
319 1
@blueprint.post('/categories/<uuid:category_id>/down')
320 1
@permission_required('board_category.update')
321 1
@respond_no_content
322
def category_move_down(category_id):
323
    """Move the category downwards by one position."""
324
    category = _get_category_or_404(category_id)
325
326
    result = board_category_command_service.move_category_down(category.id)
327
328
    if result.is_err():
329
        flash_error(
330
            gettext(
331
                'Category "%(title)s" is already at the bottom.',
332
                title=category.title,
333
            )
334
        )
335
        return
336
337
    flash_success(
338
        gettext(
339
            'Category "%(title)s" has been moved downwards by one position.',
340
            title=category.title,
341
        )
342
    )
343
344 1
345 1
@blueprint.delete('/categories/<uuid:category_id>')
346 1
@permission_required('board_category.create')
347 1
@respond_no_content
348
def category_delete(category_id):
349
    """Delete a category."""
350
    category = _get_category_or_404(category_id)
351
352
    result = board_category_command_service.delete_category(category.id)
353
354
    if result.is_err():
355
        flash_error(
356
            gettext(
357
                'Category "%(title)s" could not be deleted.',
358
                title=category.title,
359
            )
360
        )
361
        return
362
363
    flash_success(
364
        gettext('Category "%(title)s" has been deleted.', title=category.title)
365
    )
366
367
368
# -------------------------------------------------------------------- #
369
# helpers
370
371 1
372 1
def _get_brand_or_404(brand_id):
373
    brand = brand_service.find_brand(brand_id)
374 1
375
    if brand is None:
376
        abort(404)
377 1
378
    return brand
379
380 1
381 1
def _get_board_or_404(board_id) -> Board:
382
    board = board_service.find_board(board_id)
383 1
384
    if board is None:
385
        abort(404)
386 1
387
    return board
388
389 1
390 1
def _get_category_or_404(category_id: UUID) -> BoardCategory:
391
    category = board_category_query_service.find_category_by_id(
392 1
        BoardCategoryID(category_id)
393
    )
394
395 1
    if category is None:
396
        abort(404)
397
398
    return category
399