-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add CLIs for managing workers. #11
Changes from 6 commits
9b262ee
8d4506c
a361ae0
51d89e2
fceea61
d71c86c
1419ff7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,17 @@ | ||
"""CLIs for managing HITs and their results""" | ||
|
||
from amti.clis import ( | ||
associate, | ||
block, | ||
create, | ||
delete, | ||
disassociate, | ||
expire, | ||
extract, | ||
extraction, | ||
notify, | ||
review, | ||
save, | ||
status) | ||
status, | ||
unblock | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
"""Command line interface for associating quals with Workers""" | ||
|
||
import logging | ||
|
||
import click | ||
import csv | ||
|
||
from amti import actions | ||
from amti import settings | ||
from amti import utils | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
@click.command( | ||
context_settings={ | ||
'help_option_names': ['--help', '-h'] | ||
}) | ||
@click.argument( | ||
'ids', | ||
type=str, | ||
nargs=-1) | ||
@click.option( | ||
'--file', '-f', | ||
type=click.Path(exists=True, file_okay=True, dir_okay=False), | ||
help="Path to file of WorkerIds.") | ||
@click.option( | ||
'--qual', '-q', | ||
help='QualificationId (or name, if --name flag passed).') | ||
@click.option( | ||
'--value', '-v', | ||
help='Integer value for qual.') | ||
@click.option( | ||
'--name', '-n', | ||
is_flag=True, | ||
help='Search for qual by name instead of id.') | ||
@click.option( | ||
'--notify', | ||
is_flag=True, | ||
help='Send notification message about qual.') | ||
@click.option( | ||
'--live', '-l', | ||
is_flag=True, | ||
help='View the status of HITs from the live MTurk site.') | ||
def associate_qual(file, ids, qual, name, value, notify, live): | ||
"""Associate workers with a qualification. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be helpful if you could provide a more complete description of the args as well as what the method returns. |
||
|
||
Given a space seperated list of WorkerIds (IDS) and/or a path to | ||
a CSV of WorkerIds, associate each worker with a qualification (QUAL). | ||
|
||
NOTE: Only works with quals that both exist and are owned by the user. | ||
""" | ||
env = 'live' if live else 'sandbox' | ||
|
||
client = utils.mturk.get_mturk_client(env) | ||
|
||
worker_ids = list(ids) | ||
|
||
# read ids from file (adds to provided ids) | ||
if file is not None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like |
||
worker_ids += utils.workers.read_workerids_from_file(file) | ||
|
||
# set qual_id | ||
qual_id = qual | ||
if name: | ||
qual_id = utils.mturk.get_qual_by_name(client, qual) | ||
if qual_id is None: | ||
raise ValueError(f"No qual with name {qual} found.") | ||
|
||
args = { | ||
"QualificationTypeId": qual_id, | ||
"SendNotification": notify | ||
} | ||
if value is not None: | ||
args['IntegerValue'] = value | ||
|
||
# associate qual with workers | ||
for worker_id in worker_ids: | ||
logger.info(f'Associating qualification {qual_id} with worker {worker_id}.') | ||
response = client.associate_qualification_with_worker( | ||
WorkerId=worker_id, | ||
**args | ||
) | ||
|
||
logger.info('Finished associating quals.') |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
"""Command line interfaces for blocking Workers""" | ||
|
||
import logging | ||
|
||
import click | ||
import csv | ||
|
||
from amti import actions | ||
from amti import settings | ||
from amti import utils | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
@click.command( | ||
context_settings={ | ||
'help_option_names': ['--help', '-h'] | ||
}) | ||
@click.argument( | ||
'ids', | ||
type=str, | ||
nargs=-1) | ||
@click.option( | ||
'--file', '-f', | ||
type=click.Path(exists=True, file_okay=True, dir_okay=False), | ||
help="Path to file of WorkerIds to block.") | ||
@click.option( | ||
'--reason', '-r', | ||
default="Worker has produced low quality work, or is suspected of producing spam.", | ||
help='Reason for blocking worker(s) (workers do not see).') | ||
@click.option( | ||
'--live', '-l', | ||
is_flag=True, | ||
help='View the status of HITs from the live MTurk site.') | ||
def block_workers(file, ids, reason, live): | ||
"""Block workers by WorkerId. | ||
|
||
Given a space seperated list of WorkerIds (IDS) and/or a path to | ||
a CSV of WorkerIds, create a block for each worker in the list. | ||
""" | ||
env = 'live' if live else 'sandbox' | ||
|
||
client = utils.mturk.get_mturk_client(env) | ||
|
||
worker_ids = list(ids) | ||
|
||
# read ids from file (adds to provided ids) | ||
if file is not None: | ||
worker_ids += utils.workers.read_workerids_from_file(file) | ||
|
||
# create blocks | ||
for worker_id in worker_ids: | ||
logger.info(f'Creating block for worker {worker_id}.') | ||
response = client.create_worker_block( | ||
WorkerId=worker_id, | ||
Reason=reason | ||
) | ||
|
||
logger.info('Finished creating blocks.') |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
"""Command line interface for disassociating quals with Workers""" | ||
|
||
import logging | ||
|
||
import click | ||
import csv | ||
|
||
from amti import actions | ||
from amti import settings | ||
from amti import utils | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
@click.command( | ||
context_settings={ | ||
'help_option_names': ['--help', '-h'] | ||
}) | ||
@click.argument( | ||
'ids', | ||
type=str, | ||
nargs=-1) | ||
@click.option( | ||
'--file', '-f', | ||
type=click.Path(exists=True, file_okay=True, dir_okay=False), | ||
help="Path to file of WorkerIds.") | ||
@click.option( | ||
'--qual', '-q', | ||
help='QualificationId (or name, if --name flag passed).') | ||
@click.option( | ||
'--name', '-n', | ||
is_flag=True, | ||
help='Search for qual by name instead of id.') | ||
@click.option( | ||
'--reason', '-r', | ||
help='Reason for disassociation (worker sees this).') | ||
@click.option( | ||
'--live', '-l', | ||
is_flag=True, | ||
help='View the status of HITs from the live MTurk site.') | ||
def disassociate_qual(file, ids, qual, name, reason, live): | ||
"""Disassociate workers with a qualification. | ||
|
||
Given a space seperated list of WorkerIds (IDS) and/or a path to | ||
a CSV of WorkerIds, disassociate each worker with a qualification | ||
(QUAL). | ||
|
||
NOTE: Only works with quals that both exist and are owned by the user. | ||
""" | ||
env = 'live' if live else 'sandbox' | ||
|
||
client = utils.mturk.get_mturk_client(env) | ||
|
||
worker_ids = list(ids) | ||
|
||
# read ids from file (adds to provided ids) | ||
if file is not None: | ||
worker_ids += utils.workers.read_workerids_from_file(file) | ||
|
||
# set qual_id | ||
qual_id = qual | ||
if name: | ||
qual_id = utils.mturk.get_qual_by_name(client, qual) | ||
if qual_id is None: | ||
raise ValueError(f"No qual with name {qual} found.") | ||
|
||
args = {"QualificationTypeId": qual_id} | ||
if reason is not None: | ||
args['Reason'] = reason | ||
|
||
# associate qual with workers | ||
for worker_id in worker_ids: | ||
logger.info(f'Disassociating qualification {qual_id} from worker {worker_id}.') | ||
response = client.disassociate_qualification_from_worker( | ||
WorkerId=worker_id, | ||
**args | ||
) | ||
|
||
logger.info('Finished disassociating quals.') |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
"""Command line interfaces for blocking Workers""" | ||
|
||
import logging | ||
|
||
import click | ||
import csv | ||
|
||
from amti import actions | ||
from amti import settings | ||
from amti import utils | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
@click.command( | ||
context_settings={ | ||
'help_option_names': ['--help', '-h'] | ||
}) | ||
@click.argument( | ||
'ids', | ||
type=str, | ||
nargs=-1) | ||
@click.option( | ||
'--file', '-f', | ||
type=click.Path(exists=True, file_okay=True, dir_okay=False), | ||
help="Path to file of WorkerIds to block.") | ||
@click.option( | ||
'--subject', '-s', | ||
help='Subject line for message.') | ||
@click.option( | ||
'--message', '-m', | ||
help='Text content of message.') | ||
@click.option( | ||
'--message_file', | ||
type=click.Path(exists=True, file_okay=True, dir_okay=False), | ||
help="Path to file of WorkerIds to block.") | ||
@click.option( | ||
'--live', '-l', | ||
is_flag=True, | ||
help='View the status of HITs from the live MTurk site.') | ||
def notify_workers(file, ids, subject, message, message_file, live): | ||
"""Send notification message to workers. | ||
|
||
Given a space seperated list of WorkerIds (IDS), or a path to | ||
a CSV of WorkerIds, send a notification to each worker. | ||
""" | ||
env = 'live' if live else 'sandbox' | ||
|
||
client = utils.mturk.get_mturk_client(env) | ||
|
||
worker_ids = list(ids) | ||
|
||
# read ids from file (adds to provided ids) | ||
if file is not None: | ||
worker_ids += utils.workers.read_workerids_from_file(file) | ||
|
||
# create message (file values overrides Subject, Message args) | ||
message = {'Subject': subject, 'MessageText': message} | ||
if message_file is not None: | ||
with open(args.message, 'r') as f: | ||
message = json.loads(f.read()) | ||
|
||
if any(val is None for val in message.values()): | ||
raise ValueError('Missing Message or Subject value.') | ||
|
||
# break ids into chunks of 100, notify workers in each chunk | ||
for chunk_ids in utils.workers.chunk_list(worker_ids): | ||
|
||
logger.info(f"Sending notification to workers: {chunk_ids}") | ||
|
||
response = client.notify_workers( | ||
Subject=message['Subject'], | ||
MessageText=message['MessageText'], | ||
WorkerIds=chunk_ids | ||
) | ||
|
||
for failure in response['NotifyWorkersFailureStatuses']: | ||
logger.warn(f"Notification failed for {failure.pop('WorkerId')}: {failure}") | ||
|
||
logger.info('Finished sending notifications.') |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
"""Command line interfaces for unblocking Workers""" | ||
|
||
import logging | ||
|
||
import click | ||
import csv | ||
|
||
from amti import actions | ||
from amti import settings | ||
from amti import utils | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
@click.command( | ||
context_settings={ | ||
'help_option_names': ['--help', '-h'] | ||
}) | ||
@click.argument( | ||
'ids', | ||
type=str, | ||
nargs=-1) | ||
@click.option( | ||
'--file', '-f', | ||
type=click.Path(exists=True, file_okay=True, dir_okay=False), | ||
help="Path to file of WorkerIds to block.") | ||
@click.option( | ||
'--reason', '-r', | ||
default="Worker was blocked by mistake.", | ||
help='Reason for blocking worker(s) (workers do not see).') | ||
@click.option( | ||
'--live', '-l', | ||
is_flag=True, | ||
help='View the status of HITs from the live MTurk site.') | ||
def unblock_workers(file, ids, reason, live): | ||
"""Unblock workers by WorkerId. | ||
|
||
Given a space seperated list of WorkerIds (IDS) and/or a path to | ||
a CSV of WorkerIds, remove a block for each worker listed. | ||
""" | ||
env = 'live' if live else 'sandbox' | ||
|
||
client = utils.mturk.get_mturk_client(env) | ||
|
||
worker_ids = list(ids) | ||
|
||
# read ids from file (adds to provided ids) | ||
if file is not None: | ||
worker_ids += utils.workers.read_workerids_from_file(file) | ||
|
||
# remove blocks | ||
for worker_id in worker_ids: | ||
logger.info(f'Removing block for worker {worker_id}.') | ||
response = client.delete_worker_block( | ||
WorkerId=worker_id, | ||
Reason=reason | ||
) | ||
|
||
logger.info('Finished removing blocks.') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation conventions for the code base are implicit at the moment, and should probably be put in a document somewhere, but the gist is that:
The click command doc strings here mostly just need the arguments described in the running text, the parameters sections removed, and the argument names put in all caps.