Exemplo n.º 1
0
def update_kg(self, question_id, user_email=None):
    '''
    Update the shared knowledge graph with respect to a question
    '''

    self.update_state(state='UPDATING KG')

    question = get_question_by_id(question_id)

    logger.info(f"Updating the knowledge graph for '{question.name}'...")

    r = requests.post(
        f'http://{os.environ["BUILDER_HOST"]}:{os.environ["BUILDER_PORT"]}/api/',
        json=question.toJSON())
    polling_url = f"http://{os.environ['BUILDER_HOST']}:{os.environ['BUILDER_PORT']}/api/task/{r.json()['task id']}"

    for _ in range(60 * 60 * 24):  # wait up to 1 day
        r = requests.get(polling_url)
        if r.json()['state'] == 'FAILURE':
            raise RuntimeError('Builder failed.')
        if r.json()['state'] == 'REVOKED':
            raise RuntimeError('Task terminated by admin.')
        if r.json()['state'] == 'SUCCESS':
            break
        time.sleep(1)
    else:
        raise RuntimeError(
            "KG updating has not completed after 1 day. It will continue working, but we must return to the manager."
        )

    try:
        if user_email:
            # send completion email
            question_url = f'http://{os.environ["ROBOKOP_HOST"]}/q/{question.id}'
            lines = [
                f'We have finished gathering information for your question: <a href="{question_url}">"{question.natural_question}"</a>.'
            ]
            html = '<br />\n'.join(lines)
            with app.app_context():
                msg = Message("ROBOKOP: Knowledge Graph Update Complete",
                              sender=os.environ["ROBOKOP_DEFAULT_MAIL_SENDER"],
                              recipients=[user_email],
                              html=html)
                mail.send(msg)
    except Exception as err:
        logger.warning(f"Failed to send 'completed KG update' email: {err}")

    logger.info(f"Done updating for '{question.name}'.")
    return "You updated the KG!"
Exemplo n.º 2
0
def answer_question(self, question_id, user_email=None):
    '''
    Generate answerset for a question
    '''

    self.update_state(state='ANSWERING')
    logger.info("Answering your question...")

    question = get_question_by_id(question_id)

    r = requests.post(
        f'http://{os.environ["RANKER_HOST"]}:{os.environ["RANKER_PORT"]}/api/',
        json=question.toJSON())
    # wait here for response
    if r.status_code == 204:
        # found 0 answers
        raise NoAnswersException(
            "Question answering complete, found 0 answers.")
    self.update_state(state='ANSWERS FOUND')
    logger.info("Answers found.")
    try:
        answerset_json = r.json()
    except json.decoder.JSONDecodeError as err:
        raise ValueError(f"Response is not json: {r.text}")

    answerset = Answerset(answerset_json)
    question.answersets.append(answerset)
    db.session.commit()

    if user_email:
        try:
            with app.app_context():
                question_url = f'http://{os.environ["ROBOKOP_HOST"]}/q/{question.id}'
                answerset_url = f'http://{os.environ["ROBOKOP_HOST"]}/a/{question_id}_{answerset.id}'
                lines = [
                    f'We have finished answering your question: <a href="{question_url}">"{question.natural_question}"</a>.'
                ]
                lines.append(f'<a href="{answerset_url}">ANSWERS</a>')
                html = '<br />\n'.join(lines)
                msg = Message("ROBOKOP: Answers Ready",
                              sender=os.environ["ROBOKOP_DEFAULT_MAIL_SENDER"],
                              recipients=[user_email],
                              html=html)
                mail.send(msg)
        except Exception as err:
            logger.warning(f"Failed to send 'completed answer' email: {err}")

    logger.info("Done answering.")
    return answerset.id
