1 from __future__
import absolute_import
2 from __future__
import unicode_literals
4 from django
import forms
5 from django
.template
.defaultfilters
import mark_safe
6 from django
.utils
.translation
import ugettext_lazy
as _
8 from .utils
import hash_answer
, get_operator
, get_numbers
, calculate
11 class MathCaptchaWidget(forms
.MultiWidget
):
12 def __init__(self
, start_int
=1, end_int
=10, question_tmpl
=None,
13 question_class
=None, attrs
=None):
14 self
.start_int
, self
.end_int
= self
.verify_numbers(start_int
, end_int
)
15 self
.question_class
= question_class
or 'captcha-question'
16 self
.question_tmpl
= (
17 question_tmpl
or _('What is %(num1)i %(operator)s %(num2)i? '))
18 self
.question_html
= None
19 widget_attrs
= {'size': '5'}
20 widget_attrs
.update(attrs
or {})
22 # this is the answer input field
23 forms
.TextInput(attrs
=widget_attrs
),
25 # this is the hashed answer field to compare to
28 super(MathCaptchaWidget
, self
).__init__(widgets
, attrs
)
30 def decompress(self
, value
):
33 def format_output(self
, rendered_widgets
):
34 output
= super(MathCaptchaWidget
, self
).format_output(rendered_widgets
)
35 output
= '%s%s' % (self
.question_html
, output
)
38 def render(self
, name
, value
, attrs
=None):
39 # hash answer and set as the hidden value of form
40 hashed_answer
= self
.generate_captcha()
41 value
= ['', hashed_answer
]
43 return super(MathCaptchaWidget
, self
).render(name
, value
, attrs
=attrs
)
45 def generate_captcha(self
):
46 # get operator for calculation
47 operator
= get_operator()
49 # get integers for calculation
50 x
, y
= get_numbers(self
.start_int
, self
.end_int
, operator
)
52 # set question to display in output
53 self
.set_question(x
, y
, operator
)
55 # preform the calculation
56 total
= calculate(x
, y
, operator
)
58 return hash_answer(total
)
60 def set_question(self
, x
, y
, operator
):
61 # make multiplication operator more human-readable
62 operator_for_label
= '×' if operator
== '*' else operator
63 question
= self
.question_tmpl
% {
65 'operator': operator_for_label
,
69 html
= '<span class="%s">%s</span>' % (self
.question_class
, question
)
70 self
.question_html
= mark_safe(html
)
72 def verify_numbers(self
, start_int
, end_int
):
73 start_int
, end_int
= int(start_int
), int(end_int
)
74 if start_int
< 0 or end_int
< 0:
75 raise Warning('MathCaptchaWidget requires positive integers '
76 'for start_int and end_int.')
77 elif end_int
< start_int
:
78 raise Warning('MathCaptchaWidget requires end_int be greater '
80 return start_int
, end_int