Source code for turkleton.assignment.answer

# -*- coding: utf-8 -*-
"""
    turkleton.assignment.answer
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Representations for various answer types from uploaded HITs.

"""
import collections
import decimal

import six


[docs]class BaseAnswer(object): """Base class for all answer types. This is a descriptor class.""" #: Indicates that the value of this answer has not been set. _EMPTY = object() #: Value to indicate the default value if none is given _DEFAULT = object() def __init__(self, question_name, default): """Initialize an answer with the given information. :param question_name: The question name :type question_name: str or unicode :param default: The default value :type default: str or unicode """ self.question_name = question_name self.default = default self.value_store = collections.defaultdict(lambda: self._EMPTY) def __get__(self, obj, obtype): """Descriptor method for retrieving attribute value""" if self.value_store[obj] is self._EMPTY: return self.default return self.value_store[obj] def __set__(self, obj, val): """Descriptor method for setting attribute value""" self.value_store[obj] = val
[docs]class TextAnswer(BaseAnswer): """An answer representing simple textual input"""
[docs]class BooleanAnswer(BaseAnswer): """An answer representing a boolean choice""" #: Default mapping of strings to booleans DEFAULT_STRING_TO_BOOL = {'1': True, '0': False} def __init__(self, question_name, default, string_to_bool=None): """Initialize the boolean answer with the given information. :param question_name: A question name :type question_name: str or unicode :param default: Default value for boolean :type default: mixed :param string_to_bool: (Default is {'1': True, '0': False}) A dictionary mapping string values to booleans. :type string_to_bool: dict or None """ super(BooleanAnswer, self).__init__( question_name=question_name, default=default ) self.string_to_bool = ( string_to_bool if string_to_bool else self.DEFAULT_STRING_TO_BOOL ) def __set__(self, obj, val): """Set this value to the given answer. If a string it will attempt to convert it into a boolean. """ if isinstance(val, six.string_types): super(BooleanAnswer, self).__set__( obj, self.string_to_bool.get(val, self._EMPTY) ) else: super(BooleanAnswer, self).__set__(obj, val)
[docs]class IntegerAnswer(BaseAnswer): """Represents an answer with an integer value""" def __set__(self, obj, val): """Casts the given value to an integer""" super(IntegerAnswer, self).__set__(obj, int(val))
[docs]class DecimalAnswer(BaseAnswer): """Represents an answer with a decimal value""" def __set__(self, obj, val): """Casts the given value to a decimal.Decimal""" super(DecimalAnswer, self).__set__(obj, decimal.Decimal(val))
[docs]class SingleChoiceAnswer(TextAnswer): """Represents an answer to a single-choice question"""
[docs]class MultiChoiceAnswer(BaseAnswer): """An answer with multiple potential choices for input""" def __init__(self, question_name, default=BaseAnswer._DEFAULT): super(MultiChoiceAnswer, self).__init__(question_name, default) def __get__(self, obj, objtype): """Return the value or the default value. If default value is _DEFAULT then this will return an empty list""" if self.value_store[obj] is self._EMPTY: return [] if self.default is self._DEFAULT else self.default return self.value_store[obj] def __set__(self, obj, val): """In Mechanical Turk multi-choice answers come across as text answers separated by the pipe (|) character. Handle this type of input here""" if isinstance(val, six.string_types): super(MultiChoiceAnswer, self).__set__( obj, val.split('|') if val else self._EMPTY ) else: super(MultiChoiceAnswer, self).__set__(obj, val)