_get_brand_or_404()   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 5
nop 1
dl 0
loc 7
ccs 0
cts 0
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
"""
2
byceps.blueprints.admin.site.views
3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5
:Copyright: 2014-2024 Jochen Kupperschmidt
6
:License: Revised BSD (see `LICENSE` file for details)
7
"""
8
9 1
from collections.abc import Iterable, Iterator
10 1
import dataclasses
11
12 1
from flask import abort, request
13 1
from flask_babel import gettext
14
15 1
from byceps.services.board import board_service
16 1
from byceps.services.brand import brand_service
17 1
from byceps.services.brand.models import Brand
18 1
from byceps.services.news import news_channel_service
19 1
from byceps.services.party import party_service
20 1
from byceps.services.shop.shop import shop_service
21 1
from byceps.services.shop.storefront import storefront_service
22 1
from byceps.services.shop.storefront.models import Storefront, StorefrontID
23 1
from byceps.services.site import site_service, site_setting_service
24 1
from byceps.services.site.models import Site, SiteWithBrand
25 1
from byceps.util.framework.blueprint import create_blueprint
26 1
from byceps.util.framework.flash import flash_error, flash_success
27 1
from byceps.util.framework.templating import templated
28 1
from byceps.util.views import (
29
    permission_required,
30
    redirect_to,
31
)
32
33
from .forms import AssignNewsChannelsForm, CreateForm, UpdateForm
34 1
35
36
blueprint = create_blueprint('site_admin', __name__)
37 1
38
39
@blueprint.get('/')
40 1
@permission_required('site.view')
41 1
@templated
42 1
def index():
43 1
    """List all sites."""
44
    brands = brand_service.get_all_brands()
45 1
    brands.sort(key=lambda brand: brand.title)
46 1
47
    sites = site_service.get_all_sites()
48 1
    sites = list(_sites_to_sites_with_brand(sites, brands))
49 1
    sites.sort(key=lambda site: (site.title, site.party_id))
50 1
51
    parties = party_service.get_all_parties()
52 1
    party_titles_by_id = {p.id: p.title for p in parties}
53 1
54
    storefronts_by_id = _get_storefronts_by_id(sites)
55 1
56
    return {
57 1
        'sites': sites,
58
        'brands': brands,
59
        'party_titles_by_id': party_titles_by_id,
60
        'storefronts_by_id': storefronts_by_id,
61
    }
62
63
64
@blueprint.get('/for_brand/<brand_id>')
65 1
@permission_required('site.view')
66 1
@templated
67 1
def index_for_brand(brand_id):
68 1
    """List sites for this brand."""
69
    brand = brand_service.find_brand(brand_id)
70
    if brand is None:
71
        abort(404)
72
73
    sites = site_service.get_sites_for_brand(brand.id)
74
    sites = [_site_to_site_with_brand(site, brand) for site in sites]
75
    sites.sort(key=lambda site: (site.title, site.party_id))
76
77
    parties = party_service.get_all_parties()
78
    party_titles_by_id = {p.id: p.title for p in parties}
79
80
    storefronts_by_id = _get_storefronts_by_id(sites)
81
82
    return {
83
        'sites': sites,
84
        'brand': brand,
85
        'party_titles_by_id': party_titles_by_id,
86
        'storefronts_by_id': storefronts_by_id,
87
    }
88
89
90
def _sites_to_sites_with_brand(
91 1
    sites: Iterable[Site], brands: Iterable[Brand]
92
) -> Iterator[SiteWithBrand]:
93
    brands_by_id = {brand.id: brand for brand in brands}
94 1
95
    for site in sites:
96 1
        brand = brands_by_id[site.brand_id]
97 1
        yield _site_to_site_with_brand(site, brand)
98 1
99
100
def _site_to_site_with_brand(site: Site, brand: Brand) -> SiteWithBrand:
101 1
    site_tuple = dataclasses.astuple(site)
102 1
    brand_tuple = (brand,)
103 1
104
    return SiteWithBrand(*(site_tuple + brand_tuple))
105 1
106
107
def _get_storefronts_by_id(sites) -> dict[StorefrontID, Storefront]:
108 1
    storefront_ids = {
109 1
        site.storefront_id for site in sites if site.storefront_id is not None
110
    }
111
    storefronts = storefront_service.find_storefronts(storefront_ids)
112 1
    return {storefront.id: storefront for storefront in storefronts}
