Exemplo n.º 1
0
    def get(self, question_id):
        """
        Get question subgraph
        ---
        tags: [question]
        parameters:
          - in: path
            name: question_id
            description: "question id"
            type: string
            required: true
        responses:
            200:
                description: xxx
                schema:
                    $ref: '#/definitions/Graph'
            404:
                description: "invalid question key"
                type: string
        """

        try:
            question = get_question_by_id(question_id)
        except Exception as err:
            return "Invalid question key.", 404

        logger.debug(question.toJSON())
        r = requests.post(f"http://{os.environ['RANKER_HOST']}:{os.environ['RANKER_PORT']}/api/subgraph", json=question.toJSON())
        try:
            output = r.json()
        except Exception as err:
            raise ValueError("Response is not JSON.")
        return output, 200
Exemplo n.º 2
0
    def get(self, qa_id):
        """
        Get feedback by answerset
        ---
        tags: [feedback]
        parameters:
          - in: path
            name: qa_id
            description: "<question_id>_<answerset_id>"
            type: string
            required: true
        responses:
            200:
                description: "answer feedback"
                type: array
                items:
                    $ref: '#/definitions/Feedback'
            404:
                description: "invalid answerset/answer id"
                type: string
        """
        try:
            question_id, answerset_id = qa_id.split('_')
            question = get_question_by_id(question_id)
            answerset = get_answerset_by_id(answerset_id)
            feedback = list_feedback_by_question_answerset(question, answerset)
        except Exception as err:
            return "Invalid answerset key", 404

        return [f.toJSON() for f in feedback], 200
Exemplo n.º 3
0
    def get(self, question_id):
        """
        Get question
        ---
        tags: [question]
        parameters:
          - in: path
            name: question_id
            description: "question id"
            type: string
            required: true
            example: A2T8TK8uxaEy
        responses:
            200:
                description: question
                schema:
                    $ref: '#/definitions/Question'
            404:
                description: "invalid question key"
        """

        try:
            question = get_question_by_id(question_id)
        except Exception as err:
            return "Invalid question key.", 404

        answerset_list = question.answersets

        return {'question': question.toJSON(),
                'owner': question.user.email,
                'answerset_list': [a.toStandard(data=False) for a in answerset_list]}, 200
Exemplo n.º 4
0
 def post(self, question_id):
     """
     Refresh KG for question
     ---
     tags: [cache]
     parameters:
       - in: path
         name: question_id
         description: "question id"
         type: string
         required: true
     responses:
         202:
             description: "refreshing in progress"
             type: string
         404:
             description: "invalid question key"
             type: string
     """
     auth = request.authorization
     if auth:
         user_email = auth.username
         user = get_user_by_email(user_email)
         user_id = user.id
     else:
         user_id = current_user.id
         user_email = current_user.email
     try:
         question = get_question_by_id(question_id)
     except Exception as err:
         return "Invalid question key.", 404
     # Update the knowledge graph for a question
     task = update_kg.apply_async(args=[question_id], kwargs={'user_email':user_email})
     return {'task_id':task.id}, 202
Exemplo n.º 5
0
 def delete(self, question_id):
     """
     Delete question
     ---
     tags: [question]
     parameters:
       - in: path
         name: question_id
         description: "question id"
         type: string
         required: true
     responses:
         200:
             description: "question deleted"
         401:
             description: "unauthorized"
         404:
             description: "invalid question key"
     """
     auth = request.authorization
     if auth:
         user_email = auth.username
         user = get_user_by_email(user_email)
     else:
         user = current_user
     logger.info('Deleting question %s', question_id)
     try:
         question = get_question_by_id(question_id)
     except Exception as err:
         return "Invalid question key.", 404
     if not (user == question.user or user.has_role('admin')):
         return "UNAUTHORIZED", 401 # not authorized
     db.session.delete(question)
     db.session.commit()
     return "SUCCESS", 200
Exemplo n.º 6
0
    def get(self, qa_id):
        """
        Get answerset 
        ---
        tags: [answer]
        parameters:
          - in: path
            name: qa_id
            description: "<question_id>_<answerset_id>"
            type: string
            required: true
        responses:
            200:
                description: "answerset data"
                type: object
                properties:
                    answerset:
                        schema:
                            $ref: '#/definitions/Response'
                    user:
                        type: object
                    question:
                        type: object
                    other_questions:
                        type: array
                        items:
                            type: object
                    other_answersets:
                        type: array
                        items:
                            type: object
                    feedback:
                        type: array
                        items:
                            type: object
            404:
                description: "invalid answerset id"
                type: string
        """
        try:
            question_id, answerset_id = qa_id.split('_')
            question = get_question_by_id(question_id)
            answerset = get_answerset_by_id(answerset_id)
            answersets = question.answersets
            if not answerset in answersets:
                raise AssertionError()
        except Exception as err:
            return "Invalid answerset key.", 404

        user = getAuthData()

        feedback = list_feedback_by_question_answerset(question, answerset)

        return {'question': question.toJSON(),\
                'answerset': answerset.toStandard(),\
                'feedback': [f.toJSON() for f in feedback],\
                'other_answersets': [],
                'other_questions': []}, 200
