<commit_before>import json
import time
import math
import logging
from datetime import datetime
import pdb

import pytest
from tqdm import tqdm

import boto3
from botocore.exceptions import ClientError
from boto.mturk.question  import ExternalQuestion

from service.settings import MTURK_HOST, MTURK_TARGET
from . import db
from . import api

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

MTURK_URL = MTURK_HOST+"/tasks/do"
_MTURK_PARAMS_FILE = 'kbpo/params/mturk_params.json'
with open(_MTURK_PARAMS_FILE) as f:
    _MTURK_PARAMS = json.load(f)

def connect(host_str=MTURK_TARGET, forced=False):
    """
    Connect to mechanical turk to sandbox or actual depending on
    @host_str with prompt for actual unless @forced
    """
    sandbox_endpoint_url = 'https://mturk-requester-sandbox.us-east-1.amazonaws.com'
    if  host_str == 'sandbox':
        mtc = boto3.client('mturk', endpoint_url=sandbox_endpoint_url)
    elif host_str == 'actual':
        proceed = False
        if not forced:
            proceed_input = input("This will connect to actual Mturk \
                    interface and cost money. Continue? (y/n)")
            if proceed_input in ['y', 'Y']:
                proceed = True
        else:
            proceed = True

        if proceed:
            mtc = boto3.client('mturk')
        else:
            logger.error("Aborting")
            exit(1)

    logger.debug("Connected to "+host_str+" mturk endpoint")
    logger.debug(mtc.get_account_balance())
    return mtc

# TODO: This should all be moved into question_batch
def unit_as_token(question):
    """Returns the number of tokens in a @question with parameter doc_id"""
    return sum(map(len, api.get_document(question['doc_id'])['sentences']))

def unit_as_mention_pair(doc_id):
    """Returns the number of mention_pairs in a @question with parameter doc_id"""
    return len(api.get_evaluation_mention_pairs(doc_id))

def compute_units(batch, question):
    """Compute length for a @question using the @batch parameters"""
    if 'units_function' in batch:
        length = locals()[batch['units_function']](question)
    else:
        length = 1
    return length

def compute_estimated_time(batch, question):
    """Compute estimated time for a @question using the @batch parameters"""
    length = compute_units(batch, question)
    if 'unit_time' in batch:
        est_time = int(batch['unit_time']) * length
    else:
        #TODO: Change default
        est_time = 60
    return est_time

def compute_reward(batch, question):
    """Compute reward for a @question using the @batch parameters"""
    length = compute_units(batch, question)
    if 'unit_reward' in batch:
        reward = float("{0:.2f}".format(batch['unit_reward'] * length))
    else:
        reward = batch['doc_reward']
    return reward

def get_hit(conn, hit_id):
    return conn.get_hit(HITId=hit_id)['HIT']

def create_hit(conn,
               frame_height=None,
               title=None,
               description=None,
               max_assignments=None,
               duration=None,
               lifetime=None,
               reward=None,
               **kwargs):
    """
    Create a hit on mturk with @question in a @batch using @mturk_connection
    """
    logger.debug("Creating HIT(%s)", title)
    question = ExternalQuestion(MTURK_URL, frame_height)
    response = conn.create_hit(Question=question.get_as_xml(),
                               Title=title,
                               Description=description,
                               MaxAssignments=int(max_assignments),
                               AssignmentDurationInSeconds=int(duration),
                               LifetimeInSeconds=int(lifetime),
                               Reward=reward)
    logger.debug(["HIT created: ", response['HIT']['HITTypeId'], response['HIT']['HITId']])
    return response['HIT']['HITTypeId'], response['HIT']['HITId']

class HitMustBeReviewed(Exception):
    pass

def revoke_hit(conn, hit_id):
    """
    Revokes a HIT by first expiring it and then trying to delete it.
    This method will raise an RequestException if the HIT needs to be reviewed.
    """
    hit = get_hit(conn, hit_id)

    # Check if already disposed of.
    if hit["HITStatus"] == "Disposed":
        return False

    conn.update_expiration_for_hit(HITId=hit_id, ExpireAt=datetime.now())
    hit = get_hit(conn, hit_id)

    # Verify that the statis is Reviewable
    assert hit["HITStatus"] == "Reviewable" or hit["HITStatus"] == "UnassignableReviewable"
    assignments_inflight = hit['MaxAssignments'] - (hit['NumberOfAssignmentsAvailable'] + hit['NumberOfAssignmentsCompleted'] + hit['NumberOfAssignmentsPending'])
    if assignments_inflight > 0:
        logger.error("HIT must be reviewed")
        raise HitMustBeReviewed(hit_id)

    conn.delete_hit(HITId=hit_id)
    logger.info("Finished revoking mturk_hit %s", hit_id)
    return True