113 1
114
115
@blueprint.get('/sites/<site_id>')
116 1
@permission_required('site.view')
117 1
@templated
118 1
def view(site_id):
119 1
    """Show a site's settings."""
120
    site = site_service.find_site(site_id)
121 1
    if site is None:
122 1
        abort(404)
123
124
    brand = brand_service.find_brand(site.brand_id)
125 1
126
    news_channels = news_channel_service.get_channels(site.news_channel_ids)
127 1
128
    if site.board_id:
129 1
        board = board_service.find_board(site.board_id)
130 1
    else:
131
        board = None
132
133
    if site.storefront_id:
134 1
        storefront = storefront_service.get_storefront(site.storefront_id)
135
        shop = shop_service.get_shop(storefront.shop_id)
136
    else:
137
        storefront = None
138 1
        shop = None
139 1
140
    settings = site_setting_service.get_settings(site.id)
141 1
142
    return {
143 1
        'site': site,
144
        'brand': brand,
145
        'news_channels': news_channels,
146
        'board': board,
147
        'shop': shop,
148
        'storefront': storefront,
149
        'settings': settings,
150
    }
151
152
153
@blueprint.get('/sites/create/for_brand/<brand_id>')
154 1
@permission_required('site.create')
155 1
@templated
156 1
def create_form(brand_id, erroneous_form=None):
157 1
    """Show form to create a site."""
158
    brand = _get_brand_or_404(brand_id)
159 1
160
    form = erroneous_form if erroneous_form else CreateForm()
161 1
    _fill_in_common_form_choices(form, brand.id)
162 1
163
    return {
164 1
        'brand': brand,
165
        'form': form,
166
    }
167
168
169
@blueprint.post('/sites/for_brand/<brand_id>')
170 1
@permission_required('site.create')
171 1
def create(brand_id):
172 1
    """Create a site."""
173
    brand = _get_brand_or_404(brand_id)
174 1
175
    form = CreateForm(request.form)
176 1
    _fill_in_common_form_choices(form, brand.id)
177 1
178
    if not form.validate():
179 1
        return create_form(brand_id, form)
180
181
    site_id = form.id.data.strip().lower()
182 1
    title = form.title.data.strip()
183 1
    server_name = form.server_name.data.strip()
184 1
    party_id = form.party_id.data
185 1
    enabled = form.enabled.data
186 1
    user_account_creation_enabled = form.user_account_creation_enabled.data
187 1
    login_enabled = form.login_enabled.data
188 1
    board_id = form.board_id.data.strip() or None
189 1
    storefront_id = form.storefront_id.data.strip() or None
190 1
    is_intranet = form.is_intranet.data
191 1
    check_in_on_login = form.check_in_on_login.data
192 1
193
    if party_id:
194 1
        party = party_service.find_party(party_id)
195
        if not party:
196
            flash_error(
197
                gettext(
198
                    'Party ID "%(party_id)s" is unknown.',
199
                    party_id=party_id,
200
                )
201
            )
202
            return create_form(brand_id, form)
203
    else:
204
        party_id = None
205 1
206
    site = site_service.create_site(
207 1
        site_id,
208
        title,
209
        server_name,
210
        brand.id,
211
        enabled=enabled,
212
        user_account_creation_enabled=user_account_creation_enabled,
213
        login_enabled=login_enabled,
214
        party_id=party_id,
215
        board_id=board_id,
216
        storefront_id=storefront_id,
217
        is_intranet=is_intranet,
218
        check_in_on_login=check_in_on_login,
219
    )
220
221
    flash_success(
222 1
        gettext('Site "%(title)s" has been created.', title=site.title)
223
    )
224
225
    return redirect_to('.view', site_id=site.id)
226 1
227
228
@blueprint.get('/sites/<site_id>/update')
229 1
@permission_required('site.update')
230 1
@templated
231 1
def update_form(site_id, erroneous_form=None):
232 1
    """Show form to update the site."""
233
    site = _get_site_or_404(site_id)
234 1
235
    form = (
236 1
        erroneous_form
237 1
        if erroneous_form
238 1
        else UpdateForm(site.title, site.server_name, obj=site)
239
    )
240 1
    _fill_in_common_form_choices(form, site.brand_id)
241
242
    return {
243
        'site': site,
244
        'form': form,
245
    }
