Module faq_forum.forum

Expand source code
from cluster import connector as cluster
from faq_forum.question_match import match
from faq_forum.auto_moderator import offensiveness, is_nonsense
import logging, sys
import traceback
#logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)


def __get_match(question, question_set):
    """
    Find the best semantic match to question from the given question_set.

    Args:
        question: The question to match
        question_set: The questions to match to (given as a list of dicts {"question_id":XXX,"question":YYY})

    Returns: A tuple (p,best) consisting of the 'best' match from the given question_set and the probability,
    'p', that that 'best' match is semantically equal to the given question. (1 = equal, 0 = unequal,
    0 <= p <= 1). If the question_set was empty or None, (0.0, None) is returned

    """
    prob = 0.0
    best = None

    # Only loop over the questions in question_set if it is not None
    if question_set is not None:
        # Find the best match to the given question
        best = max(question_set, key=lambda x: match(question, x["question"]))
        prob = match(question, best["question"])

    # Right now we only store the best match, but we could respond with the X best
    # matches as well
    return \
        [
            {
                "question_id": best["question_id"],
                "prob": prob
            }
        ]


def __get_offensiveness(sentence):
    """
    Estimate the offensiveness of a sentence.
    Args:
        sentence: The sentence to estimate the offensiveness of

    Returns: the probability,'p', that that the given question is offensive. (1 = yes, 0 = no,
    0 <= p <= 1). 0.0 is returned if the given sentence was None.

    """
    if sentence is None:
        return 0.0
    return offensiveness(sentence)


def __unwrap_match_request(request):
    """
    Unwrap the given "match questions" request into a tuple of Python objects.

    Args:
        request: A JSON object describing a "match questions" request

    Returns: A tuple (question,question_set) where question is a string and question_set is an iterable
    collection of questions and their ids (given as dicts {"question_id":XXX,"question":YYY}).

    """
    # request = json.loads(request)
    question = request["question"]
    question_set = request["compare_questions"]

    return question, question_set


def __wrap_match_request(request, best_matches):
    """
    Wrap the given result of a "match questions" request in a JSON object

    Args:
        request: The request that was processed
        best_matches: A list of best matches and their probabilities, given as a list of
    {"question_id":XXX,"prob":YYY} dicts

    Returns: A JSON-like dict containing all the given information
    (as described on https://clusterdocs.azurewebsites.net/)

    """
    ans = \
        {
            "question_id": request["question_id"],
            "possible_matches": best_matches,
            "msg_id": request["msg_id"],
            "question": request["question"]
        }

    return ans


def __unwrap_offensive_request(request):
    """
    Unwrap the given "estimate offensiveness" request into a string.

    Args:
        request: A JSON-like dict describing an "estimate offensiveness" request
        (as described on https://clusterdocs.azurewebsites.net/)

    Returns: A string that represents the sentence of which to estimate the offensiveness

    """
    # request = json.loads(request)
    sentence = request["sentence"]

    return sentence


def __wrap_offensive_request(request, prob):
    """
    Wrap the given result of an "estimate offensiveness" request in a JSON-like dict

    Args:
        request: The request that was processed
        prob: The probability that the question is offensive, a float

    Returns: A JSON-like dict containing all the given information
    (as described on https://clusterdocs.azurewebsites.net/)

    """
    ans = \
        {
            "sentence_id": request["sentence_id"],
            "prob": prob,
            "msg_id": request["msg_id"],
            "sentence": request["sentence"]
        }

    return ans


def __unwrap_nonsense_request(request):
    """
    Unwrap the given "estimate nonsense" request into a string.

    Args:
        request: A JSON-like dict describing an "estimate nonsense" request
        (as described on https://clusterdocs.azurewebsites.net/)

    Returns: A string that represents the sentence of which to estimate the offensiveness

    """
    # request = json.loads(request)
    sentence = request["sentence"]

    return sentence


def __wrap_nonsense_request(request, is_nonsense):
    """
    Wrap the given result of an "estimate nonsense" request in a JSON-like dict

    Args:
        request: The request that was processed
        is_nonsense:    True if the question is nonsense
                        False if the question is not nonsense

    Returns: A JSON-like dict containing all the given information
    (as described on https://clusterdocs.azurewebsites.net/)

    """
    ans = \
        {
            "sentence_id": request["sentence_id"],
            "nonsense": is_nonsense,
            "msg_id": request["msg_id"],
            "sentence": request["sentence"]
        }

    return ans


def process(request):
    """
    Process the given request and store the reply in a JSON-like dict.
    The given request is not None, it is a JSON-like dict

    Args:
        request: A JSON-like dict describing the request
        (as described on https://clusterdocs.azurewebsites.net/)

    Returns: The reply to the given request

    """
    error = \
        {
            "error": "request was invalid"
        }

    # Process the request
    if request is None:
        ans = error
    else:
        # req_dict = json.loads(request)
        if "action" not in request:
            ans = error
        elif request["action"] == cluster.Actions.MATCH_QUESTIONS.value:
            inp = __unwrap_match_request(request)
            out = __get_match(inp[0], inp[1])
            ans = __wrap_match_request(request,
                                       out)
        elif request["action"] == cluster.Actions.ESTIMATE_OFFENSIVENESS.value:
            inp = __unwrap_offensive_request(request)
            out = __get_offensiveness(inp)
            ans = __wrap_offensive_request(request,
                                           out)
        elif request["action"] == cluster.Actions.IS_NONSENSE.value:
            inp = __unwrap_nonsense_request(request)
            # Should we try/catch here ?
            out = is_nonsense(inp)
            ans = __wrap_nonsense_request(request, out)
        else:
            ans = \
                {
                    "error": "the given request is not supported"
                }

    # return json.dumps(ans)
    return ans


