• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

liqd / roots / 22574148253

02 Mar 2026 11:33AM UTC coverage: 81.431% (-7.3%) from 88.778%
22574148253

push

github

web-flow
apps/summerization:  Make Testing Playground working again

9 of 25 new or added lines in 2 files covered. (36.0%)

290 existing lines in 22 files now uncovered.

7262 of 8918 relevant lines covered (81.43%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.12
/apps/users/forms.py
1
import logging
1✔
2

3
import xmltodict
1✔
4
from allauth.account.forms import LoginForm
1✔
5
from allauth.account.forms import SignupForm
1✔
6
from allauth.socialaccount.forms import SignupForm as SocialSignupForm
1✔
7
from django import forms
1✔
8
from django.conf import settings
1✔
9
from django.contrib.auth import forms as auth_forms
1✔
10
from django.utils.translation import get_language
1✔
11
from django.utils.translation import gettext_lazy as _
1✔
12
from zeep import Client
1✔
13

14
from apps.captcha.fields import ProsopoCaptchaField
1✔
15
from apps.cms.settings import helpers
1✔
16
from apps.organisations.models import Member
1✔
17
from apps.organisations.models import Organisation
1✔
18
from apps.users.models import User
1✔
19

20
logger = logging.getLogger(__name__)
1✔
21

22
PROSOPO_CAPTCHA_HELP = _(
1✔
23
    "Please complete the captcha verification.<strong>"
24
    "If you are having difficulty please contact us by {}email{}.</strong>"
25
)
26

27

28
class TermsAndCaptchaMixin:
1✔
29
    def __init__(self, *args, **kwargs):
1✔
30
        super().__init__(*args, **kwargs)
1✔
31

32
        if "terms_of_use" not in self.fields:
1✔
UNCOV
33
            self.fields["terms_of_use"] = forms.BooleanField(label=_("Terms of use"))
×
34

35
        if not getattr(settings, "CAPTCHA", False):
1✔
36
            return  # Don't add captcha field
1✔
37

38
        try:
1✔
39
            self.fields["captcha"] = ProsopoCaptchaField(label=_("I am not a robot"))
1✔
40
            self.fields["captcha"].help_text = helpers.add_email_link_to_helptext(
1✔
41
                self.fields["captcha"].help_text, PROSOPO_CAPTCHA_HELP
42
            )
UNCOV
43
        except Exception as e:
×
UNCOV
44
            logger.warning(
×
45
                f"Could not add Prosopo captcha field, are the PROSOPO_SITE_KEY and PROSOPO_SECRET_KEY set : {e}"
46
            )
47

48

49
class DefaultLoginForm(LoginForm):
1✔
50
    def __init__(self, *args, **kwargs):
1✔
51
        super().__init__(*args, **kwargs)
1✔
52
        self.fields["login"].label = _("Username/e-mail")
1✔
53
        del self.fields["login"].widget.attrs["placeholder"]
1✔
54
        del self.fields["password"].widget.attrs["placeholder"]
1✔
55
        self.fields["password"].help_text = ""
1✔
56
        self.fields["login"].widget.attrs["autocomplete"] = "username"
1✔
57
        self.fields["password"].widget.attrs["autocomplete"] = "current-password"
1✔
58
        self.fields["password"].widget.attrs["class"] = "password-toggle"
1✔
59

60

61
class DefaultSignupForm(TermsAndCaptchaMixin, SignupForm):
1✔
62
    terms_of_use = forms.BooleanField(label=_("Terms of use"))
1✔
63
    get_newsletters = forms.BooleanField(
1✔
64
        label=_("I would like to receive further information"),
65
        help_text=_(
66
            "Projects you are following can send you "
67
            "additional information via email."
68
        ),
69
        required=False,
70
    )
71

72
    def __init__(self, *args, **kwargs):
1✔
73
        super().__init__(*args, **kwargs)
1✔
74

75
        self.fields["username"].help_text = _(
1✔
76
            "Your username will appear publicly next to your posts."
77
        )
78
        self.fields["email"].widget.attrs["autofocus"] = True
1✔
79
        del self.fields["username"].widget.attrs["placeholder"]
1✔
80
        del self.fields["email"].widget.attrs["placeholder"]
1✔
81
        del self.fields["password1"].widget.attrs["placeholder"]
1✔
82
        del self.fields["password2"].widget.attrs["placeholder"]
1✔
83
        self.fields["email"].widget.attrs["autocomplete"] = "username"
1✔
84
        self.fields["password1"].widget.attrs["autocomplete"] = "new-password"
1✔
85
        self.fields["password2"].widget.attrs["autocomplete"] = "new-password"
1✔
86
        self.fields["password1"].widget.attrs["class"] = "password-toggle"
1✔
87
        self.fields["password2"].widget.attrs["class"] = "password-toggle"
1✔
88

89
    def save(self, request):
1✔
90
        user = super().save(request)
1✔
91
        if user:
1✔
92
            user.get_newsletters = self.cleaned_data["get_newsletters"]
1✔
93
            user.language = get_language()
1✔
94
            user.save()
1✔
95
            return user
1✔
96

97

98
class GuestCreateForm(TermsAndCaptchaMixin, forms.Form):
1✔
99
    pass
1✔
100

101

102
class GuestConvertForm(DefaultSignupForm):
1✔
103

104
    get_newsletters = forms.BooleanField(
1✔
105
        label=_("I would like to receive further information"),
106
        help_text=_(
107
            "Projects you are following can send you "
108
            "additional information via email."
109
        ),
110
        required=False,
111
    )
112

113
    def __init__(self, *args, **kwargs):
1✔
114
        self.user = kwargs.pop("user", None)
1✔
115
        super().__init__(*args, **kwargs)
1✔
116

117
        if "captcha" in self.fields:
1✔
UNCOV
118
            del self.fields["captcha"]
×
119

120
        self.fields["email"].required = True
1✔
121
        self.fields["username"].required = True
1✔
122
        self.fields["password1"].required = True
1✔
123
        self.fields["password2"].required = True
1✔
124

125
    def clean_email(self):
1✔
126
        email = self.cleaned_data["email"]
1✔
127
        users = User.objects.filter(email__iexact=email)
1✔
128
        if users.exists():
1✔
UNCOV
129
            raise forms.ValidationError("Email already in use.")
×
130
        return email
1✔
131

132
    def save(self, request):
1✔
133
        user = self.user
1✔
134
        user.email = self.cleaned_data["email"]
1✔
135
        user.username = self.cleaned_data["username"]
1✔
136
        user.get_newsletters = self.cleaned_data["get_newsletters"]
1✔
137
        user.set_password(self.cleaned_data["password1"])
1✔
138
        user.save()
1✔
139

140
        return user
1✔
141

142

143
class IgbceSignupForm(DefaultSignupForm):
1✔
144
    member_number = forms.IntegerField(
1✔
145
        label=_("Membership number of IG BCE"),
146
        help_text=_(
147
            "The membership number consists of a seven-digit number "
148
            "and can be found on the membership card."
149
        ),
150
        max_value=99999999999999999999,
151
        min_value=0,
152
    )
153
    birth_date = forms.DateField(
1✔
154
        label=_("Date of birth"),
155
        help_text=_(
156
            "Please also enter your date of birth in the format "
157
            "MM/DD/YYYY for authentication. Only members of the "
158
            "IG BCE can participate."
159
        ),
160
    )
161
    terms_of_use_extra = forms.BooleanField(
1✔
162
        label=_(
163
            "I confirm that I have read and accepted the "
164
            '<a href="/info/ig-bce-datenschutz/" '
165
            'target="_blank">data protection policy</a> of IG '
166
            "BCE."
167
        )
168
    )
169

170
    def validateMemberNumberAndDate(self, member_number, birth_date):
1✔
UNCOV
171
        if not hasattr(settings, "IGBCE_NAV_URL") or not hasattr(
×
172
            settings, "IGBCE_NAV_SECURITYID"
173
        ):
UNCOV
174
            raise forms.ValidationError(
×
175
                _("Something is wrong with the setup - please try again later")
176
            )
177

178
        if Member.objects.filter(member_number=member_number).exists():
×
179
            raise forms.ValidationError(
×
180
                _(
181
                    "There is already a participant with this membership "
182
                    "number. Please check your entry. If this is your "
183
                    "membership number, please send an email to "
184
                    '"zukunftsgewerkschaft@igbce.de".'
185
                )
186
            )
187

UNCOV
188
        result = False
×
UNCOV
189
        try:
×
UNCOV
190
            client = Client("{}".format(settings.IGBCE_NAV_URL))
×
UNCOV
191
            parameters = "{};{}".format(member_number, birth_date.strftime("%d.%m.%Y"))
×
UNCOV
192
            connection_parameters = (
×
193
                "ObjectID:0;SecurityID:{};SetSize:0;UnitopProxyVersion:2.0".format(
194
                    settings.IGBCE_NAV_SECURITYID
195
                )
196
            )
197

198
            response = client.service.SendRequest(
×
199
                functionName="CALCONNECT_MemberNoBirthDate",
200
                functionParameters=parameters,
201
                filters="",
202
                connectionParameters=connection_parameters,
203
            )
204

UNCOV
205
            result_str = xmltodict.parse(response)["Response"]["ResponseData"][
×
206
                "Object"
207
            ]["CalConnectorResult"]
208

UNCOV
209
            if result_str == "true":
×
UNCOV
210
                result = True
×
211

UNCOV
212
        except BaseException:
×
UNCOV
213
            logger.exception("IGBCE API error")
×
UNCOV
214
            raise forms.ValidationError(
×
215
                _("Something is wrong with the setup - please try again later")
216
            )
217

UNCOV
218
        if not result:
×
219
            raise forms.ValidationError(
×
220
                _(
221
                    "Unfortunately, the member number and / or date of birth "
222
                    "could not be linked to an active member account. Please "
223
                    "check your input and try again. If you still have "
224
                    'problems, please contact "zukunftsgewerkschaft@igbce.de".'
225
                )
226
            )
227

228
    def clean(self):
1✔
229
        super().clean()
×
230

UNCOV
231
        if any(self.errors):
×
UNCOV
232
            return self.errors
×
233

234
        member_number = self.cleaned_data.get("member_number")
×
UNCOV
235
        birth_date = self.cleaned_data.get("birth_date")
×
236

UNCOV
237
        self.validateMemberNumberAndDate(member_number, birth_date)
×
238

239
    def save(self, request):
1✔
240
        user = super().save(request)
×
241
        if hasattr(settings, "SITE_ORGANISATION_SLUG"):
×
UNCOV
242
            organisation = Organisation.objects.get(
×
243
                slug=settings.SITE_ORGANISATION_SLUG
244
            )
UNCOV
245
            additional_info = {"birth_date": self.cleaned_data["birth_date"]}
×
UNCOV
246
            member = Member(
×
247
                member=user,
248
                member_number=self.cleaned_data["member_number"],
249
                organisation=organisation,
250
                additional_info=additional_info,
251
            )
UNCOV
252
            member.save()
×
UNCOV
253
        return user
×
254

255

256
class SocialTermsSignupForm(TermsAndCaptchaMixin, SocialSignupForm):
1✔
257
    get_newsletters = forms.BooleanField(
1✔
258
        label=_("I would like to receive further information"),
259
        help_text=_(
260
            "Projects you are following can send you "
261
            "additional information via email."
262
        ),
263
        required=False,
264
    )
265
    email = forms.EmailField(widget=forms.HiddenInput())
1✔
266

267
    def __init__(self, *args, **kwargs):
1✔
268
        super().__init__(*args, **kwargs)
×
269
        self.prevent_enumeration = False
×
UNCOV
270
        self.fields["username"].help_text = _(
×
271
            "Your username will appear publicly next to your posts."
272
        )
UNCOV
273
        del self.fields["username"].widget.attrs["placeholder"]
×
274

275
    def save(self, request):
1✔
276
        user = super().save(request)
×
277
        user.get_newsletters = self.cleaned_data["get_newsletters"]
×
278
        user.language = get_language()
×
UNCOV
279
        user.save()
×
UNCOV
280
        return user
×
281

282

283
class ChangeUserAdminForm(auth_forms.UserChangeForm):
1✔
284
    def clean_username(self):
1✔
285
        username = self.cleaned_data["username"]
×
286
        try:
×
287
            user = User.objects.get(username__iexact=username)
×
UNCOV
288
            if user != self.instance:
×
UNCOV
289
                raise forms.ValidationError(
×
290
                    User._meta.get_field("username").error_messages["unique"]
291
                )
UNCOV
292
        except User.DoesNotExist:
×
293
            pass
×
294

UNCOV
295
        try:
×
UNCOV
296
            user = User.objects.get(email__iexact=username)
×
UNCOV
297
            if user != self.instance:
×
298
                raise forms.ValidationError(
×
299
                    User._meta.get_field("username").error_messages["used_as_email"]
300
                )
301
        except User.DoesNotExist:
×
UNCOV
302
            pass
×
303

UNCOV
304
        return username
×
305

306

307
class AddUserAdminForm(auth_forms.UserCreationForm):
1✔
308
    def clean_username(self):
1✔
UNCOV
309
        username = self.cleaned_data["username"]
×
310
        user = User.objects.filter(username__iexact=username)
×
UNCOV
311
        if user.exists():
×
UNCOV
312
            raise forms.ValidationError(
×
313
                User._meta.get_field("username").error_messages["unique"]
314
            )
315
        else:
UNCOV
316
            user = User.objects.filter(email__iexact=username)
×
UNCOV
317
            if user.exists():
×
UNCOV
318
                raise forms.ValidationError(
×
319
                    User._meta.get_field("username").error_messages["used_as_email"]
320
                )
UNCOV
321
        return username
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc