Passed
Push — master ( 74655b...84d640 )
by Jochen
02:20
created

byceps.blueprints.user.creation.views.create()   D

Complexity

Conditions 11

Size

Total Lines 119
Code Lines 83

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 83
nop 0
dl 0
loc 119
rs 4.3362
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like byceps.blueprints.user.creation.views.create() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"""
2
byceps.blueprints.user.creation.views
3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5
:Copyright: 2006-2019 Jochen Kupperschmidt
6
:License: Modified BSD, see LICENSE for details.
7
"""
8
9
from datetime import datetime
10
from typing import Optional
11
12
from flask import abort, g, request
13
14
from ....config import get_site_mode
15
from ....services.brand import settings_service as brand_settings_service
16
from ....services.consent.transfer.models import Consent, SubjectID
17
from ....services.newsletter.transfer.models import (
18
    ListID as NewsletterListID,
19
    Subscription as NewsletterSubscription,
20
)
21
from ....services.site import service as site_service
22
from ....services.terms import document_service as terms_document_service
23
from ....services.terms import version_service as terms_version_service
24
from ....services.user import creation_service as user_creation_service
25
from ....services.user import service as user_service
26
from ....util.framework.blueprint import create_blueprint
27
from ....util.framework.flash import flash_error, flash_success
28
from ....util.framework.templating import templated
29
from ....util.views import redirect_to
30
31
from .. import signals
32
33
from .forms import UserCreateForm
34
35
36
blueprint = create_blueprint('user_creation', __name__)
37
38
39
@blueprint.route('/create')
40
@templated
41
def create_form(erroneous_form=None):
42
    """Show a form to create a user."""
43
    _abort_if_user_account_creation_disabled()
44
45
    terms_version = terms_version_service.find_current_version_for_brand(
46
        g.brand_id
47
    )
48
49
    privacy_policy_consent_subject_id = (
50
        _find_privacy_policy_consent_subject_id()
51
    )
52
53
    newsletter_list_id = _find_newsletter_list_for_brand()
54
55
    real_name_required = _is_real_name_required()
56
    terms_consent_required = (terms_version is not None)
57
    privacy_policy_consent_required \
58
        = (privacy_policy_consent_subject_id is not None)
59
    newsletter_offered = (newsletter_list_id is not None)
60
61
    if terms_consent_required:
62
        terms_version_id = terms_version.id
63
    else:
64
        terms_version_id = None
65
66
    form = erroneous_form if erroneous_form \
67
        else UserCreateForm(terms_version_id=terms_version_id)
68
69
    _adjust_create_form(
70
        form,
71
        real_name_required,
72
        terms_consent_required,
73
        privacy_policy_consent_required,
74
        newsletter_offered,
75
    )
76
77
    return {'form': form}
78
79
80
@blueprint.route('/', methods=['POST'])
81
def create():
82
    """Create a user."""
83
    _abort_if_user_account_creation_disabled()
84
85
    terms_document_id = terms_document_service.find_document_id_for_brand(
86
        g.brand_id
87
    )
88
89
    privacy_policy_consent_subject_id = (
90
        _find_privacy_policy_consent_subject_id()
91
    )
92
93
    newsletter_list_id = _find_newsletter_list_for_brand()
94
95
    real_name_required = _is_real_name_required()
96
    terms_consent_required = (terms_document_id is not None)
97
    privacy_policy_consent_required \
98
        = (privacy_policy_consent_subject_id is not None)
99
    newsletter_offered = (newsletter_list_id is not None)
100
101
    form = UserCreateForm(request.form)
102
103
    _adjust_create_form(
104
        form,
105
        real_name_required,
106
        terms_consent_required,
107
        privacy_policy_consent_required,
108
        newsletter_offered,
109
    )
110
111
    if not form.validate():
112
        return create_form(form)
113
114
    screen_name = form.screen_name.data.strip()
115
    email_address = form.email_address.data.strip().lower()
116
    password = form.password.data
117
118
    now_utc = datetime.utcnow()
119
120
    if user_service.is_screen_name_already_assigned(screen_name):
121
        flash_error(
122
            'Dieser Benutzername ist bereits einem Benutzerkonto zugeordnet.'
123
        )
124
        return create_form(form)
125
126
    if user_service.is_email_address_already_assigned(email_address):
127
        flash_error(
128
            'Diese E-Mail-Adresse ist bereits einem Benutzerkonto zugeordnet.'
129
        )
130
        return create_form(form)
131
132
    if real_name_required:
133
        first_names = form.first_names.data.strip()
134
        last_name = form.last_name.data.strip()