def main():
    """
    Go into a while loop and wait for requests from Cluster.

    Returns: None

    """
    while True:
        try:
            # Connect to the server
            faq = cluster.Connector()
            break
        except Exception:
            # Retry connecting until it succeeds
            pass

    # Go to an infinite loop of processing requests of the FAQ forum
    while True:
        try:
            # Get the request
            print("::: waiting for request")
            request = faq.get_next_task(timeout=None)
            print("::: received request")

            # HOTFIX, needs to be fixed in the connector
            if "question" in request and "question_id" in request:
                request["sentence"] = request["question"]
                request["sentence_id"] = request["question_id"]

            # Process the request
            ans = process(request)

            while True:
                try:
                    # Answer to the request
                    print("::: sending answer")
                    print(request)
                    print(ans)
                    faq.reply(ans)
                    print("::: answer sent")
                    break
                except Exception:
                    traceback.print_exc()
                    print("::: could not send the answer")
                    # Retry sending the reply until it succeeds
                    pass
        except Exception:
            # Retry getting a request until it succeeds
            pass


def _test():
    req_1 = \
        {
            "action": cluster.Actions.MATCH_QUESTIONS.value,
            "question": "Where is the coffee machine?",
            "question_id": 123,
            "compare_questions": [
                {
                    "question_id": 111,
                    "question": "Where can I find the coffee machine?"
                },
                {
                    "question_id": 222,
                    "question": "When did the Titanic sink?"
                },
                {
                    "question_id": 333,
                    "question": "How can I use the coffee machine?"
                }
            ],
            "msg_id": 214
        }
    ans_1 = process(req_1)
    print(ans_1)

    req_2 = \
        {
            "action": cluster.Actions.ESTIMATE_OFFENSIVENESS.value,
            "question_id": 100,
            "question": "Charlie is a little bitch, haha, what a little shit :p",
            "msg_id": 345
        }
    ans_2 = process(req_2)
    print(ans_2)

    req_3 = \
        {
            "action": cluster.Actions.IS_NONSENSE.value,
            "question_id": 200,
            "question": "xmkjnezoinmkqzm. apeozfimkln. azefpqj wdsoimkalez.",
            "msg_id": 654
        }
    ans_3 = process(req_3)
    print(ans_3)


if __name__ == "__main__":
    # Remove the test() method in deployed scripts, it is purely used for debugging
    main()

Functions

def main()

Go into a while loop and wait for requests from Cluster.

Returns: None

Expand source code
def main():
    """
    Go into a while loop and wait for requests from Cluster.

    Returns: None

    """
    while True:
        try:
            # Connect to the server
            faq = cluster.Connector()
            break
        except Exception:
            # Retry connecting until it succeeds
            pass

    # Go to an infinite loop of processing requests of the FAQ forum
    while True:
        try:
            # Get the request
            print("::: waiting for request")
            request = faq.get_next_task(timeout=None)
            print("::: received request")

            # HOTFIX, needs to be fixed in the connector
            if "question" in request and "question_id" in request:
                request["sentence"] = request["question"]
                request["sentence_id"] = request["question_id"]

            # Process the request
            ans = process(request)

            while True:
                try:
                    # Answer to the request
                    print("::: sending answer")
                    print(request)
                    print(ans)
                    faq.reply(ans)
                    print("::: answer sent")
                    break
                except Exception:
                    traceback.print_exc()
                    print("::: could not send the answer")
                    # Retry sending the reply until it succeeds
                    pass
        except Exception:
            # Retry getting a request until it succeeds
            pass
def process(request)

Process the given request and store the reply in a JSON-like dict. The given request is not None, it is a JSON-like dict

Args

request
A JSON-like dict describing the request
(as described on https://clusterdocs.azurewebsites.net/)
Returns : The reply to the given request
 
Expand source code
def process(request):
    """
    Process the given request and store the reply in a JSON-like dict.
    The given request is not None, it is a JSON-like dict

    Args:
        request: A JSON-like dict describing the request
        (as described on https://clusterdocs.azurewebsites.net/)

    Returns: The reply to the given request

    """
    error = \
        {
            "error": "request was invalid"
        }

    # Process the request
    if request is None:
        ans = error
    else:
        # req_dict = json.loads(request)
        if "action" not in request:
            ans = error
        elif request["action"] == cluster.Actions.MATCH_QUESTIONS.value:
            inp = __unwrap_match_request(request)
            out = __get_match(inp[0], inp[1])
            ans = __wrap_match_request(request,
                                       out)
        elif request["action"] == cluster.Actions.ESTIMATE_OFFENSIVENESS.value:
            inp = __unwrap_offensive_request(request)
            out = __get_offensiveness(inp)
            ans = __wrap_offensive_request(request,
                                           out)
        elif request["action"] == cluster.Actions.IS_NONSENSE.value:
            inp = __unwrap_nonsense_request(request)
            # Should we try/catch here ?
            out = is_nonsense(inp)
            ans = __wrap_nonsense_request(request, out)
        else:
            ans = \
                {
                    "error": "the given request is not supported"
                }

    # return json.dumps(ans)
    return ans