OLD | NEW |
1 # Copyright (c) 2006,2007 Mitch Garnaat http://garnaat.org/ | 1 # Copyright (c) 2006,2007 Mitch Garnaat http://garnaat.org/ |
2 # | 2 # |
3 # Permission is hereby granted, free of charge, to any person obtaining a | 3 # Permission is hereby granted, free of charge, to any person obtaining a |
4 # copy of this software and associated documentation files (the | 4 # copy of this software and associated documentation files (the |
5 # "Software"), to deal in the Software without restriction, including | 5 # "Software"), to deal in the Software without restriction, including |
6 # without limitation the rights to use, copy, modify, merge, publish, dis- | 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 | 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- | 8 # persons to whom the Software is furnished to do so, subject to the fol- |
9 # lowing conditions: | 9 # lowing conditions: |
10 # | 10 # |
(...skipping 23 matching lines...) Expand all Loading... |
34 | 34 |
35 class MTurkRequestError(EC2ResponseError): | 35 class MTurkRequestError(EC2ResponseError): |
36 "Error for MTurk Requests" | 36 "Error for MTurk Requests" |
37 # todo: subclass from an abstract parent of EC2ResponseError | 37 # todo: subclass from an abstract parent of EC2ResponseError |
38 | 38 |
39 class MTurkConnection(AWSQueryConnection): | 39 class MTurkConnection(AWSQueryConnection): |
40 | 40 |
41 APIVersion = '2008-08-02' | 41 APIVersion = '2008-08-02' |
42 | 42 |
43 def __init__(self, aws_access_key_id=None, aws_secret_access_key=None, | 43 def __init__(self, aws_access_key_id=None, aws_secret_access_key=None, |
44 is_secure=False, port=None, proxy=None, proxy_port=None, | 44 is_secure=True, port=None, proxy=None, proxy_port=None, |
45 proxy_user=None, proxy_pass=None, | 45 proxy_user=None, proxy_pass=None, |
46 host=None, debug=0, | 46 host=None, debug=0, |
47 https_connection_factory=None): | 47 https_connection_factory=None): |
48 if not host: | 48 if not host: |
49 if config.has_option('MTurk', 'sandbox') and config.get('MTurk', 'sa
ndbox') == 'True': | 49 if config.has_option('MTurk', 'sandbox') and config.get('MTurk', 'sa
ndbox') == 'True': |
50 host = 'mechanicalturk.sandbox.amazonaws.com' | 50 host = 'mechanicalturk.sandbox.amazonaws.com' |
51 else: | 51 else: |
52 host = 'mechanicalturk.amazonaws.com' | 52 host = 'mechanicalturk.amazonaws.com' |
53 | 53 |
54 AWSQueryConnection.__init__(self, aws_access_key_id, | 54 AWSQueryConnection.__init__(self, aws_access_key_id, |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
221 self.build_list_params(params, response_groups, 'ResponseGroup') | 221 self.build_list_params(params, response_groups, 'ResponseGroup') |
222 | 222 |
223 # Submit | 223 # Submit |
224 return self._process_request('CreateHIT', params, [('HIT', HIT),]) | 224 return self._process_request('CreateHIT', params, [('HIT', HIT),]) |
225 | 225 |
226 def change_hit_type_of_hit(self, hit_id, hit_type): | 226 def change_hit_type_of_hit(self, hit_id, hit_type): |
227 """ | 227 """ |
228 Change the HIT type of an existing HIT. Note that the reward associated | 228 Change the HIT type of an existing HIT. Note that the reward associated |
229 with the new HIT type must match the reward of the current HIT type in | 229 with the new HIT type must match the reward of the current HIT type in |
230 order for the operation to be valid. | 230 order for the operation to be valid. |
231 \thit_id is a string | 231 |
232 \thit_type is a string | 232 :type hit_id: str |
| 233 :type hit_type: str |
233 """ | 234 """ |
234 params = {'HITId' : hit_id, | 235 params = {'HITId' : hit_id, |
235 'HITTypeId': hit_type} | 236 'HITTypeId': hit_type} |
236 | 237 |
237 return self._process_request('ChangeHITTypeOfHIT', params) | 238 return self._process_request('ChangeHITTypeOfHIT', params) |
238 | 239 |
239 def get_reviewable_hits(self, hit_type=None, status='Reviewable', | 240 def get_reviewable_hits(self, hit_type=None, status='Reviewable', |
240 sort_by='Expiration', sort_direction='Ascending', | 241 sort_by='Expiration', sort_direction='Ascending', |
241 page_size=10, page_number=1): | 242 page_size=10, page_number=1): |
242 """ | 243 """ |
(...skipping 265 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
508 Unblock a worker from working on my tasks. | 509 Unblock a worker from working on my tasks. |
509 """ | 510 """ |
510 params = {'WorkerId': worker_id, 'Reason': reason} | 511 params = {'WorkerId': worker_id, 'Reason': reason} |
511 | 512 |
512 return self._process_request('UnblockWorker', params) | 513 return self._process_request('UnblockWorker', params) |
513 | 514 |
514 def notify_workers(self, worker_ids, subject, message_text): | 515 def notify_workers(self, worker_ids, subject, message_text): |
515 """ | 516 """ |
516 Send a text message to workers. | 517 Send a text message to workers. |
517 """ | 518 """ |
518 params = {'WorkerId' : worker_ids, | 519 params = {'Subject' : subject, |
519 'Subject' : subject, | |
520 'MessageText': message_text} | 520 'MessageText': message_text} |
| 521 self.build_list_params(params, worker_ids, 'WorkerId') |
521 | 522 |
522 return self._process_request('NotifyWorkers', params) | 523 return self._process_request('NotifyWorkers', params) |
523 | 524 |
524 def create_qualification_type(self, | 525 def create_qualification_type(self, |
525 name, | 526 name, |
526 description, | 527 description, |
527 status, | 528 status, |
528 keywords=None, | 529 keywords=None, |
529 retry_delay=None, | 530 retry_delay=None, |
530 test=None, | 531 test=None, |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
572 } | 573 } |
573 if retry_delay is not None: | 574 if retry_delay is not None: |
574 params['RetryDelay'] = retry_delay | 575 params['RetryDelay'] = retry_delay |
575 | 576 |
576 if test is not None: | 577 if test is not None: |
577 assert(isinstance(test, QuestionForm)) | 578 assert(isinstance(test, QuestionForm)) |
578 assert(test_duration is not None) | 579 assert(test_duration is not None) |
579 params['Test'] = test.get_as_xml() | 580 params['Test'] = test.get_as_xml() |
580 | 581 |
581 if test_duration is not None: | 582 if test_duration is not None: |
582 params['TestDuration'] = test_duration | 583 params['TestDurationInSeconds'] = test_duration |
583 | 584 |
584 if answer_key is not None: | 585 if answer_key is not None: |
585 if isinstance(answer_key, basestring): | 586 if isinstance(answer_key, basestring): |
586 params['AnswerKey'] = answer_key # xml | 587 params['AnswerKey'] = answer_key # xml |
587 else: | 588 else: |
588 raise TypeError | 589 raise TypeError |
589 # Eventually someone will write an AnswerKey class. | 590 # Eventually someone will write an AnswerKey class. |
590 | 591 |
591 if auto_granted: | 592 if auto_granted: |
592 assert(test is False) | 593 assert(test is False) |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
664 must_be_owned_by_caller=True): | 665 must_be_owned_by_caller=True): |
665 """TODO: Document.""" | 666 """TODO: Document.""" |
666 params = {'Query' : query, | 667 params = {'Query' : query, |
667 'SortProperty' : sort_by, | 668 'SortProperty' : sort_by, |
668 'SortDirection' : sort_direction, | 669 'SortDirection' : sort_direction, |
669 'PageSize' : page_size, | 670 'PageSize' : page_size, |
670 'PageNumber' : page_number, | 671 'PageNumber' : page_number, |
671 'MustBeRequestable' : must_be_requestable, | 672 'MustBeRequestable' : must_be_requestable, |
672 'MustBeOwnedByCaller' : must_be_owned_by_caller} | 673 'MustBeOwnedByCaller' : must_be_owned_by_caller} |
673 return self._process_request('SearchQualificationTypes', params, | 674 return self._process_request('SearchQualificationTypes', params, |
674 [('QualificationType', QualificationType),]
) | 675 [('QualificationType', QualificationType),]) |
675 | 676 |
676 def get_qualification_requests(self, qualification_type_id, | 677 def get_qualification_requests(self, qualification_type_id, |
677 sort_by='Expiration', | 678 sort_by='Expiration', |
678 sort_direction='Ascending', page_size=10, | 679 sort_direction='Ascending', page_size=10, |
679 page_number=1): | 680 page_number=1): |
680 """TODO: Document.""" | 681 """TODO: Document.""" |
681 params = {'QualificationTypeId' : qualification_type_id, | 682 params = {'QualificationTypeId' : qualification_type_id, |
682 'SortProperty' : sort_by, | 683 'SortProperty' : sort_by, |
683 'SortDirection' : sort_direction, | 684 'SortDirection' : sort_direction, |
684 'PageSize' : page_size, | 685 'PageSize' : page_size, |
685 'PageNumber' : page_number} | 686 'PageNumber' : page_number} |
686 return self._process_request('GetQualificationRequests', params, | 687 return self._process_request('GetQualificationRequests', params, |
687 [('QualificationRequest', QualificationRequ
est),]) | 688 [('QualificationRequest', QualificationRequest),]) |
688 | 689 |
689 def grant_qualification(self, qualification_request_id, integer_value=1): | 690 def grant_qualification(self, qualification_request_id, integer_value=1): |
690 """TODO: Document.""" | 691 """TODO: Document.""" |
691 params = {'QualificationRequestId' : qualification_request_id, | 692 params = {'QualificationRequestId' : qualification_request_id, |
692 'IntegerValue' : integer_value} | 693 'IntegerValue' : integer_value} |
693 return self._process_request('GrantQualification', params) | 694 return self._process_request('GrantQualification', params) |
694 | 695 |
695 def revoke_qualification(self, subject_id, qualification_type_id, | 696 def revoke_qualification(self, subject_id, qualification_type_id, |
696 reason=None): | 697 reason=None): |
697 """TODO: Document.""" | 698 """TODO: Document.""" |
698 params = {'SubjectId' : subject_id, | 699 params = {'SubjectId' : subject_id, |
699 'QualificationTypeId' : qualification_type_id, | 700 'QualificationTypeId' : qualification_type_id, |
700 'Reason' : reason} | 701 'Reason' : reason} |
701 return self._process_request('RevokeQualification', params) | 702 return self._process_request('RevokeQualification', params) |
702 | 703 |
703 def assign_qualification(self, qualification_type_id, worker_id, | 704 def assign_qualification(self, qualification_type_id, worker_id, |
704 value=1, send_notification=True): | 705 value=1, send_notification=True): |
705 params = {'QualificationTypeId' : qualification_type_id, | 706 params = {'QualificationTypeId' : qualification_type_id, |
706 'WorkerId' : worker_id, | 707 'WorkerId' : worker_id, |
707 'IntegerValue' : value, | 708 'IntegerValue' : value, |
708 'SendNotification' : send_notification, } | 709 'SendNotification' : send_notification} |
709 return self._process_request('AssignQualification', params) | 710 return self._process_request('AssignQualification', params) |
710 | 711 |
| 712 def get_qualification_score(self, qualification_type_id, worker_id): |
| 713 """TODO: Document.""" |
| 714 params = {'QualificationTypeId' : qualification_type_id, |
| 715 'SubjectId' : worker_id} |
| 716 return self._process_request('GetQualificationScore', params, |
| 717 [('Qualification', Qualification),]) |
| 718 |
| 719 def update_qualification_score(self, qualification_type_id, worker_id, |
| 720 value): |
| 721 """TODO: Document.""" |
| 722 params = {'QualificationTypeId' : qualification_type_id, |
| 723 'SubjectId' : worker_id, |
| 724 'IntegerValue' : value} |
| 725 return self._process_request('UpdateQualificationScore', params) |
| 726 |
711 def _process_request(self, request_type, params, marker_elems=None): | 727 def _process_request(self, request_type, params, marker_elems=None): |
712 """ | 728 """ |
713 Helper to process the xml response from AWS | 729 Helper to process the xml response from AWS |
714 """ | 730 """ |
715 response = self.make_request(request_type, params, verb='POST') | 731 response = self.make_request(request_type, params, verb='POST') |
716 return self._process_response(response, marker_elems) | 732 return self._process_response(response, marker_elems) |
717 | 733 |
718 def _process_response(self, response, marker_elems=None): | 734 def _process_response(self, response, marker_elems=None): |
719 """ | 735 """ |
720 Helper to process the xml response from AWS | 736 Helper to process the xml response from AWS |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
797 now = datetime.datetime.utcnow() | 813 now = datetime.datetime.utcnow() |
798 expiration = datetime.datetime.strptime(self.Expiration, '%Y-%m-%dT%
H:%M:%SZ') | 814 expiration = datetime.datetime.strptime(self.Expiration, '%Y-%m-%dT%
H:%M:%SZ') |
799 expired = (now >= expiration) | 815 expired = (now >= expiration) |
800 else: | 816 else: |
801 raise ValueError("ERROR: Request for expired property, but no Expira
tion in HIT!") | 817 raise ValueError("ERROR: Request for expired property, but no Expira
tion in HIT!") |
802 return expired | 818 return expired |
803 | 819 |
804 # are we there yet? | 820 # are we there yet? |
805 expired = property(_has_expired) | 821 expired = property(_has_expired) |
806 | 822 |
| 823 class Qualification(BaseAutoResultElement): |
| 824 """ |
| 825 Class to extract an Qualification structure from a response (used in |
| 826 ResultSet) |
| 827 |
| 828 Will have attributes named as per the Developer Guide such as |
| 829 QualificationTypeId, IntegerValue. Does not seem to contain GrantTime. |
| 830 """ |
| 831 |
| 832 pass |
| 833 |
807 class QualificationType(BaseAutoResultElement): | 834 class QualificationType(BaseAutoResultElement): |
808 """ | 835 """ |
809 Class to extract an QualificationType structure from a response (used in | 836 Class to extract an QualificationType structure from a response (used in |
810 ResultSet) | 837 ResultSet) |
811 | 838 |
812 Will have attributes named as per the Developer Guide, | 839 Will have attributes named as per the Developer Guide, |
813 e.g. QualificationTypeId, CreationTime, Name, etc | 840 e.g. QualificationTypeId, CreationTime, Name, etc |
814 """ | 841 """ |
815 | 842 |
816 pass | 843 pass |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
874 """ | 901 """ |
875 | 902 |
876 def __init__(self, connection): | 903 def __init__(self, connection): |
877 BaseAutoResultElement.__init__(self, connection) | 904 BaseAutoResultElement.__init__(self, connection) |
878 self.fields = [] | 905 self.fields = [] |
879 self.qid = None | 906 self.qid = None |
880 | 907 |
881 def endElement(self, name, value, connection): | 908 def endElement(self, name, value, connection): |
882 if name == 'QuestionIdentifier': | 909 if name == 'QuestionIdentifier': |
883 self.qid = value | 910 self.qid = value |
884 elif name in ['FreeText', 'SelectionIdentifier'] and self.qid: | 911 elif name in ['FreeText', 'SelectionIdentifier', 'OtherSelectionText'] a
nd self.qid: |
885 self.fields.append((self.qid,value)) | 912 self.fields.append((self.qid,value)) |
886 elif name == 'Answer': | 913 elif name == 'Answer': |
887 self.qid = None | 914 self.qid = None |
OLD | NEW |