135
    else:
136
        first_names = None
137
        last_name = None
138
139
    terms_consent = None
140
    if terms_consent_required:
141
        terms_version_id = form.terms_version_id.data
142
        consent_to_terms = form.consent_to_terms.data
143
144
        terms_version = terms_version_service.find_version(terms_version_id)
145
        if terms_version.document_id != terms_document_id:
146
            abort(400, 'Die AGB-Version gehört nicht zu dieser Veranstaltung.')
147
148
        terms_consent = Consent(
149
            user_id=None,  # not available at this point
150
            subject_id=terms_version.consent_subject_id,
151
            expressed_at=now_utc,
152
        )
153
154
    privacy_policy_consent = None
155
    if privacy_policy_consent_required:
156
        privacy_policy_consent = Consent(
157
            user_id=None,  # not available at this point
158
            subject_id=privacy_policy_consent_subject_id,
159
            expressed_at=now_utc,
160
        )
161
162
    newsletter_subscription = None
163
    if newsletter_offered:
164
        subscribe_to_newsletter = form.subscribe_to_newsletter.data
165
        if subscribe_to_newsletter:
166
            newsletter_subscription = NewsletterSubscription(
167
                user_id=None,  # not available at this point
168
                list_id=newsletter_list_id,
169
                expressed_at=now_utc,
170
            )
171
172
    try:
173
        user, event = user_creation_service.create_user(
174
            screen_name,
175
            email_address,
176
            password,
177
            first_names,
178
            last_name,
179
            g.site_id,
180
            terms_consent=terms_consent,
181
            privacy_policy_consent=privacy_policy_consent,
182
            newsletter_subscription=newsletter_subscription,
183
        )
184
    except user_creation_service.UserCreationFailed:
185
        flash_error(
186
            f'Das Benutzerkonto für "{screen_name}" konnte nicht angelegt werden.'
187
        )
188
        return create_form(form)
189
190
    flash_success(
191
        f'Das Benutzerkonto für "{user.screen_name}" wurde angelegt. '
192
        'Bevor du dich damit anmelden kannst, muss zunächst der Link in '
193
        'der an die angegebene Adresse verschickten E-Mail besucht werden.',
194
    )
195
196
    signals.account_created.send(None, event=event)
197
198
    return redirect_to('authentication.login_form')
199
200
201
def _abort_if_user_account_creation_disabled():
202
    if not _is_user_account_creation_enabled():
203
        flash_error('Das Erstellen von Benutzerkonten ist deaktiviert.')
204
        abort(403)
205
206
207
def _is_user_account_creation_enabled():
208
    if get_site_mode().is_admin():
209
        return False
210
211
    site = site_service.get_site(g.site_id)
212
    return site.user_account_creation_enabled
213
214
215
def _adjust_create_form(
216
    form,
217
    real_name_required,
218
    terms_consent_required,
219
    privacy_policy_consent_required,
220
    newsletter_offered,
221
):
222
    if not real_name_required:
223
        del form.first_names
224
        del form.last_name
225
226
    if not terms_consent_required:
227
        del form.terms_version_id
228
        del form.consent_to_terms
229
230
    if not privacy_policy_consent_required:
231
        del form.consent_to_privacy_policy
232
233
    if not newsletter_offered:
234
        del form.subscribe_to_newsletter
235
236
237
def _is_real_name_required() -> bool:
238
    """Return `True` if real name is required.
239
240
    By default, real name is required. It can be disabled by configuring
241
    the string `false` for the brand setting `real_name_required`.
242
    """
243
    value = _find_brand_setting_value('real_name_required')
244
245
    return value != 'false'
246
247
248
def _find_privacy_policy_consent_subject_id() -> Optional[SubjectID]:
249
    """Return the privacy policy consent subject ID configured for this
250
    brand, or `None` if none is configured.
251
    """
252
    value = _find_brand_setting_value('privacy_policy_consent_subject_id')
253
254
    if not value:
255
        return None
256
257
    return SubjectID(value)
258
259
260
def _find_newsletter_list_for_brand() -> Optional[NewsletterListID]:
261
    """Return the newsletter list configured for this brand, or `None`
262
    if none is configured.
263
    """
264
    value = _find_brand_setting_value('newsletter_list_id')
265
266
    if not value:
267
        return None
268
269
    return NewsletterListID(value)
270
271
272
def _find_brand_setting_value(setting_name: str) -> Optional[str]:
273
    """Return the value configured for this brand and the given setting
274
    name, or `None` if not configured.
275
    """
276
    return brand_settings_service.find_setting_value(g.brand_id, setting_name)
277