Exemplo n.º 7
0
    def get(self, question_id):
        """
        Get list of queued tasks for question
        ---
        tags: [tasks]
        parameters:
          - in: path
            name: question_id
            description: "question id"
            type: string
            required: true
        responses:
            200:
                description: tasks
                type: object
                properties:
                    answerers:
                        type: array
                        items:
                            $ref: '#/definitions/Task'
                    updaters:
                        type: array
                        items:
                            $ref: '#/definitions/Task'
            404:
                description: "invalid question key"
                type: string
        """

        try:
            question = get_question_by_id(question_id)
        except Exception as err:
            return "Invalid question key.", 404

        tasks = list(get_tasks().values())

        # filter out the SUCCESS/FAILURE tasks
        tasks = [t for t in tasks if not (t['state'] == 'SUCCESS' or t['state'] == 'FAILURE' or t['state'] == 'REVOKED')]

        # filter out tasks for other questions
        question_tasks = []
        for t in tasks:
            if not t['args']:
                continue
            match = re.match(r"[\[(]'(.*)',?[)\]]", t['args'])
            if match:
                if match.group(1) == question.id:
                    question_tasks.append(t)

        # split into answer and update tasks
        answerers = [t for t in question_tasks if t['name'] == 'manager.tasks.answer_question']
        updaters = [t for t in question_tasks if t['name'] == 'manager.tasks.update_kg']

        return {'answerers': answerers,
                'updaters': updaters}, 200
Exemplo n.º 8
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.º 9
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.º 10
0
 def post(self, question_id):
     """
     Edit question metadata
     ---
     tags: [question]
     parameters:
       - in: path
         name: question_id
         description: "question id"
         type: string
         required: true
       - in: body
         name: name
         description: "name of question"
         required: true
       - in: body
         name: natural_question
         description: "natural-language question"
         required: true
       - in: body
         name: notes
         description: "notes"
         required: true
     responses:
         200:
             description: "question edited"
         401:
             description: "unauthorized"
         404:
             description: "invalid question key"
     """
     auth = request.authorization
     if auth:
         user_email = auth.username
         user = get_user_by_email(user_email)
     else:
         user = current_user
     logger.info('Editing question %s', question_id)
     try:
         question = get_question_by_id(question_id)
     except Exception as err:
         return "Invalid question key.", 404
     if not (user == question.user or user.has_role('admin')):
         return "UNAUTHORIZED", 401 # not authorized
     question.name = request.json['name']
     question.notes = request.json['notes']
     question.natural_question = request.json['natural_question']
     db.session.commit()
     return "SUCCESS", 200
Exemplo n.º 11
0
    def get(self, question_id):
        """
        Create new feedback
        ---
        tags: [feedback]
        parameters:
          - in: path
            name: question_id
            description: "question id"
            type: string
            required: true
        responses:
            200:
                description: success
            404:
                description: "invalid question key"
        """
        try:
            question = get_question_by_id(question_id)
            feedback = list_feedback_by_question(question)
        except Exception as err:
            return "Invalid question id", 404

        return feedback.toJSON(), 200
Exemplo n.º 12
0
    def get(self, qa_id, answer_id):
        """
        Get answer
        ---
        tags: [answer]
        parameters:
          - in: path
            name: qa_id
            description: "<question_id>_<answerset_id>"
            type: string
            required: true
          - in: path
            name: answer_id
            description: "answer/result id"
            type: string
            required: true
        responses:
            200:
                description: "answer data"
                type: object
                properties:
                    answer:
                        schema:
                            $ref: '#/definitions/Result'
                    answerset:
                        schema:
                            $ref: '#/definitions/Response'
                    user:
                        type: object
                    question:
                        type: object
                    other_questions:
                        type: array
                        items:
                            type: object
                    other_answersets:
                        type: array
                        items:
                            type: object
                    feedback:
                        type: array
                        items:
                            type: object
            404:
                description: "invalid answerset/answer id"
                type: string
        """

        try:
            question_id, answerset_id = qa_id.split('_')
            question = get_question_by_id(question_id)
            answerset = get_answerset_by_id(answerset_id)
            answersets = question.answersets
            if not answerset in answersets:
                raise AssertionError()
            answer = get_answer_by_id(answer_id)
            if not answer in answerset.answers:
                raise AssertionError()
        except Exception as err:
            return "Invalid answerset or answer key.", 404

        questions = answerset.questions
        idx = questions.index(question)
        questions.pop(idx)
        idx = answersets.index(answerset)
        answersets.pop(idx)

        feedback = list_feedback_by_question_answer(question, answer)

        user = getAuthData()

        return {'user': user,\
                'answerset': answerset.toJSON(),\
                'answer': answer.toJSON(),\
                'feedback': [f.toJSON() for f in feedback],\
                'question': question.toJSON(),\
                'other_answersets': [aset.toJSON() for aset in answersets],
                'other_questions': [q.toJSON() for q in questions]}, 200