~jk0/nova/xs-ipv6

« back to all changes in this revision

Viewing changes to vendor/boto/boto/mturk/question.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2006,2007 Mitch Garnaat http://garnaat.org/
 
2
#
 
3
# Permission is hereby granted, free of charge, to any person obtaining a
 
4
# copy of this software and associated documentation files (the
 
5
# "Software"), to deal in the Software without restriction, including
 
6
# without limitation the rights to use, copy, modify, merge, publish, dis-
 
7
# tribute, sublicense, and/or sell copies of the Software, and to permit
 
8
# persons to whom the Software is furnished to do so, subject to the fol-
 
9
# lowing conditions:
 
10
#
 
11
# The above copyright notice and this permission notice shall be included
 
12
# in all copies or substantial portions of the Software.
 
13
#
 
14
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 
15
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
 
16
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
 
17
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
 
18
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
19
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 
20
# IN THE SOFTWARE.
 
21
 
 
22
class Question(object):
 
23
    
 
24
    QUESTION_XML_TEMPLATE = """<Question><QuestionIdentifier>%s</QuestionIdentifier>%s<IsRequired>%s</IsRequired>%s%s</Question>"""
 
25
    DISPLAY_NAME_XML_TEMPLATE = """<DisplayName>%s</DisplayName>"""
 
26
    
 
27
    def __init__(self, identifier, content, answer_spec, is_required=False, display_name=None): #amount=0.0, currency_code='USD'):
 
28
        self.identifier = identifier
 
29
        self.content = content
 
30
        self.answer_spec = answer_spec
 
31
        self.is_required = is_required
 
32
        self.display_name = display_name
 
33
    
 
34
    def get_as_params(self, label='Question', identifier=None):
 
35
        
 
36
        if identifier is None:
 
37
            raise ValueError("identifier (QuestionIdentifier) is required per MTurk spec.")
 
38
        
 
39
        return { label : self.get_as_xml() }
 
40
    
 
41
    def get_as_xml(self):
 
42
        # add the display name if required
 
43
        display_name_xml = ''
 
44
        if self.display_name:
 
45
            display_name_xml = self.DISPLAY_NAME_XML_TEMPLATE %(self.display_name)
 
46
 
 
47
        ret = Question.QUESTION_XML_TEMPLATE % (self.identifier, 
 
48
                                                display_name_xml,
 
49
                                                str(self.is_required).lower(),
 
50
                                                self.content.get_as_xml(), 
 
51
                                                self.answer_spec.get_as_xml())
 
52
 
 
53
        return ret
 
54
 
 
55
class ExternalQuestion(object):
 
56
    
 
57
    EXTERNAL_QUESTIONFORM_SCHEMA_LOCATION = "http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2006-07-14/ExternalQuestion.xsd"
 
58
    EXTERNAL_QUESTION_XML_TEMPLATE = """<ExternalQuestion xmlns="%s"><ExternalURL>%s</ExternalURL><FrameHeight>%s</FrameHeight></ExternalQuestion>"""
 
59
    
 
60
    def __init__(self, external_url, frame_height):
 
61
        self.external_url = external_url
 
62
        self.frame_height = frame_height
 
63
    
 
64
    def get_as_params(self, label='ExternalQuestion'):
 
65
        return { label : self.get_as_xml() }
 
66
    
 
67
    def get_as_xml(self):
 
68
        ret = ExternalQuestion.EXTERNAL_QUESTION_XML_TEMPLATE % (ExternalQuestion.EXTERNAL_QUESTIONFORM_SCHEMA_LOCATION,
 
69
                                                self.external_url,
 
70
                                                self.frame_height)
 
71
        return ret
 
72
 
 
73
class OrderedContent(object):
 
74
    def __init__(self):
 
75
        self.items = []
 
76
 
 
77
    def append(self, field, value):
 
78
        "Expects field type and value"
 
79
        self.items.append((field, value))
 
80
 
 
81
    def get_binary_xml(self, field, value):
 
82
        return """
 
83
<Binary>
 
84
  <MimeType>
 
85
    <Type>%s</Type>
 
86
    <SubType>%s</SubType>
 
87
  </MimeType>
 
88
  <DataURL>%s</DataURL>
 
89
  <AltText>%s</AltText>
 
90
</Binary>""" % (value['type'],
 
91
                value['subtype'],
 
92
                value['dataurl'],
 
93
                value['alttext'])
 
94
    
 
95
    def get_application_xml(self, field, value):
 
