1
# Copyright (c) 2006,2007 Mitch Garnaat http://garnaat.org/
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-
11
# The above copyright notice and this permission notice shall be included
12
# in all copies or substantial portions of the Software.
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
24
from boto import handler
25
from boto.mturk.price import Price
26
from boto.mturk.question import QuestionForm
27
import boto.mturk.notification
28
from boto.connection import AWSQueryConnection
29
from boto.exception import EC2ResponseError
30
from boto.resultset import ResultSet
32
class MTurkConnection(AWSQueryConnection):
34
APIVersion = '2006-10-31'
35
SignatureVersion = '1'
37
def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
38
is_secure=False, port=None, proxy=None, proxy_port=None,
39
host='mechanicalturk.amazonaws.com', debug=0,
40
https_connection_factory=None):
41
AWSQueryConnection.__init__(self, aws_access_key_id,
42
aws_secret_access_key,
43
is_secure, port, proxy, proxy_port,
44
host, debug, https_connection_factory)
46
def get_account_balance(self):
48
response = self.make_request('GetAccountBalance', params)
49
body = response.read()
50
if response.status == 200:
51
rs = ResultSet([('AvailableBalance', Price),
52
('OnHoldBalance', Price)])
53
h = handler.XmlHandler(rs, self)
54
xml.sax.parseString(body, h)
57
raise EC2ResponseError(response.status, response.reason, body)
59
def register_hit_type(self, title, description, reward, duration,
60
keywords=None, approval_delay=None, qual_req=None):
62
Register a new HIT Type
63
\ttitle, description are strings
64
\treward is a Price object
65
\tduration can be an integer or string
67
params = {'Title' : title,
68
'Description' : description,
69
'AssignmentDurationInSeconds' : duration}
70
params.update(MTurkConnection.get_price_as_price(reward).get_as_params('Reward'))
71
response = self.make_request('RegisterHITType', params)
72
body = response.read()
73
if response.status == 200:
75
h = handler.XmlHandler(rs, self)
76
xml.sax.parseString(body, h)
79
raise EC2ResponseError(response.status, response.reason, body)
81
def set_email_notification(hit_type, email):
83
Performs a SetHITTypeNotification operation to set email notification for a specified HIT type
85
assert type(hit_type) is str, "hit_type argument should be a string."
87
params = {'HITTypeId': hit_type}
89
notification_params = {'Destination': email,
91
'Version': boto.mturk.notification.NOTIFICATION_VERSION }
93
# Set up dict of 'Notification.1.Transport' etc. values
94
notification_rest_params = {}
96
for key in notification_params:
97
notification_rest_params['Notification.%d.%s' % (num, key)] = notification_params[key]
99
# Update main params dict
100
params.update(notification_rest_params)
103
response = self.make_request('SetHITTypeNotification', params)
104
body = response.read()
106
# From the Developer Guide:
107
# A successful request for the SetHITTypeNotification operation will return with no errors. The
108
# response will include a SetHITTypeNotificationResult element, which contains the Request (if
109
# the Request response group is specified). The operation returns no other data.
111
if response.status == 200: # Is this the correct way to test if it worked?
113
h = handler.XmlHandler(rs, self)
114
xml.sax.parseString(body, h)
117
raise EC2ResponseError(response.status, response.reason, body)
120
def create_hit(self, title=None, description=None, keywords=None, reward=0.00,
121
duration=60*60*24*7, approval_delay=None, qual_req=None, hit_type=None,
122
question=None, questions=None):
125
Returns HITId as a string.
126
See: http://docs.amazonwebservices.com/AWSMechanicalTurkRequester/2006-10-31/ApiReference_CreateHITOperation.html
129
# handle single or multiple questions
130
if question is not None and questions is not None:
131
raise ValueError("Must specify either question (single Question instance) or questions (list), but not both")
132
if question is not None and questions is None:
133
questions = [question]
137
final_keywords = MTurkConnection.get_keywords_as_string(keywords)
139
# Handle price argument
140
final_price = MTurkConnection.get_price_as_price(reward)
142
# Set up QuestionForm data structure
143
qf = QuestionForm(questions=questions)
145
# Handle basic arguments and set up params dict
146
params = {'Title': title,
147
'Description' : description,
148
'Keywords': final_keywords,
149
'AssignmentDurationInSeconds' : duration,
150
'Question': qf.get_as_xml() }
152
if approval_delay is not None:
153
params.update({'AutoApprovalDelayInSeconds': approval_delay })
155
params.update(final_price.get_as_params('Reward'))
157
# Handle optional hit_type argument
158
if hit_type is not None:
159
params.update({'HITTypeId': hit_type})
162
response = self.make_request('CreateHIT', params)
163
body = response.read()
164
if response.status == 200:
166
h = handler.XmlHandler(rs, self)
167
xml.sax.parseString(body, h)
170
#return rs # return entire ResultSet for testing purposes
172
raise EC2ResponseError(response.status, response.reason, body)
175
def get_keywords_as_string(keywords):
177
Returns a comma+space-separated string of keywords from either a list or a string
179
if type(keywords) is list:
180
final_keywords = ', '.join(keywords)
181
elif type(keywords) is str:
182
final_keywords = keywords
183
elif keywords is None:
186
raise TypeError("keywords argument must be a string or a list of strings; got a %s" % type(keywords))
187
return final_keywords
190
def get_price_as_price(reward):
192
Returns a Price data structure from either a float or a Price
194
if isinstance(reward, Price):
197
final_price = Price(reward)