_TEST_PARAMS = {
    "title": "Find relations between people, companies and places",
    "description": "You'll need to pick which relationship is described between a single pair of people, places or organisations in a sentece.",
    "answer_field": "relations",
    "duration": "300",
    "frame_height": "1200",
    "max_assignments": "3",
    "doc_reward": "0.10",<commit_msg>Fixed bug in check for revoke_hit<commit_after>import json
import time
import math
import logging
from datetime import datetime
import pdb

import pytest
from tqdm import tqdm

import boto3
from botocore.exceptions import ClientError
from boto.mturk.question  import ExternalQuestion

from service.settings import MTURK_HOST, MTURK_TARGET
from . import db
from . import api

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

MTURK_URL = MTURK_HOST+"/tasks/do"
_MTURK_PARAMS_FILE = 'kbpo/params/mturk_params.json'
with open(_MTURK_PARAMS_FILE) as f:
    _MTURK_PARAMS = json.load(f)

def connect(host_str=MTURK_TARGET, forced=False):
    """
    Connect to mechanical turk to sandbox or actual depending on
    @host_str with prompt for actual unless @forced
    """
    sandbox_endpoint_url = 'https://mturk-requester-sandbox.us-east-1.amazonaws.com'
    if  host_str == 'sandbox':
        mtc = boto3.client('mturk', endpoint_url=sandbox_endpoint_url)
    elif host_str == 'actual':
        proceed = False
        if not forced:
            proceed_input = input("This will connect to actual Mturk \
                    interface and cost money. Continue? (y/n)")
            if proceed_input in ['y', 'Y']:
                proceed = True
        else:
            proceed = True

        if proceed:
            mtc = boto3.client('mturk')
        else:
            logger.error("Aborting")
            exit(1)

    logger.debug("Connected to "+host_str+" mturk endpoint")
    logger.debug(mtc.get_account_balance())
    return mtc

# TODO: This should all be moved into question_batch
def unit_as_token(question):
    """Returns the number of tokens in a @question with parameter doc_id"""
    return sum(map(len, api.get_document(question['doc_id'])['sentences']))

def unit_as_mention_pair(doc_id):
    """Returns the number of mention_pairs in a @question with parameter doc_id"""
    return len(api.get_evaluation_mention_pairs(doc_id))

def compute_units(batch, question):
    """Compute length for a @question using the @batch parameters"""
    if 'units_function' in batch:
        length = locals()[batch['units_function']](question)
    else:
        length = 1
    return length

def compute_estimated_time(batch, question):
    """Compute estimated time for a @question using the @batch parameters"""
    length = compute_units(batch, question)
    if 'unit_time' in batch:
        est_time = int(batch['unit_time']) * length
    else:
        #TODO: Change default
        est_time = 60
    return est_time

def compute_reward(batch, question):
    """Compute reward for a @question using the @batch parameters"""
    length = compute_units(batch, question)
    if 'unit_reward' in batch:
        reward = float("{0:.2f}".format(batch['unit_reward'] * length))
    else:
        reward = batch['doc_reward']
    return reward

def get_hit(conn, hit_id):
    return conn.get_hit(HITId=hit_id)['HIT']

def create_hit(conn,
               frame_height=None,
               title=None,
               description=None,
               max_assignments=None,
               duration=None,
               lifetime=None,
               reward=None,
               **kwargs):
    """
    Create a hit on mturk with @question in a @batch using @mturk_connection
    """
    logger.debug("Creating HIT(%s)", title)
    question = ExternalQuestion(MTURK_URL, frame_height)
    response = conn.create_hit(Question=question.get_as_xml(),
                               Title=title,
                               Description=description,
                               MaxAssignments=int(max_assignments),
                               AssignmentDurationInSeconds=int(duration),
                               LifetimeInSeconds=int(lifetime),
                               Reward=reward)
    logger.debug(["HIT created: ", response['HIT']['HITTypeId'], response['HIT']['HITId']])
    return response['HIT']['HITTypeId'], response['HIT']['HITId']

class HitMustBeReviewed(Exception):
    pass

def revoke_hit(conn, hit_id):
    """
    Revokes a HIT by first expiring it and then trying to delete it.
    This method will raise an RequestException if the HIT needs to be reviewed.
    """
    hit = get_hit(conn, hit_id)

    # Check if already disposed of.
    if hit["HITStatus"] == "Disposed":
        return False

    conn.update_expiration_for_hit(HITId=hit_id, ExpireAt=datetime.now())
    hit = get_hit(conn, hit_id)

    # Verify that the statis is Reviewable
    assert hit["HITStatus"] == "Reviewable" or hit["HITStatus"] == "Unassignable"
    assignments_inflight = hit['MaxAssignments'] - (hit['NumberOfAssignmentsAvailable'] + hit['NumberOfAssignmentsCompleted'] + hit['NumberOfAssignmentsPending'])
    if assignments_inflight > 0:
        logger.error("HIT must be reviewed")
        raise HitMustBeReviewed(hit_id)

    conn.delete_hit(HITId=hit_id)
    logger.info("Finished revoking mturk_hit %s", hit_id)
    return True

_TEST_PARAMS = {
    "title": "Find relations between people, companies and places",
    "description": "You'll need to pick which relationship is described between a single pair of people, places or organisations in a sentece.",
    "answer_field": "relations",
    "duration": "300",
    "frame_height": "1200",
    "max_assignments": "3",
    "doc_reward": "0.10",
{'fraction_non_alphanumeric': 0.10403755868544601, 'fraction_numerical': 0.0041314553990610325, 'mean_word_length': 2.9772218073188945, 'pattern_counts': {'":': 16, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7010877', 'n_tokens_mistral': 3213, 'n_tokens_neox': 3178, 'n_words': 844}