Exemplo n.º 3
0
def update_kg(self, question_id, user_email=None):
    """Update the shared knowledge graph with respect to a question."""
    self.update_state(state='UPDATING KG')
    logger.info(f"Updating the knowledge graph for '{question_id}'")

    save_starting_task_info(task_id=self.request.id)

    try:
        question_json = get_question_by_id(question_id)
        builder_question = {'query_graph': question_json['question_graph']}

        logger.info('Calling Builder')
        try:
            response = requests.post(
                f'http://{os.environ["BUILDER_HOST"]}:{os.environ["BUILDER_PORT"]}/api/',
                json=builder_question)
        except Exception as err:
            logger.warning('Failed to contact the builder')
            logger.exception(err)
            raise err

        remote_task_id = response.json()['task_id']

        logger.info(
            f'The builder has acknowledge with task_id {remote_task_id}')
        save_remote_task_info(self.request.id, remote_task_id)

        logger.info(f"Starting to poll for results.")
        polling_url = f"http://{os.environ['BUILDER_HOST']}:{os.environ['BUILDER_PORT']}/api/task/{remote_task_id}"
        for _ in range(60 * 60 * 24):  # wait up to 1 day
            time.sleep(1)
            response = requests.get(polling_url)
            if response.status_code == 200:
                if response.json()['status'] == 'FAILURE':
                    logger.info(
                        'Builder reported the task as FAILURE. Aborting.')
                    raise RuntimeError('Knowledge graph building failed.')
                if response.json()['status'] == 'REVOKED':
                    logger.info(
                        'Builder reported the task as REVOKED. Aborting.')
                    raise RuntimeError('Task terminated by admin.')
                if response.json()['status'] == 'SUCCESS':
                    break
                if _ % 30 == 0:  # Every 30s update the log?
                    logger.info(
                        f'Builder is reporting it is busy, status = {response.json()["status"]}'
                    )
                else:
                    pass
            else:
                # We didn't get a 200. This is because of server error or sometimes a dropped task
                raise RuntimeError(
                    'Builder did not return a 200 when requesting task status.'
                )
        else:
            raise RuntimeError(
                "KG updating has not completed after 1 day. It will continue working, but we must return to the manager."
            )

        logger.info('Builder reported SUCCESS.')

        try:
            if user_email:
                # send completion email
                question_url = f'http://{os.environ["ROBOKOP_HOST"]}/q/{question_id}'
                nat_quest = question_json[
                    "natural_question"] if 'natural_question' in question_json else "Check it out"
                lines = [
                    f'We have finished gathering information for your question: <a href="{question_url}">"{nat_quest}"</a>.'
                ]
                html = '<br />\n'.join(lines)
                with app.app_context():
                    msg = Message(
                        "ROBOKOP: Knowledge Graph Update Complete",
                        sender=os.environ["ROBOKOP_DEFAULT_MAIL_SENDER"],
                        recipients=[user_email],
                        html=html)
                    mail.send(msg)
        except Exception as err:
            logger.warning(
                f"Failed to send 'completed KG update' email: {err}")

    except Exception as err:
        try:
            logger.info(f"Saving final task info after error")
            save_final_task_info(task_id=self.request.id)
        except:
            pass
        logger.exception(err)
        raise err

    try:
        save_final_task_info(task_id=self.request.id)
    except:
        pass

    logger.info(f"Done updating '{question_id}'.")

    return "You updated the KG!"
