Index: third_party/gsutil/third_party/boto/boto/mturk/question.py |
diff --git a/third_party/gsutil/third_party/boto/boto/mturk/question.py b/third_party/gsutil/third_party/boto/boto/mturk/question.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..293b0782edf72923b2ed8412975b1708f23b06ee |
--- /dev/null |
+++ b/third_party/gsutil/third_party/boto/boto/mturk/question.py |
@@ -0,0 +1,455 @@ |
+# Copyright (c) 2006,2007 Mitch Garnaat http://garnaat.org/ |
+# |
+# Permission is hereby granted, free of charge, to any person obtaining a |
+# copy of this software and associated documentation files (the |
+# "Software"), to deal in the Software without restriction, including |
+# without limitation the rights to use, copy, modify, merge, publish, dis- |
+# tribute, sublicense, and/or sell copies of the Software, and to permit |
+# persons to whom the Software is furnished to do so, subject to the fol- |
+# lowing conditions: |
+# |
+# The above copyright notice and this permission notice shall be included |
+# in all copies or substantial portions of the Software. |
+# |
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- |
+# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT |
+# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
+# IN THE SOFTWARE. |
+ |
+import xml.sax.saxutils |
+ |
+class Question(object): |
+ template = "<Question>%(items)s</Question>" |
+ |
+ def __init__(self, identifier, content, answer_spec, |
+ is_required=False, display_name=None): |
+ # copy all of the parameters into object attributes |
+ self.__dict__.update(vars()) |
+ del self.self |
+ |
+ def get_as_params(self, label='Question'): |
+ return {label: self.get_as_xml()} |
+ |
+ def get_as_xml(self): |
+ items = [ |
+ SimpleField('QuestionIdentifier', self.identifier), |
+ SimpleField('IsRequired', str(self.is_required).lower()), |
+ self.content, |
+ self.answer_spec, |
+ ] |
+ if self.display_name is not None: |
+ items.insert(1, SimpleField('DisplayName', self.display_name)) |
+ items = ''.join(item.get_as_xml() for item in items) |
+ return self.template % vars() |
+ |
+try: |
+ from lxml import etree |
+ |
+ class ValidatingXML(object): |
+ |
+ def validate(self): |
+ import urllib2 |
+ schema_src_file = urllib2.urlopen(self.schema_url) |
+ schema_doc = etree.parse(schema_src_file) |
+ schema = etree.XMLSchema(schema_doc) |
+ doc = etree.fromstring(self.get_as_xml()) |
+ schema.assertValid(doc) |
+except ImportError: |
+ class ValidatingXML(object): |
+ |
+ def validate(self): |
+ pass |
+ |
+ |
+class ExternalQuestion(ValidatingXML): |
+ """ |
+ An object for constructing an External Question. |
+ """ |
+ schema_url = "http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2006-07-14/ExternalQuestion.xsd" |
+ template = '<ExternalQuestion xmlns="%(schema_url)s"><ExternalURL>%%(external_url)s</ExternalURL><FrameHeight>%%(frame_height)s</FrameHeight></ExternalQuestion>' % vars() |
+ |
+ def __init__(self, external_url, frame_height): |
+ self.external_url = xml.sax.saxutils.escape( external_url ) |
+ self.frame_height = frame_height |
+ |
+ def get_as_params(self, label='ExternalQuestion'): |
+ return {label: self.get_as_xml()} |
+ |
+ def get_as_xml(self): |
+ return self.template % vars(self) |
+ |
+ |
+class XMLTemplate(object): |
+ def get_as_xml(self): |
+ return self.template % vars(self) |
+ |
+ |
+class SimpleField(XMLTemplate): |
+ """ |
+ A Simple name/value pair that can be easily rendered as XML. |
+ |
+ >>> SimpleField('Text', 'A text string').get_as_xml() |
+ '<Text>A text string</Text>' |
+ """ |
+ template = '<%(field)s>%(value)s</%(field)s>' |
+ |
+ def __init__(self, field, value): |
+ self.field = field |
+ self.value = value |
+ |
+ |
+class Binary(XMLTemplate): |
+ template = """<Binary><MimeType><Type>%(type)s</Type><SubType>%(subtype)s</SubType></MimeType><DataURL>%(url)s</DataURL><AltText>%(alt_text)s</AltText></Binary>""" |
+ |
+ def __init__(self, type, subtype, url, alt_text): |
+ self.__dict__.update(vars()) |
+ del self.self |
+ |
+ |
+class List(list): |
+ """A bulleted list suitable for OrderedContent or Overview content""" |
+ def get_as_xml(self): |
+ items = ''.join('<ListItem>%s</ListItem>' % item for item in self) |
+ return '<List>%s</List>' % items |
+ |
+ |
+class Application(object): |
+ template = "<Application><%(class_)s>%(content)s</%(class_)s></Application>" |
+ parameter_template = "<Name>%(name)s</Name><Value>%(value)s</Value>" |
+ |
+ def __init__(self, width, height, **parameters): |
+ self.width = width |
+ self.height = height |
+ self.parameters = parameters |
+ |
+ def get_inner_content(self, content): |
+ content.append_field('Width', self.width) |
+ content.append_field('Height', self.height) |
+ for name, value in self.parameters.items(): |
+ value = self.parameter_template % vars() |
+ content.append_field('ApplicationParameter', value) |
+ |
+ def get_as_xml(self): |
+ content = OrderedContent() |
+ self.get_inner_content(content) |
+ content = content.get_as_xml() |
+ class_ = self.__class__.__name__ |
+ return self.template % vars() |
+ |
+ |
+class HTMLQuestion(ValidatingXML): |
+ schema_url = 'http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2011-11-11/HTMLQuestion.xsd' |
+ template = '<HTMLQuestion xmlns=\"%(schema_url)s\"><HTMLContent><![CDATA[<!DOCTYPE html>%%(html_form)s]]></HTMLContent><FrameHeight>%%(frame_height)s</FrameHeight></HTMLQuestion>' % vars() |
+ |
+ def __init__(self, html_form, frame_height): |
+ self.html_form = html_form |
+ self.frame_height = frame_height |
+ |
+ def get_as_params(self, label="HTMLQuestion"): |
+ return {label: self.get_as_xml()} |
+ |
+ def get_as_xml(self): |
+ return self.template % vars(self) |
+ |
+ |
+class JavaApplet(Application): |
+ def __init__(self, path, filename, *args, **kwargs): |
+ self.path = path |
+ self.filename = filename |
+ super(JavaApplet, self).__init__(*args, **kwargs) |
+ |
+ def get_inner_content(self, content): |
+ content = OrderedContent() |
+ content.append_field('AppletPath', self.path) |
+ content.append_field('AppletFilename', self.filename) |
+ super(JavaApplet, self).get_inner_content(content) |
+ |
+ |
+class Flash(Application): |
+ def __init__(self, url, *args, **kwargs): |
+ self.url = url |
+ super(Flash, self).__init__(*args, **kwargs) |
+ |
+ def get_inner_content(self, content): |
+ content = OrderedContent() |
+ content.append_field('FlashMovieURL', self.url) |
+ super(Flash, self).get_inner_content(content) |
+ |
+ |
+class FormattedContent(XMLTemplate): |
+ schema_url = 'http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2006-07-14/FormattedContentXHTMLSubset.xsd' |
+ template = '<FormattedContent><![CDATA[%(content)s]]></FormattedContent>' |
+ |
+ def __init__(self, content): |
+ self.content = content |
+ |
+ |
+class OrderedContent(list): |
+ |
+ def append_field(self, field, value): |
+ self.append(SimpleField(field, value)) |
+ |
+ def get_as_xml(self): |
+ return ''.join(item.get_as_xml() for item in self) |
+ |
+ |
+class Overview(OrderedContent): |
+ template = '<Overview>%(content)s</Overview>' |
+ |
+ def get_as_params(self, label='Overview'): |
+ return {label: self.get_as_xml()} |
+ |
+ def get_as_xml(self): |
+ content = super(Overview, self).get_as_xml() |
+ return self.template % vars() |
+ |
+ |
+class QuestionForm(ValidatingXML, list): |
+ """ |
+ From the AMT API docs: |
+ |
+ The top-most element of the QuestionForm data structure is a |
+ QuestionForm element. This element contains optional Overview |
+ elements and one or more Question elements. There can be any |
+ number of these two element types listed in any order. The |
+ following example structure has an Overview element and a |
+ Question element followed by a second Overview element and |
+ Question element--all within the same QuestionForm. |
+ |
+ :: |
+ |
+ <QuestionForm xmlns="[the QuestionForm schema URL]"> |
+ <Overview> |
+ [...] |
+ </Overview> |
+ <Question> |
+ [...] |
+ </Question> |
+ <Overview> |
+ [...] |
+ </Overview> |
+ <Question> |
+ [...] |
+ </Question> |
+ [...] |
+ </QuestionForm> |
+ |
+ QuestionForm is implemented as a list, so to construct a |
+ QuestionForm, simply append Questions and Overviews (with at least |
+ one Question). |
+ """ |
+ schema_url = "http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2005-10-01/QuestionForm.xsd" |
+ xml_template = """<QuestionForm xmlns="%(schema_url)s">%%(items)s</QuestionForm>""" % vars() |
+ |
+ def is_valid(self): |
+ return ( |
+ any(isinstance(item, Question) for item in self) |
+ and |
+ all(isinstance(item, (Question, Overview)) for item in self) |
+ ) |
+ |
+ def get_as_xml(self): |
+ assert self.is_valid(), "QuestionForm contains invalid elements" |
+ items = ''.join(item.get_as_xml() for item in self) |
+ return self.xml_template % vars() |
+ |
+ |
+class QuestionContent(OrderedContent): |
+ template = '<QuestionContent>%(content)s</QuestionContent>' |
+ |
+ def get_as_xml(self): |
+ content = super(QuestionContent, self).get_as_xml() |
+ return self.template % vars() |
+ |
+ |
+class AnswerSpecification(object): |
+ template = '<AnswerSpecification>%(spec)s</AnswerSpecification>' |
+ |
+ def __init__(self, spec): |
+ self.spec = spec |
+ |
+ def get_as_xml(self): |
+ spec = self.spec.get_as_xml() |
+ return self.template % vars() |
+ |
+ |
+class Constraints(OrderedContent): |
+ template = '<Constraints>%(content)s</Constraints>' |
+ |
+ def get_as_xml(self): |
+ content = super(Constraints, self).get_as_xml() |
+ return self.template % vars() |
+ |
+ |
+class Constraint(object): |
+ def get_attributes(self): |
+ pairs = zip(self.attribute_names, self.attribute_values) |
+ attrs = ' '.join( |
+ '%s="%d"' % (name, value) |
+ for (name, value) in pairs |
+ if value is not None |
+ ) |
+ return attrs |
+ |
+ def get_as_xml(self): |
+ attrs = self.get_attributes() |
+ return self.template % vars() |
+ |
+ |
+class NumericConstraint(Constraint): |
+ attribute_names = 'minValue', 'maxValue' |
+ template = '<IsNumeric %(attrs)s />' |
+ |
+ def __init__(self, min_value=None, max_value=None): |
+ self.attribute_values = min_value, max_value |
+ |
+ |
+class LengthConstraint(Constraint): |
+ attribute_names = 'minLength', 'maxLength' |
+ template = '<Length %(attrs)s />' |
+ |
+ def __init__(self, min_length=None, max_length=None): |
+ self.attribute_values = min_length, max_length |
+ |
+ |
+class RegExConstraint(Constraint): |
+ attribute_names = 'regex', 'errorText', 'flags' |
+ template = '<AnswerFormatRegex %(attrs)s />' |
+ |
+ def __init__(self, pattern, error_text=None, flags=None): |
+ self.attribute_values = pattern, error_text, flags |
+ |
+ def get_attributes(self): |
+ pairs = zip(self.attribute_names, self.attribute_values) |
+ attrs = ' '.join( |
+ '%s="%s"' % (name, value) |
+ for (name, value) in pairs |
+ if value is not None |
+ ) |
+ return attrs |
+ |
+ |
+class NumberOfLinesSuggestion(object): |
+ template = '<NumberOfLinesSuggestion>%(num_lines)s</NumberOfLinesSuggestion>' |
+ |
+ def __init__(self, num_lines=1): |
+ self.num_lines = num_lines |
+ |
+ def get_as_xml(self): |
+ num_lines = self.num_lines |
+ return self.template % vars() |
+ |
+ |
+class FreeTextAnswer(object): |
+ template = '<FreeTextAnswer>%(items)s</FreeTextAnswer>' |
+ |
+ def __init__(self, default=None, constraints=None, num_lines=None): |
+ self.default = default |
+ if constraints is None: |
+ self.constraints = Constraints() |
+ else: |
+ self.constraints = Constraints(constraints) |
+ self.num_lines = num_lines |
+ |
+ def get_as_xml(self): |
+ items = [self.constraints] |
+ if self.default: |
+ items.append(SimpleField('DefaultText', self.default)) |
+ if self.num_lines: |
+ items.append(NumberOfLinesSuggestion(self.num_lines)) |
+ items = ''.join(item.get_as_xml() for item in items) |
+ return self.template % vars() |
+ |
+ |
+class FileUploadAnswer(object): |
+ template = """<FileUploadAnswer><MaxFileSizeInBytes>%(max_bytes)d</MaxFileSizeInBytes><MinFileSizeInBytes>%(min_bytes)d</MinFileSizeInBytes></FileUploadAnswer>""" |
+ |
+ def __init__(self, min_bytes, max_bytes): |
+ assert 0 <= min_bytes <= max_bytes <= 2 * 10 ** 9 |
+ self.min_bytes = min_bytes |
+ self.max_bytes = max_bytes |
+ |
+ def get_as_xml(self): |
+ return self.template % vars(self) |
+ |
+ |
+class SelectionAnswer(object): |
+ """ |
+ A class to generate SelectionAnswer XML data structures. |
+ Does not yet implement Binary selection options. |
+ """ |
+ SELECTIONANSWER_XML_TEMPLATE = """<SelectionAnswer>%s%s<Selections>%s</Selections></SelectionAnswer>""" # % (count_xml, style_xml, selections_xml) |
+ SELECTION_XML_TEMPLATE = """<Selection><SelectionIdentifier>%s</SelectionIdentifier>%s</Selection>""" # (identifier, value_xml) |
+ SELECTION_VALUE_XML_TEMPLATE = """<%s>%s</%s>""" # (type, value, type) |
+ STYLE_XML_TEMPLATE = """<StyleSuggestion>%s</StyleSuggestion>""" # (style) |
+ MIN_SELECTION_COUNT_XML_TEMPLATE = """<MinSelectionCount>%s</MinSelectionCount>""" # count |
+ MAX_SELECTION_COUNT_XML_TEMPLATE = """<MaxSelectionCount>%s</MaxSelectionCount>""" # count |
+ ACCEPTED_STYLES = ['radiobutton', 'dropdown', 'checkbox', 'list', 'combobox', 'multichooser'] |
+ OTHER_SELECTION_ELEMENT_NAME = 'OtherSelection' |
+ |
+ def __init__(self, min=1, max=1, style=None, selections=None, type='text', other=False): |
+ |
+ if style is not None: |
+ if style in SelectionAnswer.ACCEPTED_STYLES: |
+ self.style_suggestion = style |
+ else: |
+ raise ValueError("style '%s' not recognized; should be one of %s" % (style, ', '.join(SelectionAnswer.ACCEPTED_STYLES))) |
+ else: |
+ self.style_suggestion = None |
+ |
+ if selections is None: |
+ raise ValueError("SelectionAnswer.__init__(): selections must be a non-empty list of (content, identifier) tuples") |
+ else: |
+ self.selections = selections |
+ |
+ self.min_selections = min |
+ self.max_selections = max |
+ |
+ assert len(selections) >= self.min_selections, "# of selections is less than minimum of %d" % self.min_selections |
+ #assert len(selections) <= self.max_selections, "# of selections exceeds maximum of %d" % self.max_selections |
+ |
+ self.type = type |
+ |
+ self.other = other |
+ |
+ def get_as_xml(self): |
+ if self.type == 'text': |
+ TYPE_TAG = "Text" |
+ elif self.type == 'binary': |
+ TYPE_TAG = "Binary" |
+ else: |
+ raise ValueError("illegal type: %s; must be either 'text' or 'binary'" % str(self.type)) |
+ |
+ # build list of <Selection> elements |
+ selections_xml = "" |
+ for tpl in self.selections: |
+ value_xml = SelectionAnswer.SELECTION_VALUE_XML_TEMPLATE % (TYPE_TAG, tpl[0], TYPE_TAG) |
+ selection_xml = SelectionAnswer.SELECTION_XML_TEMPLATE % (tpl[1], value_xml) |
+ selections_xml += selection_xml |
+ |
+ if self.other: |
+ # add OtherSelection element as xml if available |
+ if hasattr(self.other, 'get_as_xml'): |
+ assert isinstance(self.other, FreeTextAnswer), 'OtherSelection can only be a FreeTextAnswer' |
+ selections_xml += self.other.get_as_xml().replace('FreeTextAnswer', 'OtherSelection') |
+ else: |
+ selections_xml += "<OtherSelection />" |
+ |
+ if self.style_suggestion is not None: |
+ style_xml = SelectionAnswer.STYLE_XML_TEMPLATE % self.style_suggestion |
+ else: |
+ style_xml = "" |
+ |
+ if self.style_suggestion != 'radiobutton': |
+ count_xml = SelectionAnswer.MIN_SELECTION_COUNT_XML_TEMPLATE %self.min_selections |
+ count_xml += SelectionAnswer.MAX_SELECTION_COUNT_XML_TEMPLATE %self.max_selections |
+ else: |
+ count_xml = "" |
+ |
+ ret = SelectionAnswer.SELECTIONANSWER_XML_TEMPLATE % (count_xml, style_xml, selections_xml) |
+ |
+ # return XML |
+ return ret |