246 1
247 1
248 1
@blueprint.post('/sites/<site_id>')
249
@permission_required('site.update')
250
def update(site_id):
251
    """Update the site."""
252
    site = _get_site_or_404(site_id)
253
254
    form = UpdateForm(site.title, site.server_name, request.form)
255
    _fill_in_common_form_choices(form, site.brand_id)
256
257
    if not form.validate():
258
        return update_form(site.id, form)
259
260
    title = form.title.data.strip()
261
    server_name = form.server_name.data.strip()
262
    party_id = form.party_id.data
263
    enabled = form.enabled.data
264
    user_account_creation_enabled = form.user_account_creation_enabled.data
265
    login_enabled = form.login_enabled.data
266
    board_id = form.board_id.data.strip() or None
267
    storefront_id = form.storefront_id.data.strip() or None
268
    is_intranet = form.is_intranet.data
269
    check_in_on_login = form.check_in_on_login.data
270
    archived = form.archived.data
271
272
    if party_id:
273
        party = party_service.find_party(party_id)
274
        if not party:
275
            flash_error(
276
                gettext(
277
                    'Party ID "%(party_id)s" is unknown.',
278
                    party_id=party_id,
279
                )
280
            )
281
            return update_form(site.id, form)
282
    else:
283
        party_id = None
284
285
    try:
286
        site = site_service.update_site(
287
            site.id,
288
            title,
289
            server_name,
290
            party_id,
291
            enabled,
292
            user_account_creation_enabled,
293
            login_enabled,
294
            board_id,
295
            storefront_id,
296
            is_intranet,
297
            check_in_on_login,
298
            archived,
299
        )
300
    except site_service.UnknownSiteIdError:
301
        abort(404, f'Unknown site ID "{site_id}".')
302
303
    flash_success(
304
        gettext('Site "%(title)s" has been updated.', title=site.title)
305
    )
306
307
    return redirect_to('.view', site_id=site.id)
308
309
310
def _fill_in_common_form_choices(form, brand_id):
311 1
    form.set_party_choices(brand_id)
312 1
    form.set_board_choices(brand_id)
313 1
    form.set_storefront_choices(brand_id)
314 1
315
316
# -------------------------------------------------------------------- #
317
# news channels
318
319
320
@blueprint.get('/sites/<site_id>/news_channels/update')
321 1
@permission_required('site.update')
322 1
@templated
323 1
def assign_news_channels_form(site_id, erroneous_form=None):
324 1
    """Show form to assign news channels to the site."""
325
    site = _get_site_or_404(site_id)
326
327
    form = (
328
        erroneous_form
329
        if erroneous_form
330
        else AssignNewsChannelsForm(news_channel_ids=site.news_channel_ids)
331
    )
332
    form.set_news_channel_id_choices(site.brand_id)
333
334
    return {
335
        'site': site,
336
        'form': form,
337 1
    }
338 1
339 1
340
@blueprint.post('/sites/<site_id>/news_channels')
341
@permission_required('site.update')
342
def assign_news_channels(site_id):
343
    """Assign news channels to the site."""
344
    site = _get_site_or_404(site_id)
345
346
    form = AssignNewsChannelsForm(request.form)
347
    form.set_news_channel_id_choices(site.brand_id)
348
349
    if not form.validate():
350
        return assign_news_channels_form(site.id, form)
351
352
    all_channels = news_channel_service.get_channels_for_brand(site.brand_id)
353
    all_channel_ids = {c.id for c in all_channels}
354
355
    channel_ids_to_add = set(form.news_channel_ids.data)
356
    channel_ids_to_remove = all_channel_ids.difference(channel_ids_to_add)
357
358
    for channel_id in channel_ids_to_add:
359
        site_service.add_news_channel(site.id, channel_id)
360
361
    for channel_id in channel_ids_to_remove:
362
        site_service.remove_news_channel(site.id, channel_id)
363
364
    flash_success(gettext('Changes have been saved.'))
365 1
366 1
    return redirect_to('.view', site_id=site.id)
367 1
368 1
369
# -------------------------------------------------------------------- #
370
371
372
def _get_site_or_404(site_id):
373
    site = site_service.find_site(site_id)
374
375
    if site is None:
376
        abort(404)
377
378
    return site
379
380
381
def _get_brand_or_404(brand_id):
382
    brand = brand_service.find_brand(brand_id)
383
384
    if brand is None:
385
        abort(404)
386
387
    return brand
388