Exemplo n.º 4
0
def answer_question(self, question_id, user_email=None):
    """Generate answerset for a question."""
    self.update_state(state='ANSWERING')
    logger.info("Answering question")

    save_starting_task_info(task_id=self.request.id)

    try:
        question = get_question_by_id(question_id)
        qgraph_id = get_qgraph_id_by_question_id(question_id)
        logger.info(f'question_graph: {question}')
        message = {
            'question_graph': question['question_graph'],
        }

        logger.info('Calling Ranker')
        try:
            response = requests.post(
                f'http://{os.environ["RANKER_HOST"]}:{os.environ["RANKER_PORT"]}/api/?output_format=Answers',
                json=message)
        except Exception as err:
            logger.warning('Failed to contact the ranker')
            logger.exception(err)
            raise err

        remote_task_id = response.json()['task_id']

        logger.info(
            f'The ranker has acknowledged with task_id {remote_task_id}')
        save_remote_task_info(self.request.id, remote_task_id)

        logger.info(f"Starting to poll for results.")
        polling_url = f"http://{os.environ['RANKER_HOST']}:{os.environ['RANKER_PORT']}/api/task/{remote_task_id}"
        for _ in range(60 * 60 * 24):  # wait up to 1 day
            time.sleep(1)
            response = requests.get(polling_url)
            if response.status_code == 200:
                # logger.info(f"Poll results: {response}")
                if response.json()['status'] == 'FAILURE':
                    logger.info(
                        'Ranker reported the task as FAILURE. Aborting.')
                    raise RuntimeError('Question answering failed.')
                if response.json()['status'] == 'REVOKED':
                    logger.info(
                        'Ranker reported the task as REVOKED. Aborting.')
                    raise RuntimeError('Task terminated by admin.')
                if response.json()['status'] == 'SUCCESS':
                    break
                if _ % 30 == 0:  # Every 30s update the log?
                    logger.info(
                        f'Ranker is reporting it is busy, status = {response.json()["status"]}'
                    )
                else:
                    pass
            else:
                # We didn't get a 200. This is because of server error or sometimes a dropped task
                raise RuntimeError(
                    'Ranker did not return a 200 when requesting task status.')
        else:
            # We couldn't complete the task in a day!? That's a problem.
            #
            # We should cancel the ranker task, otherwise it will run for a long while and no one will listen to the answer.
            # To delete the ranker task we send a delete request to the polling_url
            response = requests.delete(polling_url)
            # We could check the response here, but there is nothing really that the user can do
            raise RuntimeError(
                "Question answering has not completed after 1 day. The task has been canceled"
            )

        logger.info('Ranking reported as SUCCESS. Requesting answers:')
        response = requests.get(
            f'http://{os.environ["RANKER_HOST"]}:{os.environ["RANKER_PORT"]}/api/task/{remote_task_id}/result'
        )

        message = response.json()
        # logger.info(message)

        if not isinstance(message, dict) or not message["answers"]:
            logger.info(f'No answers found')
            try:
                if user_email:
                    logger.info('Sending email notification')
                    # send completion email
                    question_url = f'http://{os.environ["ROBOKOP_HOST"]}/q/{question["id"]}'
                    nat_quest = question["natural_question"]
                    lines = [
                        f'We are sorry but we did not find any answers for your question: <a href="{question_url}">"{nat_quest}"</a>.'
                    ]
                    html = '<br />\n'.join(lines)
                    with app.app_context():
                        msg = Message(
                            "ROBOKOP: Question Answering Found No Answers",
                            sender=os.environ["ROBOKOP_DEFAULT_MAIL_SENDER"],
                            recipients=[user_email],
                            html=html)
                        mail.send(msg)
            except Exception as err:
                logger.warning(f"Failed to send 'no answers' email: {err}")

            return "NORESULTS"

        logger.info(f'{len(message["answers"])} answers were found')

        logger.info('Storing answers.')
        answerset_id = add_answerset(message['answers'], qgraph_id=qgraph_id)
        logger.info('Answers stored.')
        try:
            if user_email:
                logger.info('Sending email notification')
                # send completion email
                question_url = f'http://{os.environ["ROBOKOP_HOST"]}/a/{question["id"]}_{answerset_id}'
                nat_quest = question["natural_question"]
                lines = [
                    f'We have finished answering your question: <a href="{question_url}">"{nat_quest}"</a>.'
                ]
                html = '<br />\n'.join(lines)
                with app.app_context():
                    msg = Message(
                        "ROBOKOP: Question Answering Complete",
                        sender=os.environ["ROBOKOP_DEFAULT_MAIL_SENDER"],
                        recipients=[user_email],
                        html=html)
                    mail.send(msg)
        except Exception as err:
            logger.warning(
                f"Failed to send 'completed answer update' email: {err}")

    except Exception as err:
        logger.warning(f"Exception found during answering '{question_id}'.")
        try:
            logger.info(f"Saving final task info after error")
            save_final_task_info(task_id=self.request.id)
        except:
            pass
        logger.exception(err)
        raise err

    try:
        save_final_task_info(task_id=self.request.id)
    except:
        pass

    logger.info(f"Done answering '{question_id}'.")

    return answerset_id