96
        raise NotImplementedError("Application question content is not yet supported.")
 
97
 
 
98
    def get_as_xml(self):
 
99
        default_handler = lambda f,v: '<%s>%s</%s>' % (f,v,f)
 
100
        bulleted_list_handler = lambda _,list: '<List>%s</List>' % ''.join([('<ListItem>%s</ListItem>' % item) for item in list])
 
101
        formatted_content_handler = lambda _,content: "<FormattedContent><![CDATA[%s]]></FormattedContent>" % content
 
102
        application_handler = self.get_application_xml
 
103
        binary_handler = self.get_binary_xml
 
104
        
 
105
        children = ''
 
106
        for (field,value) in self.items:
 
107
            handler = default_handler
 
108
            if field == 'List':
 
109
                handler = bulleted_list_handler
 
110
            elif field == 'Application':
 
111
                handler = application_handler
 
112
            elif field == 'Binary':
 
113
                handler = binary_handler
 
114
            elif field == 'FormattedContent':
 
115
                handler = formatted_content_handler
 
116
            children = children + handler(field, value)
 
117
 
 
118
        return children    
 
119
 
 
120
class Overview(object):
 
121
    OVERVIEW_XML_TEMPLATE = """<Overview>%s</Overview>"""
 
122
    
 
123
    def __init__(self):
 
124
        self.ordered_content = OrderedContent()
 
125
 
 
126
    def append(self, field, value):
 
127
        self.ordered_content.append(field,value)
 
128
    
 
129
    def get_as_params(self, label='Overview'):
 
130
        return { label : self.get_as_xml() }
 
131
    
 
132
    def get_as_xml(self):
 
133
        ret = Overview.OVERVIEW_XML_TEMPLATE % (self.ordered_content.get_as_xml())
 
134
 
 
135
        return ret
 
136
    
 
137
 
 
138
class QuestionForm(object):
 
139
    
 
140
    QUESTIONFORM_SCHEMA_LOCATION = "http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2005-10-01/QuestionForm.xsd"
 
141
    QUESTIONFORM_XML_TEMPLATE = """<QuestionForm xmlns="%s">%s</QuestionForm>"""
 
142
 
 
143
    def __init__(self):
 
144
        self.items = []
 
145
 
 
146
    def append(self, item):
 
147
        "Expects field type and value"
 
148
        self.items.append(item)
 
149
        
 
150
    def get_as_xml(self):
 
151
        xml = ''
 
152
        for item in self.items:
 
153
            xml = xml + item.get_as_xml()
 
154
        return QuestionForm.QUESTIONFORM_XML_TEMPLATE % (QuestionForm.QUESTIONFORM_SCHEMA_LOCATION, xml)
 
155
        
 
156
class QuestionContent(object):
 
157
    QUESTIONCONTENT_XML_TEMPLATE = """<QuestionContent>%s</QuestionContent>"""
 
158
    
 
159
    def __init__(self):
 
160
        self.ordered_content = OrderedContent()
 
161
 
 
162
    def append(self, field, value):
 
163
        self.ordered_content.append(field,value)
 
164
    
 
165
    def get_as_xml(self):
 
166
        ret = QuestionContent.QUESTIONCONTENT_XML_TEMPLATE % (self.ordered_content.get_as_xml())
 
167
 
 
168
        return ret
 
169
 
 
170
 
 
171
class AnswerSpecification(object):
 
172
    
 
173
    ANSWERSPECIFICATION_XML_TEMPLATE = """<AnswerSpecification>%s</AnswerSpecification>"""
 
174
    
 
175
    def __init__(self, spec):
 
176
        self.spec = spec
 
177
    def get_as_xml(self):
 
178
        values = () # TODO
 
179
        return AnswerSpecification.ANSWERSPECIFICATION_XML_TEMPLATE % self.spec.get_as_xml()
 
180
 
 
181
class FreeTextAnswer(object):
 
182
    
 
183
    FREETEXTANSWER_XML_TEMPLATE = """<FreeTextAnswer>%s%s</FreeTextAnswer>""" # (constraints, default)
 
184
    FREETEXTANSWER_CONSTRAINTS_XML_TEMPLATE = """<Constraints>%s%s%s</Constraints>""" # (is_numeric_xml, length_xml, regex_xml)
 
185
    FREETEXTANSWER_LENGTH_XML_TEMPLATE = """<Length %s %s />""" # (min_length_attr, max_length_attr)
 
186
    FREETEXTANSWER_ISNUMERIC_XML_TEMPLATE = """<IsNumeric %s %s />""" # (min_value_attr, max_value_attr)
 
187
    FREETEXTANSWER_DEFAULTTEXT_XML_TEMPLATE = """<DefaultText>%s</DefaultText>""" # (default)
 
188
    
 
189
    def __init__(self, default=None, min_length=None, max_length=None, is_numeric=False, min_value=None, max_value=None, format_regex=None):
 
190
        self.default = default
 
191
        self.min_length = min_length
 
192
        self.max_length = max_length
 
193
        self.is_numeric = is_numeric
 
194
        self.min_value = min_value
 
195
        self.max_value = max_value
 
196
        self.format_regex = format_regex
 
197
    
 
198
    def get_as_xml(self):
 
199
        is_numeric_xml = ""
 
200
        if self.is_numeric:
 
201
            min_value_attr = ""
 
202
            max_value_attr = ""
 
203
            if self.min_value:
 
204
                min_value_attr = """minValue="%d" """ % self.min_value
 
205
            if self.max_value:
 
206
                max_value_attr = """maxValue="%d" """ % self.max_value
 
207
            is_numeric_xml = FreeTextAnswer.FREETEXTANSWER_ISNUMERIC_XML_TEMPLATE % (min_value_attr, max_value_attr)
 
208
        
 
209
        length_xml = ""
 
210
        if self.min_length or self.max_length:
 
211
            min_length_attr = ""
 
212
            max_length_attr = ""
 
213
            if self.min_length:
 
214
                min_length_attr = """minLength="%d" """
 
215
            if self.max_length:
 
216
                max_length_attr = """maxLength="%d" """
 
217
            length_xml = FreeTextAnswer.FREETEXTANSWER_LENGTH_XML_TEMPLATE % (min_length_attr, max_length_attr)
 
218
 
 
219
        regex_xml = ""
 
220
        if self.format_regex:
 
221
            format_regex_attribs = '''regex="%s"''' %self.format_regex['regex']
 
222
 
 
223
            error_text = self.format_regex.get('error_text', None)
 
224
            if error_text:
 
225
                format_regex_attribs += ' errorText="%s"' %error_text
 
226
 
 
227
            flags = self.format_regex.get('flags', None)
 
228
            if flags:
 
229
                format_regex_attribs += ' flags="%s"' %flags
 
230
 
 
231
            regex_xml = """<AnswerFormatRegex %s/>""" %format_regex_attribs
 
232
            
 
233
        constraints_xml = ""
 
234
        if is_numeric_xml or length_xml or regex_xml:
 
235
            constraints_xml = FreeTextAnswer.FREETEXTANSWER_CONSTRAINTS_XML_TEMPLATE % (is_numeric_xml, length_xml, regex_xml)
 
236
        
 
237
        default_xml = ""
 
238
        if self.default is not None:
 
239
            default_xml = FreeTextAnswer.FREETEXTANSWER_DEFAULTTEXT_XML_TEMPLATE % self.default
 
240
            
 
241
        return FreeTextAnswer.FREETEXTANSWER_XML_TEMPLATE % (constraints_xml, default_xml)
 
242
 
 
243
class FileUploadAnswer(object):
 
244
    FILEUPLOADANSWER_XML_TEMLPATE = """<FileUploadAnswer><MinFileSizeInBytes>%d</MinFileSizeInBytes><MaxFileSizeInBytes>%d</MaxFileSizeInBytes></FileUploadAnswer>""" # (min, max)
 
245
    DEFAULT_MIN_SIZE = 1024 # 1K (completely arbitrary!)
 
246
    DEFAULT_MAX_SIZE = 5 * 1024 * 1024 # 5MB (completely arbitrary!)
 
247
    
 
248
    def __init__(self, min=None, max=None):
 
249
        self.min = min
 
250
        self.max = max
 
251
        if self.min is None:
 
252
            self.min = FileUploadAnswer.DEFAULT_MIN_SIZE
 
253
        if self.max is None:
 
254
            self.max = FileUploadAnswer.DEFAULT_MAX_SIZE
 
255
    
 
256
    def get_as_xml(self):
 
257
        return FileUploadAnswer.FILEUPLOADANSWER_XML_TEMLPATE % (self.min, self.max)
 
258
 
 
259
class SelectionAnswer(object):
 
260
    """
 
261
    A class to generate SelectionAnswer XML data structures.
 
262
    Does not yet implement Binary selection options.
 
263
    """
 
264
    SELECTIONANSWER_XML_TEMPLATE = """<SelectionAnswer>%s%s<Selections>%s</Selections></SelectionAnswer>""" # % (count_xml, style_xml, selections_xml)
 
265
    SELECTION_XML_TEMPLATE = """<Selection><SelectionIdentifier>%s</SelectionIdentifier>%s</Selection>""" # (identifier, value_xml)
 
266
    SELECTION_VALUE_XML_TEMPLATE = """<%s>%s</%s>""" # (type, value, type)
 
267
    STYLE_XML_TEMPLATE = """<StyleSuggestion>%s</StyleSuggestion>""" # (style)
 
268
    MIN_SELECTION_COUNT_XML_TEMPLATE = """<MinSelectionCount>%s</MinSelectionCount>""" # count
 
269
    MAX_SELECTION_COUNT_XML_TEMPLATE = """<MaxSelectionCount>%s</MaxSelectionCount>""" # count
 
270
    ACCEPTED_STYLES = ['radiobutton', 'dropdown', 'checkbox', 'list', 'combobox', 'multichooser']
 
271
    OTHER_SELECTION_ELEMENT_NAME = 'OtherSelection'
 
272
    
 
273
    def __init__(self, min=1, max=1, style=None, selections=None, type='text', other=False):
 
274
        
 
275
        if style is not None:
 
276
            if style in SelectionAnswer.ACCEPTED_STYLES:
 
277
                self.style_suggestion = style
 
278
            else:
 
279
                raise ValueError("style '%s' not recognized; should be one of %s" % (style, ', '.join(SelectionAnswer.ACCEPTED_STYLES)))
 
280
        else:
 
281
            self.style_suggestion = None
 
282
        
 
283
        if selections is None:
 
284
            raise ValueError("SelectionAnswer.__init__(): selections must be a non-empty list of (content, identifier) tuples")
 
285
        else:
 
286
            self.selections = selections
 
287
        
 
288
        self.min_selections = min
 
289
        self.max_selections = max
 
290
        
 
291
        assert len(selections) >= self.min_selections, "# of selections is less than minimum of %d" % self.min_selections
 
292
        #assert len(selections) <= self.max_selections, "# of selections exceeds maximum of %d" % self.max_selections
 
293
        
 
294
        self.type = type
 
295
        
 
296
        self.other = other
 
297
    
 
298
    def get_as_xml(self):
 
299
        if self.type == 'text':
 
300
            TYPE_TAG = "Text"
 
301
        elif self.type == 'binary':
 
302
            TYPE_TAG = "Binary"
 
303
        else:
 
304
            raise ValueError("illegal type: %s; must be either 'text' or 'binary'" % str(self.type))
 
305
        
 
306
        # build list of <Selection> elements
 
307
        selections_xml = ""
 
308
        for tpl in self.selections:
 
309
            value_xml = SelectionAnswer.SELECTION_VALUE_XML_TEMPLATE % (TYPE_TAG, tpl[0], TYPE_TAG)
 
310
            selection_xml = SelectionAnswer.SELECTION_XML_TEMPLATE % (tpl[1], value_xml)
 
311
            selections_xml += selection_xml
 
312
        
 
313
        if self.other:
 
314
            # add OtherSelection element as xml if available
 
315
            if hasattr(self.other, 'get_as_xml'):
 
316
                assert type(self.other) == FreeTextAnswer, 'OtherSelection can only be a FreeTextAnswer'
 
317
                selections_xml += self.other.get_as_xml().replace('FreeTextAnswer', 'OtherSelection')
 
318
            else:
 
319
                selections_xml += "<OtherSelection />"
 
320
        
 
321
        if self.style_suggestion is not None:
 
322
            style_xml = SelectionAnswer.STYLE_XML_TEMPLATE % self.style_suggestion
 
323
        else:
 
324
            style_xml = ""
 
325
 
 
326
        if self.style_suggestion != 'radiobutton':
 
327
            count_xml = SelectionAnswer.MIN_SELECTION_COUNT_XML_TEMPLATE %self.min_selections
 
328
            count_xml += SelectionAnswer.MAX_SELECTION_COUNT_XML_TEMPLATE %self.max_selections
 
329
        else:
 
330
            count_xml = ""
 
331
        
 
332
        ret = SelectionAnswer.SELECTIONANSWER_XML_TEMPLATE % (count_xml, style_xml, selections_xml)
 
333
 
 
334
        # return XML
 
335
        return ret
 
336