Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/boto/mturk/question.py @ 0:26e78fe6e8c4 draft
"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author | shellac |
---|---|
date | Sat, 02 May 2020 07:14:21 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:26e78fe6e8c4 |
---|---|
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 import xml.sax.saxutils | |
23 | |
24 class Question(object): | |
25 template = "<Question>%(items)s</Question>" | |
26 | |
27 def __init__(self, identifier, content, answer_spec, | |
28 is_required=False, display_name=None): | |
29 # copy all of the parameters into object attributes | |
30 self.__dict__.update(vars()) | |
31 del self.self | |
32 | |
33 def get_as_params(self, label='Question'): | |
34 return {label: self.get_as_xml()} | |
35 | |
36 def get_as_xml(self): | |
37 items = [ | |
38 SimpleField('QuestionIdentifier', self.identifier), | |
39 SimpleField('IsRequired', str(self.is_required).lower()), | |
40 self.content, | |
41 self.answer_spec, | |
42 ] | |
43 if self.display_name is not None: | |
44 items.insert(1, SimpleField('DisplayName', self.display_name)) | |
45 items = ''.join(item.get_as_xml() for item in items) | |
46 return self.template % vars() | |
47 | |
48 try: | |
49 from lxml import etree | |
50 | |
51 class ValidatingXML(object): | |
52 | |
53 def validate(self): | |
54 import urllib2 | |
55 schema_src_file = urllib2.urlopen(self.schema_url) | |
56 schema_doc = etree.parse(schema_src_file) | |
57 schema = etree.XMLSchema(schema_doc) | |
58 doc = etree.fromstring(self.get_as_xml()) | |
59 schema.assertValid(doc) | |
60 except ImportError: | |
61 class ValidatingXML(object): | |
62 | |
63 def validate(self): | |
64 pass | |
65 | |
66 | |
67 class ExternalQuestion(ValidatingXML): | |
68 """ | |
69 An object for constructing an External Question. | |
70 """ | |
71 schema_url = "http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2006-07-14/ExternalQuestion.xsd" | |
72 template = '<ExternalQuestion xmlns="%(schema_url)s"><ExternalURL>%%(external_url)s</ExternalURL><FrameHeight>%%(frame_height)s</FrameHeight></ExternalQuestion>' % vars() | |
73 | |
74 def __init__(self, external_url, frame_height): | |
75 self.external_url = xml.sax.saxutils.escape( external_url ) | |
76 self.frame_height = frame_height | |
77 | |
78 def get_as_params(self, label='ExternalQuestion'): | |
79 return {label: self.get_as_xml()} | |
80 | |
81 def get_as_xml(self): | |
82 return self.template % vars(self) | |
83 | |
84 | |
85 class XMLTemplate(object): | |
86 def get_as_xml(self): | |
87 return self.template % vars(self) | |
88 | |
89 | |
90 class SimpleField(XMLTemplate): | |
91 """ | |
92 A Simple name/value pair that can be easily rendered as XML. | |
93 | |
94 >>> SimpleField('Text', 'A text string').get_as_xml() | |
95 '<Text>A text string</Text>' | |
96 """ | |
97 template = '<%(field)s>%(value)s</%(field)s>' | |
98 | |
99 def __init__(self, field, value): | |
100 self.field = field | |
101 self.value = value | |
102 | |
103 | |
104 class Binary(XMLTemplate): | |
105 template = """<Binary><MimeType><Type>%(type)s</Type><SubType>%(subtype)s</SubType></MimeType><DataURL>%(url)s</DataURL><AltText>%(alt_text)s</AltText></Binary>""" | |
106 | |
107 def __init__(self, type, subtype, url, alt_text): | |
108 self.__dict__.update(vars()) | |
109 del self.self | |
110 | |
111 | |
112 class List(list): | |
113 """A bulleted list suitable for OrderedContent or Overview content""" | |
114 def get_as_xml(self): | |
115 items = ''.join('<ListItem>%s</ListItem>' % item for item in self) | |
116 return '<List>%s</List>' % items | |
117 | |
118 | |
119 class Application(object): | |
120 template = "<Application><%(class_)s>%(content)s</%(class_)s></Application>" | |
121 parameter_template = "<Name>%(name)s</Name><Value>%(value)s</Value>" | |
122 | |
123 def __init__(self, width, height, **parameters): | |
124 self.width = width | |
125 self.height = height | |
126 self.parameters = parameters | |
127 | |
128 def get_inner_content(self, content): | |
129 content.append_field('Width', self.width) | |
130 content.append_field('Height', self.height) | |
131 for name, value in self.parameters.items(): | |
132 value = self.parameter_template % vars() | |
133 content.append_field('ApplicationParameter', value) | |
134 | |
135 def get_as_xml(self): | |
136 content = OrderedContent() | |
137 self.get_inner_content(content) | |
138 content = content.get_as_xml() | |
139 class_ = self.__class__.__name__ | |
140 return self.template % vars() | |
141 | |
142 | |
143 class HTMLQuestion(ValidatingXML): | |
144 schema_url = 'http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2011-11-11/HTMLQuestion.xsd' | |
145 template = '<HTMLQuestion xmlns=\"%(schema_url)s\"><HTMLContent><![CDATA[<!DOCTYPE html>%%(html_form)s]]></HTMLContent><FrameHeight>%%(frame_height)s</FrameHeight></HTMLQuestion>' % vars() | |
146 | |
147 def __init__(self, html_form, frame_height): | |
148 self.html_form = html_form | |
149 self.frame_height = frame_height | |
150 | |
151 def get_as_params(self, label="HTMLQuestion"): | |
152 return {label: self.get_as_xml()} | |
153 | |
154 def get_as_xml(self): | |
155 return self.template % vars(self) | |
156 | |
157 | |
158 class JavaApplet(Application): | |
159 def __init__(self, path, filename, *args, **kwargs): | |
160 self.path = path | |
161 self.filename = filename | |
162 super(JavaApplet, self).__init__(*args, **kwargs) | |
163 | |
164 def get_inner_content(self, content): | |
165 content = OrderedContent() | |
166 content.append_field('AppletPath', self.path) | |
167 content.append_field('AppletFilename', self.filename) | |
168 super(JavaApplet, self).get_inner_content(content) | |
169 | |
170 | |
171 class Flash(Application): | |
172 def __init__(self, url, *args, **kwargs): | |
173 self.url = url | |
174 super(Flash, self).__init__(*args, **kwargs) | |
175 | |
176 def get_inner_content(self, content): | |
177 content = OrderedContent() | |
178 content.append_field('FlashMovieURL', self.url) | |
179 super(Flash, self).get_inner_content(content) | |
180 | |
181 | |
182 class FormattedContent(XMLTemplate): | |
183 schema_url = 'http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2006-07-14/FormattedContentXHTMLSubset.xsd' | |
184 template = '<FormattedContent><![CDATA[%(content)s]]></FormattedContent>' | |
185 | |
186 def __init__(self, content): | |
187 self.content = content | |
188 | |
189 | |
190 class OrderedContent(list): | |
191 | |
192 def append_field(self, field, value): | |
193 self.append(SimpleField(field, value)) | |
194 | |
195 def get_as_xml(self): | |
196 return ''.join(item.get_as_xml() for item in self) | |
197 | |
198 | |
199 class Overview(OrderedContent): | |
200 template = '<Overview>%(content)s</Overview>' | |
201 | |
202 def get_as_params(self, label='Overview'): | |
203 return {label: self.get_as_xml()} | |
204 | |
205 def get_as_xml(self): | |
206 content = super(Overview, self).get_as_xml() | |
207 return self.template % vars() | |
208 | |
209 | |
210 class QuestionForm(ValidatingXML, list): | |
211 """ | |
212 From the AMT API docs: | |
213 | |
214 The top-most element of the QuestionForm data structure is a | |
215 QuestionForm element. This element contains optional Overview | |
216 elements and one or more Question elements. There can be any | |
217 number of these two element types listed in any order. The | |
218 following example structure has an Overview element and a | |
219 Question element followed by a second Overview element and | |
220 Question element--all within the same QuestionForm. | |
221 | |
222 :: | |
223 | |
224 <QuestionForm xmlns="[the QuestionForm schema URL]"> | |
225 <Overview> | |
226 [...] | |
227 </Overview> | |
228 <Question> | |
229 [...] | |
230 </Question> | |
231 <Overview> | |
232 [...] | |
233 </Overview> | |
234 <Question> | |
235 [...] | |
236 </Question> | |
237 [...] | |
238 </QuestionForm> | |
239 | |
240 QuestionForm is implemented as a list, so to construct a | |
241 QuestionForm, simply append Questions and Overviews (with at least | |
242 one Question). | |
243 """ | |
244 schema_url = "http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2005-10-01/QuestionForm.xsd" | |
245 xml_template = """<QuestionForm xmlns="%(schema_url)s">%%(items)s</QuestionForm>""" % vars() | |
246 | |
247 def is_valid(self): | |
248 return ( | |
249 any(isinstance(item, Question) for item in self) | |
250 and | |
251 all(isinstance(item, (Question, Overview)) for item in self) | |
252 ) | |
253 | |
254 def get_as_xml(self): | |
255 assert self.is_valid(), "QuestionForm contains invalid elements" | |
256 items = ''.join(item.get_as_xml() for item in self) | |
257 return self.xml_template % vars() | |
258 | |
259 | |
260 class QuestionContent(OrderedContent): | |
261 template = '<QuestionContent>%(content)s</QuestionContent>' | |
262 | |
263 def get_as_xml(self): | |
264 content = super(QuestionContent, self).get_as_xml() | |
265 return self.template % vars() | |
266 | |
267 | |
268 class AnswerSpecification(object): | |
269 template = '<AnswerSpecification>%(spec)s</AnswerSpecification>' | |
270 | |
271 def __init__(self, spec): | |
272 self.spec = spec | |
273 | |
274 def get_as_xml(self): | |
275 spec = self.spec.get_as_xml() | |
276 return self.template % vars() | |
277 | |
278 | |
279 class Constraints(OrderedContent): | |
280 template = '<Constraints>%(content)s</Constraints>' | |
281 | |
282 def get_as_xml(self): | |
283 content = super(Constraints, self).get_as_xml() | |
284 return self.template % vars() | |
285 | |
286 | |
287 class Constraint(object): | |
288 def get_attributes(self): | |
289 pairs = zip(self.attribute_names, self.attribute_values) | |
290 attrs = ' '.join( | |
291 '%s="%d"' % (name, value) | |
292 for (name, value) in pairs | |
293 if value is not None | |
294 ) | |
295 return attrs | |
296 | |
297 def get_as_xml(self): | |
298 attrs = self.get_attributes() | |
299 return self.template % vars() | |
300 | |
301 | |
302 class NumericConstraint(Constraint): | |
303 attribute_names = 'minValue', 'maxValue' | |
304 template = '<IsNumeric %(attrs)s />' | |
305 | |
306 def __init__(self, min_value=None, max_value=None): | |
307 self.attribute_values = min_value, max_value | |
308 | |
309 | |
310 class LengthConstraint(Constraint): | |
311 attribute_names = 'minLength', 'maxLength' | |
312 template = '<Length %(attrs)s />' | |
313 | |
314 def __init__(self, min_length=None, max_length=None): | |
315 self.attribute_values = min_length, max_length | |
316 | |
317 | |
318 class RegExConstraint(Constraint): | |
319 attribute_names = 'regex', 'errorText', 'flags' | |
320 template = '<AnswerFormatRegex %(attrs)s />' | |
321 | |
322 def __init__(self, pattern, error_text=None, flags=None): | |
323 self.attribute_values = pattern, error_text, flags | |
324 | |
325 def get_attributes(self): | |
326 pairs = zip(self.attribute_names, self.attribute_values) | |
327 attrs = ' '.join( | |
328 '%s="%s"' % (name, value) | |
329 for (name, value) in pairs | |
330 if value is not None | |
331 ) | |
332 return attrs | |
333 | |
334 | |
335 class NumberOfLinesSuggestion(object): | |
336 template = '<NumberOfLinesSuggestion>%(num_lines)s</NumberOfLinesSuggestion>' | |
337 | |
338 def __init__(self, num_lines=1): | |
339 self.num_lines = num_lines | |
340 | |
341 def get_as_xml(self): | |
342 num_lines = self.num_lines | |
343 return self.template % vars() | |
344 | |
345 | |
346 class FreeTextAnswer(object): | |
347 template = '<FreeTextAnswer>%(items)s</FreeTextAnswer>' | |
348 | |
349 def __init__(self, default=None, constraints=None, num_lines=None): | |
350 self.default = default | |
351 if constraints is None: | |
352 self.constraints = Constraints() | |
353 else: | |
354 self.constraints = Constraints(constraints) | |
355 self.num_lines = num_lines | |
356 | |
357 def get_as_xml(self): | |
358 items = [self.constraints] | |
359 if self.default: | |
360 items.append(SimpleField('DefaultText', self.default)) | |
361 if self.num_lines: | |
362 items.append(NumberOfLinesSuggestion(self.num_lines)) | |
363 items = ''.join(item.get_as_xml() for item in items) | |
364 return self.template % vars() | |
365 | |
366 | |
367 class FileUploadAnswer(object): | |
368 template = """<FileUploadAnswer><MaxFileSizeInBytes>%(max_bytes)d</MaxFileSizeInBytes><MinFileSizeInBytes>%(min_bytes)d</MinFileSizeInBytes></FileUploadAnswer>""" | |
369 | |
370 def __init__(self, min_bytes, max_bytes): | |
371 assert 0 <= min_bytes <= max_bytes <= 2 * 10 ** 9 | |
372 self.min_bytes = min_bytes | |
373 self.max_bytes = max_bytes | |
374 | |
375 def get_as_xml(self): | |
376 return self.template % vars(self) | |
377 | |
378 | |
379 class SelectionAnswer(object): | |
380 """ | |
381 A class to generate SelectionAnswer XML data structures. | |
382 Does not yet implement Binary selection options. | |
383 """ | |
384 SELECTIONANSWER_XML_TEMPLATE = """<SelectionAnswer>%s%s<Selections>%s</Selections></SelectionAnswer>""" # % (count_xml, style_xml, selections_xml) | |
385 SELECTION_XML_TEMPLATE = """<Selection><SelectionIdentifier>%s</SelectionIdentifier>%s</Selection>""" # (identifier, value_xml) | |
386 SELECTION_VALUE_XML_TEMPLATE = """<%s>%s</%s>""" # (type, value, type) | |
387 STYLE_XML_TEMPLATE = """<StyleSuggestion>%s</StyleSuggestion>""" # (style) | |
388 MIN_SELECTION_COUNT_XML_TEMPLATE = """<MinSelectionCount>%s</MinSelectionCount>""" # count | |
389 MAX_SELECTION_COUNT_XML_TEMPLATE = """<MaxSelectionCount>%s</MaxSelectionCount>""" # count | |
390 ACCEPTED_STYLES = ['radiobutton', 'dropdown', 'checkbox', 'list', 'combobox', 'multichooser'] | |
391 OTHER_SELECTION_ELEMENT_NAME = 'OtherSelection' | |
392 | |
393 def __init__(self, min=1, max=1, style=None, selections=None, type='text', other=False): | |
394 | |
395 if style is not None: | |
396 if style in SelectionAnswer.ACCEPTED_STYLES: | |
397 self.style_suggestion = style | |
398 else: | |
399 raise ValueError("style '%s' not recognized; should be one of %s" % (style, ', '.join(SelectionAnswer.ACCEPTED_STYLES))) | |
400 else: | |
401 self.style_suggestion = None | |
402 | |
403 if selections is None: | |
404 raise ValueError("SelectionAnswer.__init__(): selections must be a non-empty list of (content, identifier) tuples") | |
405 else: | |
406 self.selections = selections | |
407 | |
408 self.min_selections = min | |
409 self.max_selections = max | |
410 | |
411 assert len(selections) >= self.min_selections, "# of selections is less than minimum of %d" % self.min_selections | |
412 #assert len(selections) <= self.max_selections, "# of selections exceeds maximum of %d" % self.max_selections | |
413 | |
414 self.type = type | |
415 | |
416 self.other = other | |
417 | |
418 def get_as_xml(self): | |
419 if self.type == 'text': | |
420 TYPE_TAG = "Text" | |
421 elif self.type == 'binary': | |
422 TYPE_TAG = "Binary" | |
423 else: | |
424 raise ValueError("illegal type: %s; must be either 'text' or 'binary'" % str(self.type)) | |
425 | |
426 # build list of <Selection> elements | |
427 selections_xml = "" | |
428 for tpl in self.selections: | |
429 value_xml = SelectionAnswer.SELECTION_VALUE_XML_TEMPLATE % (TYPE_TAG, tpl[0], TYPE_TAG) | |
430 selection_xml = SelectionAnswer.SELECTION_XML_TEMPLATE % (tpl[1], value_xml) | |
431 selections_xml += selection_xml | |
432 | |
433 if self.other: | |
434 # add OtherSelection element as xml if available | |
435 if hasattr(self.other, 'get_as_xml'): | |
436 assert isinstance(self.other, FreeTextAnswer), 'OtherSelection can only be a FreeTextAnswer' | |
437 selections_xml += self.other.get_as_xml().replace('FreeTextAnswer', 'OtherSelection') | |
438 else: | |
439 selections_xml += "<OtherSelection />" | |
440 | |
441 if self.style_suggestion is not None: | |
442 style_xml = SelectionAnswer.STYLE_XML_TEMPLATE % self.style_suggestion | |
443 else: | |
444 style_xml = "" | |
445 | |
446 if self.style_suggestion != 'radiobutton': | |
447 count_xml = SelectionAnswer.MIN_SELECTION_COUNT_XML_TEMPLATE %self.min_selections | |
448 count_xml += SelectionAnswer.MAX_SELECTION_COUNT_XML_TEMPLATE %self.max_selections | |
449 else: | |
450 count_xml = "" | |
451 | |
452 ret = SelectionAnswer.SELECTIONANSWER_XML_TEMPLATE % (count_xml, style_xml, selections_xml) | |
453 | |
454 # return XML | |
455 return ret |