_site_to_site_with_brand()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nop 2
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 1
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 = erroneous_form if erroneous_form else UpdateForm(obj=site)
236 1
    _fill_in_common_form_choices(form, site.brand_id)
237 1
238 1
    return {
239
        'site': site,
240 1
        'form': form,
241
    }
242
243
244
@blueprint.post('/sites/<site_id>')
245
@permission_required('site.update')
246 1
def update(site_id):
247 1
    """Update the site."""
248 1
    site = _get_site_or_404(site_id)
249
250
    form = UpdateForm(request.form)
251
    _fill_in_common_form_choices(form, site.brand_id)
252
253
    if not form.validate():
254
        return update_form(site.id, form)
255
256
    title = form.title.data.strip()
257
    server_name = form.server_name.data.strip()
258
    party_id = form.party_id.data
259
    enabled = form.enabled.data
260
    user_account_creation_enabled = form.user_account_creation_enabled.data
261
    login_enabled = form.login_enabled.data
262
    board_id = form.board_id.data.strip() or None
263
    storefront_id = form.storefront_id.data.strip() or None
264
    is_intranet = form.is_intranet.data
265
    check_in_on_login = form.check_in_on_login.data
266
    archived = form.archived.data
267
268
    if party_id:
269
        party = party_service.find_party(party_id)
270
        if not party:
271
            flash_error(
272
                gettext(
273
                    'Party ID "%(party_id)s" is unknown.',
274
                    party_id=party_id,
275
                )
276
            )
277
            return update_form(site.id, form)
278
    else:
279
        party_id = None
280
281
    try:
282
        site = site_service.update_site(
283
            site.id,
284
            title,
285
            server_name,
286
            party_id,
287
            enabled,
288
            user_account_creation_enabled,
289
            login_enabled,
290
            board_id,
291
            storefront_id,
292
            is_intranet,
293
            check_in_on_login,
294
            archived,
295
        )
296
    except site_service.UnknownSiteIdError:
297
        abort(404, f'Unknown site ID "{site_id}".')
298
299
    flash_success(
300
        gettext('Site "%(title)s" has been updated.', title=site.title)
301
    )
302
303
    return redirect_to('.view', site_id=site.id)
304
305
306
def _fill_in_common_form_choices(form, brand_id):
307
    form.set_party_choices(brand_id)
308
    form.set_board_choices(brand_id)
309
    form.set_storefront_choices(brand_id)
310
311 1
312 1
# -------------------------------------------------------------------- #
313 1
# news channels
314 1
315
316
@blueprint.get('/sites/<site_id>/news_channels/update')
317
@permission_required('site.update')
318
@templated
319
def assign_news_channels_form(site_id, erroneous_form=None):
320
    """Show form to assign news channels to the site."""
321 1
    site = _get_site_or_404(site_id)
322 1
323 1
    form = (
324 1
        erroneous_form
325
        if erroneous_form
326
        else AssignNewsChannelsForm(news_channel_ids=site.news_channel_ids)
327
    )
328
    form.set_news_channel_id_choices(site.brand_id)
329
330
    return {
331
        'site': site,
332
        'form': form,
333
    }
334
335
336
@blueprint.post('/sites/<site_id>/news_channels')
337 1
@permission_required('site.update')
338 1
def assign_news_channels(site_id):
339 1
    """Assign news channels to the site."""
340
    site = _get_site_or_404(site_id)
341
342
    form = AssignNewsChannelsForm(request.form)
343
    form.set_news_channel_id_choices(site.brand_id)
344
345
    if not form.validate():
346
        return assign_news_channels_form(site.id, form)
347
348
    all_channels = news_channel_service.get_channels_for_brand(site.brand_id)
349
    all_channel_ids = {c.id for c in all_channels}
350
351
    channel_ids_to_add = set(form.news_channel_ids.data)
352
    channel_ids_to_remove = all_channel_ids.difference(channel_ids_to_add)
353
354
    for channel_id in channel_ids_to_add:
355
        site_service.add_news_channel(site.id, channel_id)
356
357
    for channel_id in channel_ids_to_remove:
358
        site_service.remove_news_channel(site.id, channel_id)
359
360
    flash_success(gettext('Changes have been saved.'))
361
362
    return redirect_to('.view', site_id=site.id)
363
364
365 1
# -------------------------------------------------------------------- #
366 1
367 1
368 1
def _get_site_or_404(site_id):
369
    site = site_service.find_site(site_id)
370
371
    if site is None:
372
        abort(404)
373
374
    return site
375
376
377
def _get_brand_or_404(brand_id):
378
    brand = brand_service.find_brand(brand_id)
379
380
    if brand is None:
381
        abort(404)
382
383
    return brand
384