Skip to content

Instantly share code, notes, and snippets.

@mitchellrj
Created August 31, 2012 15:26
Show Gist options
  • Save mitchellrj/3554584 to your computer and use it in GitHub Desktop.
Save mitchellrj/3554584 to your computer and use it in GitHub Desktop.
deform widget for a turing test
<input type="hidden" name="__start__" value="${field.name}:mapping"/>
<div id="${field.oid}">
<p>Solve this problem:</p>
<p class="turing-challange">${challenge}</p>
<input type="hidden"
name="${field.oid}-challenge"
value="${challenge}"/>
<input type="text"
name="${field.oid}"/>
</div>
<input type="hidden" name="__end__" value="${field.name}:mapping"/>
import random
import colander
from deform import widget
# Largely based on this CAPTCHA widget example: https://gist.github.com/701444
class TuringTestWidget(widget.TextInputWidget):
template = 'turing_test_widget'
readonly_template = 'turing_test_widget'
requirements = ()
complexity = 2
symbols='+-*'
number_range = (0, 10)
def _create_challenge(self):
complexity = self.complexity
if isinstance(complexity, tuple):
complexity = random.randrange(*complexity)
if not complexity:
return ''
challenge_string = str(random.randrange(*self.number_range))
for i in xrange(1, complexity):
challenge_string += ' %s %s' % (random.choice(self.symbols),
random.randrange(*self.number_range))
return challenge_string
def _solve_challenge(self, challenge):
# this may be inefficient, but it should be secure -
# any exceptions are caught by the caller and raised as invalid
challenge_parts = challenge.split()
challenge_parts.reverse()
if not challenge_parts:
return ''
num = int(challenge_parts.pop())
assert self.number_range[0] <= num < self.number_range[1]
safe_challenge = str(num)
while challenge_parts:
symbol = challenge_parts.pop()
assert symbol in self.symbols
safe_challenge += symbol
num = int(challenge_parts.pop())
assert self.number_range[0] <= num < self.number_range[1]
safe_challenge += str(num)
return str(eval(safe_challenge, {'__builtins__': {}}, {}))
def serialize(self, field, cstruct, readonly=False):
if cstruct in (colander.null, None):
cstruct = ''
template = readonly and self.readonly_template or self.template
return field.renderer(template, field=field, cstruct=cstruct, challenge=self._create_challenge())
def deserialize(self, field, pstruct):
if pstruct is colander.null:
return colander.null
response = (pstruct.get(field.oid) or '').strip()
challenge = pstruct.get('%s-challenge' % (field.oid,)) or ''
if not response and self.complexity and field.required:
raise colander.Invalid(field.schema, 'Required')
elif not response and (not self.complexity or not field.required):
return pstruct
try:
expected = self._solve_challenge(challenge)
except:
raise colander.Invalid(field.schema, "Invalid challenge.")
if response != expected:
message = "No, that's not right. Try a different question."
raise colander.Invalid(field.schema, message)
return pstruct
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment