| 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 |