Exemple #1
0
def answer_for_postcode(loc: Tuple):
    # Send API request
    res = query_geocode_api_coords(loc[0], loc[1])

    # Verify that we have at least one valid result
    if (not res or "results" not in res or not len(res["results"])
            or not res["results"][0]):
        return None

    # Grab top result from API call
    top = res["results"][0]
    # TODO: Fall back on lower-ranked results from the API
    # if the top result doesn't even contain a locality.

    # Extract address info from top result
    (street, num, locality, postcode,
     country_code) = _addrinfo_from_api_result(top)

    # Only support Icelandic postcodes for now
    if country_code == "IS" and postcode:
        pc = postcodes.get(int(postcode))
        pd = "{0} {1}".format(postcode, pc["stadur_nf"])
        (response, answer, voice) = gen_answer(pd)
        voice = "Þú ert í {0}".format(pd)
        return response, answer, voice
    else:
        return gen_answer("Ég veit ekki í hvaða póstnúmeri þú ert.")
Exemple #2
0
def get_currweather_answer(query, result):
    """ Handle queries concerning current weather conditions """
    res = _curr_observations(query, result)
    if not res:
        return gen_answer(_API_ERRMSG)

    try:
        temp = int(round(float(res["T"])))  # Round to nearest whole number
        desc = res["W"].lower()
        windsp = float(res["F"])
    except:
        logging.warning("Exception parsing weather API result: {0}".format(e))
        return gen_answer(_API_ERRMSG)

    wind_desc = _wind_descr(windsp)
    temp_type = "hiti" if temp >= 0 else "frost"
    mdesc = ", " + desc + "," if desc else ""

    locdesc = result.get("subject") or "Úti"

    voice = "{0} er {1} stiga {2}{3} og {4}".format(locdesc.capitalize(),
                                                    abs(temp), temp_type,
                                                    mdesc, wind_desc)

    answer = "{0}°{1} og {2} ({3} m/s)".format(
        temp, mdesc, wind_desc,
        str(windsp).rstrip("0").rstrip("."))

    response = dict(answer=answer)

    return response, answer, voice
Exemple #3
0
def _answ_for_petrol_query(q: Query, result) -> AnswerTuple:
    req_distance = True
    location = q.location
    if location is None:
        return gen_answer("Ég veit ekki hvar þú ert")
    if result.qkey == "ClosestStation":
        station = _closest_petrol_station(location)
        answer = "{0} {1} ({2}, bensínverð {3})"
        desc = "Næsta bensínstöð"
    elif result.qkey == "CheapestStation":
        station = _cheapest_petrol_station()
        answer = "{0} {1} ({2}, bensínverð {3})"
        desc = "Ódýrasta bensínstöðin"
        req_distance = False
    elif result.qkey == "ClosestCheapestStation":
        station = _closest_cheapest_petrol_station(location)
        desc = "Ódýrasta bensínstöðin í grenndinni"
    else:
        raise ValueError("Unknown petrol query type")

    if (not station or "bensin95" not in station or "diesel" not in station
            or (req_distance and "distance" not in station)):
        return gen_answer(_ERRMSG)

    bensin_kr_desc = krona_desc(float(station["bensin95"]))
    diesel_kr_desc = krona_desc(float(station["diesel"]))

    if req_distance:
        answ_fmt = "{0} {1} ({2}, bensínverð {3}, díselverð {4})"
        voice_fmt = ("{0} er {1} {2} í um það bil {3} fjarlægð. "
                     "Þar kostar bensínlítrinn {4} og dísel-lítrinn {5}.")
        dist_nf = distance_desc(station["distance"], case="nf")
        dist_þf = distance_desc(station["distance"], case="þf")
        answer = answ_fmt.format(station["company"], station["name"], dist_nf,
                                 bensin_kr_desc, diesel_kr_desc)
        voice = voice_fmt.format(
            desc,
            station["company"],
            station["name"],
            dist_þf,
            bensin_kr_desc,
            diesel_kr_desc,
        )
    else:
        answ_fmt = "{0} {1} (bensínverð {2}, díselverð {3})"
        voice_fmt = "{0} er {1} {2}. Þar kostar bensínlítrinn {3} og dísel-lítrinn {4}."
        answer = answ_fmt.format(station["company"], station["name"],
                                 bensin_kr_desc, diesel_kr_desc)
        voice = voice_fmt.format(desc, station["company"], station["name"],
                                 bensin_kr_desc, diesel_kr_desc)

    response = dict(answer=answer)

    return response, answer, voice
Exemple #4
0
def howlong_answ(q, result):
    """ Generate answer to a query about number of days since/until a given date. """
    now = datetime.utcnow()
    target = result["target"]

    q.set_key("HowLongUntilDate" if "until" in result else "HowLongSinceDate")

    # Check if it's today
    if target.date() == now.date():
        return q.set_answer(
            *gen_answer("Það er {0} í dag.".format(target.strftime("%-d. %B")))
        )
    # Check if it's tomorrow
    # TODO: Maybe return num hours until tomorrow?
    if target.date() == now.date() + timedelta(days=1):
        return q.set_answer(
            *gen_answer("Það er {0} á morgun.".format(target.strftime("%-d. %B")))
        )

    # Returns num days rounded down, so we increment by one.
    days = _date_diff(now, target, unit="days") + 1

    # Diff. strings for singular vs. plural
    plural = is_plural(days)
    verb = "eru" if plural else "er"
    days_desc = "dagar" if plural else "dagur"

    # Format date
    fmt = "%-d. %B" if now.year == target.year else "%-d. %B %Y"
    tfmt = target.strftime(fmt)

    # Date asked about is in the past
    if days < 0:
        days = abs(days)
        passed = "liðnir" if plural else "liðinn"
        voice = "Það {0} {1} {2} {3} frá {4}.".format(
            verb, days, days_desc, passed, tfmt
        )
        # Convert e.g. '25.' to 'tuttugasta og fimmta'
        voice = re.sub(r" \d+\. ", " " + _DAY_INDEX_DAT[target.day] + " ", voice)
        answer = "{0} {1}".format(days, days_desc)
    # It's in the future
    else:
        voice = "Það {0} {1} {2} þar til {3} gengur í garð.".format(
            verb, days, days_desc, tfmt
        )
        # Convert e.g. '25.' to 'tuttugasti og fimmti'
        voice = re.sub(r" \d+\. ", " " + _DAY_INDEX_NOM[target.day] + " ", voice)
        answer = "{0} {1}".format(days, days_desc)

    response = dict(answer=answer)

    q.set_answer(response, answer, voice)
Exemple #5
0
def _gen_curr_tv_program_answer(q: Query):
    """ Generate answer to query about current TV program """
    sched = _query_tv_schedule_api()
    if not sched:
        return gen_answer(_TV_API_ERRMSG)

    prog = _curr_prog(sched)
    if not prog:
        return gen_answer("Það er engin dagskrá á RÚV núna.")

    title = prog["title"]
    ep = "" if "fréttir" in title.lower() else ""
    answ = "RÚV er að sýna {0}{1}. {2}.".format(
        ep, title, _clean_desc(prog["description"]))
    return gen_answer(answ)
Exemple #6
0
def handle_plain_text(q: Query) -> bool:
    """ Handle a plain text query requesting a call to a telephone number. """
    ql = q.query_lower.strip().rstrip("?")

    pfx = None
    number = None

    for rx in _PHONECALL_REGEXES:
        m = re.search(rx, ql)
        if m:
            pfx = m.group(1)
            telsubj = m.group(2).strip()
            break
    else:
        return False

    # Special handling if context
    if telsubj in _CONTEXT_SUBJ:
        ctx = q.fetch_context()
        if ctx is None or "phone_number" not in ctx:
            a = gen_answer("Ég veit ekki við hvern þú átt")
        else:
            q.set_url("tel:{0}".format(ctx["phone_number"]))
            answer = "Skal gert"
            a = (dict(answer=answer), answer, "")
    # Only number digits
    else:
        clean_num = re.sub(r"[^0-9]", "", telsubj).strip()
        if len(clean_num) < 3:
            # The number is clearly not a valid phone number
            a = gen_answer("{0} er ekki gilt símanúmer.".format(telsubj))
        elif re.search(r"^[\d|\s]+$", clean_num):
            # At this point we have what looks like a legitimate phone number.
            # Send tel: url to trigger phone call in client
            q.set_url("tel:{0}".format(clean_num))
            answer = "Skal gert"
            a = (dict(answer=answer), answer, "")
            q.set_beautified_query("{0}{1}".format(pfx, clean_num))
        else:
            # This is a named subject
            subj_þgf = NounPhrase(telsubj.title()).dative or telsubj
            a = gen_answer("Ég veit ekki símanúmerið hjá {0}".format(subj_þgf))

    q.set_answer(*a)
    q.set_qtype(_TELEPHONE_QTYPE)
    q.query_is_command()

    return True
Exemple #7
0
def _gen_evening_tv_program_answer(q: Query) -> Tuple:
    """ Generate answer to query about the evening's TV programs """
    sched = _query_tv_schedule_api()
    if not sched:
        return gen_answer(_TV_API_ERRMSG)

    prog = _evening_prog(sched)
    if not prog:
        return gen_answer("Það er enginn liður eftir á dagskrá RÚV.")

    answ = ["Klukkan"]
    for p in prog:
        answ.append("{0} : {1}.\n".format(p["startTime"][11:16], p["title"]))
    voice_answer = "".join(answ)
    answer = "".join(answ[1:]).replace(" : ", " ")
    return dict(answer=answer), answer, voice_answer
Exemple #8
0
def sentence(state, result):
    """ Called when sentence processing is complete """
    q: Query = state["query"]
    if "qtype" in result and "qkey" in result:
        # Successfully matched a query type
        q.set_qtype(result.qtype)
        q.set_key(result.qkey)

        # Asking for a location outside Iceland
        if q.location and not in_iceland(q.location):
            return gen_answer("Ég þekki ekki til veðurs utan Íslands")

        handler_func = _HANDLERS[result.qkey]

        try:
            r = handler_func(q, result)
            if r:
                q.set_answer(*r)
        except Exception as e:
            logging.warning(
                "Exception while processing weather query: {0}".format(e))
            q.set_error("E_EXCEPTION: {0}".format(e))
            raise
    else:
        q.set_error("E_QUERY_NOT_UNDERSTOOD")
Exemple #9
0
def handle_plain_text(q):
    """ Handle a plain text query, contained in the q parameter """
    ql = q.query_lower.rstrip("?")

    remote_loc = None
    for rx in _QDISTANCE_REGEXES:
        m = re.search(rx, ql)
        if m:
            remote_loc = m.group(1)
            handler = dist_answer_for_loc
            break
    else:
        # Nothing caught by regexes, bail
        return False

    # Look up in geo API
    try:
        if q.location:
            answ = handler(remote_loc, q)
        else:
            answ = gen_answer("Ég veit ekki hvar þú ert.")
    except Exception as e:
        logging.warning(
            "Exception generating answer from geocode API: {0}".format(e))
        q.set_error("E_EXCEPTION: {0}".format(e))
        answ = None

    if answ:
        q.set_qtype(_DISTANCE_QTYPE)
        q.set_answer(*answ)
        return True

    return False
Exemple #10
0
def query_nearest_stop(query: Query, session, result):
    """ A query for the stop closest to the user """
    # Retrieve the client location
    location = query.location
    if location is None:
        # No location provided in the query
        answer = "Staðsetning óþekkt"
        response = dict(answer=answer)
        voice_answer = "Ég veit ekki hvar þú ert."
        return response, answer, voice_answer
    if not in_iceland(location):
        # User's location is not in Iceland
        return gen_answer("Ég þekki ekki strætósamgöngur utan Íslands.")

    # Get the stop closest to the user
    stop = straeto.BusStop.closest_to(location)
    answer = stop.name
    # Use the same word for the bus stop as in the query
    stop_word = result.stop_word if "stop_word" in result else "stoppistöð"
    va = [
        "Næsta",
        stop_word,
        "er",
        stop.name + ";",
        "þangað",
        "eru",
        voice_distance(straeto.distance(location, stop.location)),
    ]
    # Store a location coordinate and a bus stop name in the context
    query.set_context({"location": stop.location, "bus_stop": stop.name})
    voice_answer = " ".join(va) + "."
    response = dict(answer=answer)
    return response, answer, voice_answer
Exemple #11
0
def declension_answer_for_word(word: str, query: Query) -> AnswerTuple:
    """ Look up all morphological forms of a given word,
        construct natural language response. """

    query.set_qtype("Declension")
    query.set_key(word)
    # Look up in BÍN
    forms = lookup_best_word(word)

    if not forms:
        return gen_answer(_NOT_IN_BIN_MSG.format(word))

    answ = ", ".join(forms)
    response = dict(answer=answ)
    # TODO: Handle plural e.g. "Hér eru"
    cases_desc = "Hér er {0}, um {1}, frá {2}, til {3}".format(*forms)
    voice = "Orðið {0} beygist á eftirfarandi hátt: {1}.".format(
        icequote(word), cases_desc
    )

    # Beautify by placing word in query within quotation marks
    bq = re.sub(word + r"\??$", icequote(word) + "?", query.beautified_query)
    query.set_beautified_query(bq)

    return response, answ, voice
Exemple #12
0
def _gen_count(q: Query, result):
    num_range = None
    if result.qkey == "CountUp":
        num_range = list(range(1, result.first_num + 1))
    elif result.qkey == "CountDown":
        num_range = list(range(0, result.first_num))[::-1]
    else:
        (fn, sn) = (result.first_num, result.second_num)
        if fn > sn:
            (fn, sn) = (sn, fn)
        num_range = list(range(fn, sn + 1))

    if len(num_range) > _MAX_COUNT:
        return gen_answer("Ég nenni ekki að telja svona lengi.")

    answ = "{0}…{1}".format(num_range[0], num_range[-1])
    response = dict(answer=answ)
    components = list()
    delay = result.get("delay", _DEFAULT_DELAY)
    for n in num_range:
        # Default delay results in roughly 1 sec per number in count
        components.append('{0} <break time="{1}s"/>'.format(n, delay))
    voice = " ".join(components)

    return response, answ, voice
Exemple #13
0
def sentence(state: QueryStateDict, result: Result) -> None:
    """ Called when sentence processing is complete """
    q: Query = state["query"]
    if "qtype" in result and "qkey" in result and "subject_nom" in result:
        # Successfully matched a query type
        subj = result["subject_nom"]
        try:
            handlerfunc = _HANDLER_MAP[result.qkey]
            res: Optional[AnswerTuple] = None
            if q.location is not None:
                res = handlerfunc(subj, q.location, result.qkey)
            if res:
                q.set_answer(*res)
                q.set_source("Google Maps")
            else:
                errmsg = "Ekki tókst að fletta upp staðnum {0}".format(
                    icequote(subj))
                q.set_answer(*gen_answer(errmsg))
            q.set_qtype(result.qtype)
            q.set_key(subj)
        except Exception as e:
            logging.warning("Exception answering places query: {0}".format(e))
            q.set_error("E_EXCEPTION: {0}".format(e))
            return
    else:
        q.set_error("E_QUERY_NOT_UNDERSTOOD")
Exemple #14
0
def howlong_desc_answ(target):
    """ Generate answer to a query about length of period to a given date. """
    now = datetime.utcnow()
    days = date_diff(now, target, unit="days")

    # Diff. strings for singular vs. plural
    plural = is_plural(days)
    verb = "eru" if plural else "er"
    days_desc = "dagar" if plural else "dagur"

    # Format date
    fmt = "%-d. %B" if now.year == target.year else "%-d. %B %Y"
    tfmt = target.strftime(fmt)

    # Date asked about is current date
    if days == 0:
        return gen_answer("Það er {0} í dag.".format(tfmt))
    elif days < 0:
        # It's in the past
        days = abs(days)
        passed = "liðinn" if sing else "liðnir"
        voice = "Það {0} {1} {2} {3} frá {4}.".format(verb, days, days_desc,
                                                      passed, tfmt)
        answer = "{0} {1}".format(days, days_desc)
    else:
        # It's in the future
        voice = "Það {0} {1} {2} þar til {3} gengur í garð.".format(
            verb, days, days_desc, tfmt)
        answer = "{0} {1}".format(days, days_desc)

    response = dict(answer=answer)

    return (response, answer, voice)
Exemple #15
0
def get_forecast_answer(query, result):
    """ Handle weather forecast queries """
    loc = query.location
    txt_id = _CAPITAL_FC_ID if (
        loc and _near_capital_region(loc)) else _COUNTRY_FC_ID

    # Did the query mention a specific scope?
    if "location" in result:
        if result.location == "capital":
            txt_id = _CAPITAL_FC_ID
        elif result.location == "general":
            txt_id = _COUNTRY_FC_ID

    try:
        res = forecast_text(txt_id)
    except Exception as e:
        logging.warning("Failed to fetch weather text: {0}".format(e))
        res = None

    if (not res or not "results" in res or not len(res["results"])
            or "content" not in res["results"][0]):
        return gen_answer(_API_ERRMSG)

    answer = res["results"][0]["content"]
    response = dict(answer=answer)
    voice = _descr4voice(answer)

    return response, answer, voice
Exemple #16
0
def get_currweather_answer(query, result):
    """ Handle queries concerning current weather conditions """
    res = _curr_observations(query, result)
    if not res:
        return gen_answer(_API_ERRMSG)

    try:
        temp = int(round(float(res["T"])))  # Round to nearest whole number
        desc = res["W"].lower()
        windsp = float(res["F"])
    except:
        logging.warning("Exception parsing weather API result: {0}".format(e))
        return gen_answer(_API_ERRMSG)

    wind_desc = _wind_descr(windsp)
    wind_ms_str = str(windsp).rstrip("0").rstrip(".")
    temp_type = "hiti" if temp >= 0 else "frost"
    mdesc = ", " + desc + "," if desc else ""

    locdesc = result.get("subject") or "Úti"

    # Meters per second string for voice. Say nothing if "logn".
    voice_ms = ", {0} {1} á sekúndu".format(
        wind_ms_str, "metrar"
        if is_plural(wind_ms_str) else "metri") if wind_ms_str != "0" else ""

    # Format voice string
    voice = "{0} er {1} stiga {2}{3} og {4}{5}".format(
        locdesc.capitalize(),
        abs(temp),
        temp_type,
        mdesc,
        wind_desc,
        voice_ms,
    )

    # Text answer
    answer = "{0}°{1} og {2} ({3} m/s)".format(temp, mdesc, wind_desc,
                                               wind_ms_str)

    response = dict(answer=answer)

    return response, answer, voice
Exemple #17
0
def gen_repeat_answ(text: str, cmd_prefix: str, q: Query):
    atxt = text.strip()
    atxt = atxt[:1].upper() + atxt[1:]  # Capitalize first character
    q.set_answer(*gen_answer(atxt))
    q.set_qtype(_REPEAT_QTYPE)
    q.set_key(atxt)
    q.set_expires(datetime.utcnow() + timedelta(hours=24))
    q.set_context(dict(subject=text))
    # Beautify query by placing text to repeat within quotation marks
    q.set_beautified_query("{0}{1}".format(cmd_prefix.capitalize(),
                                           icequote(atxt.rstrip(".") + ".")))
Exemple #18
0
def answ_address(placename: str, loc: LatLonTuple, qtype: str) -> AnswerTuple:
    """ Generate answer to a question concerning the address of a place. """
    # Look up placename in places API
    res = query_places_api(placename,
                           userloc=loc,
                           fields="formatted_address,name,geometry")

    if (not res or res["status"] != "OK" or "candidates" not in res
            or not res["candidates"]):
        return gen_answer(_PLACES_API_ERRMSG)

    # Use top result in Iceland
    place = _top_candidate(res["candidates"])
    if not place:
        return gen_answer(_NOT_IN_ICELAND_ERRMSG)

    # Remove superfluous "Ísland" in addr string
    addr = re.sub(r", Ísland$", "", place["formatted_address"])
    # Get street name without number to get preposition
    street_name = addr.split()[0].rstrip(",")
    maybe_postcode = re.search(r"^\d\d\d", street_name) is not None
    prep = "í" if maybe_postcode else iceprep_for_street(street_name)
    # Split addr into street name w. number, and remainder
    street_addr = addr.split(",")[0]
    remaining = re.sub(r"^{0}".format(street_addr), "", addr)
    # Get street name in dative case
    addr_þgf = NounPhrase(street_addr).dative or street_addr
    # Assemble final address
    final_addr = "{0}{1}".format(addr_þgf, remaining)

    # Create answer
    answer = final_addr
    voice = "{0} er {1} {2}".format(placename, prep,
                                    numbers_to_neutral(final_addr))
    response = dict(answer=answer)

    return response, answer, voice
Exemple #19
0
def sentence(state: QueryStateDict, result: Result) -> None:
    """ Called when sentence processing is complete """
    q: Query = state["query"]

    if "qtype" not in result or "subject_nom" not in result:
        q.set_error("E_QUERY_NOT_UNDERSTOOD")
        return

    # OK, we've successfully matched a query type
    subj: str = result["subject_nom"]
    answer = "Ég hef enga sérstaka skoðun í þeim efnum."
    q.set_answer(*gen_answer(answer))
    q.set_qtype(_OPINION_QTYPE)
    q.set_context(dict(subject=subj))
    q.set_key(subj)
    q.set_expires(datetime.utcnow() + timedelta(hours=24))
Exemple #20
0
def get_currtemp_answer(query, result):
    """ Handle queries concerning temperature """
    res = _curr_observations(query, result)
    if not res:
        return gen_answer(_API_ERRMSG)

    temp = int(round(float(res["T"])))  # Round to nearest whole number
    temp_type = "hiti" if temp >= 0 else "frost"

    locdesc = result.get("subject") or "Úti"

    voice = "{0} er {1} stiga {2}".format(locdesc.capitalize(), abs(temp),
                                          temp_type)
    answer = "{0}°".format(temp)
    response = dict(answer=answer)

    return response, answer, voice
Exemple #21
0
def sentence(state, result):
    """ Called when sentence processing is complete """
    q: Query = state["query"]
    if "qtype" in result and "qkey" in result:
        # Successfully matched a query type
        q.set_qtype(result.qtype)
        q.set_key(result.qkey)

        try:
            answ = None
            loc = q.location
            if loc:
                # Get relevant info about this location
                if result.qkey == "CurrentPostcode":
                    answ = answer_for_postcode(loc)
                else:
                    answ = answer_for_location(loc)
            if answ and loc is not None:
                # For uniformity, store the returned location in the context
                # !!! TBD: We might want to store an address here as well
                q.set_context({"location": loc})
            else:
                # We either don't have a location or no info about
                # the location associated with the query
                answ = gen_answer("Ég veit ekki hvar þú ert.")

            ql = q.query_lower
            if ql.startswith("hvað er ég"):
                bq = re.sub(
                    r"^hvað er ég",
                    "Hvar er ég",
                    q.beautified_query,
                    flags=re.IGNORECASE,
                )
                q.set_beautified_query(bq)

            q.set_answer(*answ)

        except Exception as e:
            logging.warning(
                "Exception while processing location query: {0}".format(e))
            q.set_error("E_EXCEPTION: {0}".format(e))
            raise
    else:
        q.set_error("E_QUERY_NOT_UNDERSTOOD")
Exemple #22
0
def handle_plain_text(q: Query) -> bool:
    """ Handle a plain text query, contained in the q parameter. """
    ql = q.query_lower.rstrip("?")

    matches = None
    handler = None

    # Distance queries
    for rx in _QDISTANCE_REGEXES:
        matches = re.search(rx, ql)
        if matches:
            handler = dist_answer_for_loc
            break

    # Travel time queries
    if not handler:
        for rx in _QTRAVELTIME_REGEXES:
            matches = re.search(rx, ql)
            if matches:
                handler = traveltime_answer_for_loc
                break

    # Nothing caught by regexes, bail
    if not handler or not matches:
        return False

    # Look up answer in geo API
    try:
        if q.location:
            answ = handler(matches, q)
        else:
            answ = gen_answer(_UNKNOWN_LOC_RESP)
    except Exception as e:
        logging.warning(
            "Exception gen. answer from geocode API: {0}".format(e))
        q.set_error("E_EXCEPTION: {0}".format(e))
        answ = None

    if answ:
        q.set_qtype(_DISTANCE_QTYPE)
        q.set_answer(*answ)
        q.set_source("Google Maps")
        return True

    return False
Exemple #23
0
def _gen_most_mentioned_answer(q) -> bool:
    """ Answer questions about the most mentioned/talked about people in Icelandic news. """
    top = top_persons(limit=_MOST_MENTIONED_COUNT, days=_MOST_MENTIONED_PERIOD)

    q.set_qtype(_STATS_QTYPE)
    q.set_key("MostMentioned")

    if not top:
        # No people for the period, empty scraper db?
        q.set_answer(*gen_answer("Engar manneskjur fundust í gagnagrunni"))
        return True

    answer = natlang_seq([t["name"] for t in top if "name" in t])
    response = dict(answer=answer)
    voice = "Umtöluðustu einstaklingar síðustu daga eru {0}.".format(answer)

    q.set_expires(datetime.utcnow() + timedelta(hours=1))
    q.set_answer(response, answer, voice)

    return True
Exemple #24
0
def declension_answer_for_word(word, query):
    """ Look up all morphological forms of a given word,
        construct natural language response. """

    # Look up in BÍN
    forms = lookup_best_word(word)

    if not forms:
        return gen_answer(_NOT_IN_BIN_MSG.format(word))

    answ = ", ".join(forms)
    response = dict(answer=answ)
    # TODO: Handle plural e.g. "Hér eru"
    cases_desc = "Hér er {0}, um {1}, frá {2}, til {3}".format(*forms)
    voice = "Orðið '{0}' beygist á eftirfarandi hátt: {1}.".format(
        word, cases_desc)

    query.set_qtype("Declension")
    query.set_key(word)

    return response, answ, voice
Exemple #25
0
def sentence(state, result):
    """ Called when sentence processing is complete """
    q: Query = state["query"]
    if "qtype" not in result:
        q.set_error("E_QUERY_NOT_UNDERSTOOD")
        return

    # Successfully matched a query type, we're handling it...
    q.set_qtype(result.qtype)

    # Beautify query by fixing spelling of Wikipedia
    b = q.beautified_query
    for w in _WIKI_VARIATIONS:
        b = b.replace(w, _WIKIPEDIA_CANONICAL)
        b = b.replace(w.capitalize(), _WIKIPEDIA_CANONICAL)
    q.set_beautified_query(b)

    # Check for error in context ref
    if "error_context_reference" in result:
        q.set_answer(*gen_answer("Ég veit ekki til hvers þú vísar."))
        return

    # We have a subject
    if "subject_nom" in result:
        # Fetch data from Wikipedia API
        subj = result["subject_nom"]
        answer = get_wiki_summary(subj)
        response = dict(answer=answer)
        voice = _clean_voice_answer(answer)
        # Set query answer
        q.set_answer(response, answer, voice)
        q.set_key(subj)
        q.set_context(dict(subject=subj))
        q.set_source("Wikipedía")
        # Cache reply for 24 hours
        q.set_expires(datetime.utcnow() + timedelta(hours=24))
        return

    q.set_error("E_QUERY_NOT_UNDERSTOOD")
Exemple #26
0
def sentence(state, result):
    """ Called when sentence processing is complete """
    q: Query = state["query"]
    if "qtype" in result:
        try:
            res = top_news_answer()
            if res:
                # We've successfully answered a query
                q.set_qtype(result.qtype)
                q.set_key("LatestNews")
                q.set_answer(*res)
            else:
                errmsg = "Ekki tókst að sækja fréttir"
                q.set_answer(*gen_answer(errmsg))
            q.set_source("RÚV")
        except Exception as e:
            logging.warning("Exception answering news query '{0}': {1}".format(
                q, e))
            q.set_error("E_EXCEPTION: {0}".format(e))
            return
    else:
        q.set_error("E_QUERY_NOT_UNDERSTOOD")
Exemple #27
0
def handle_plain_text(q):
    """ Handle a plain text query asking about user's current location. """
    ql = q.query_lower.rstrip("?")

    if ql not in _WHERE_AM_I_QUERIES:
        return False

    answ = None
    loc = q.location
    if loc:
        # Get info about this location
        answ = answer_for_location(loc)

    if not answ:
        # We either don't have a location or no info about
        # the location associated with the query
        answ = gen_answer("Ég veit ekki hvar þú ert.")

    q.set_qtype(_LOC_QTYPE)
    q.set_key("CurrentPosition")
    q.set_answer(*answ)

    return True
Exemple #28
0
def gen_random_answer(q, result):
    (num1, num2) = (1, 6)  # Default

    if "numbers" in result:
        # Asking for a number between x and y
        if len(result.numbers) == 2:
            (num1, num2) = sorted(result.numbers)
        # Asking for the roll of an x-sided die
        else:
            if result.numbers[0] == 0:
                return gen_answer("Núll hliða teningar eru ekki til.")
            (num1, num2) = (1, result.numbers[0])

    # Query key is random number range (e.g. 1-6)
    q.set_key("{0}-{1}".format(num1, num2))

    answer = str(random.randint(num1, num2))
    response = dict(answer=answer)
    if result.action == "dieroll":
        voice_answer = "Talan {0} kom upp á teningnum".format(answer)
    else:
        voice_answer = "Ég vel töluna {0}".format(answer)

    return response, answer, voice_answer
Exemple #29
0
def sentence(state, result) -> None:
    """ Called when sentence processing is complete """
    q: Query = state["query"]
    if "qtype" in result and "qkey" in result:
        # Successfully matched a query type
        try:
            loc = q.location
            if result.qkey == "CheapestStation" or loc:
                answ = _answ_for_petrol_query(q, result)
            else:
                # We need a location but don't have one
                answ = gen_answer("Ég veit ekki hvar þú ert.")
            if answ:
                q.set_qtype(result.qtype)
                q.set_key(result.qkey)
                q.set_answer(*answ)
                q.set_source("Gasvaktin")
        except Exception as e:
            logging.warning(
                "Exception while processing petrol query: {0}".format(e))
            q.set_error("E_EXCEPTION: {0}".format(e))
            raise
    else:
        q.set_error("E_QUERY_NOT_UNDERSTOOD")
Exemple #30
0
def sentence(state: QueryStateDict, result: Result) -> None:
    """ Called when sentence processing is complete """
    q = state["query"]

    if "qtype" not in result:
        q.set_error("E_QUERY_NOT_UNDERSTOOD")
        return

    # Connect smartdevice action
    if result.qtype == "ConnectSmartDevice":

        answer = "Skal gert"
        host = flask.request.host

        js = read_jsfile("connectHub.js")

        # Function from the javascript file needs to be called with
        # relevant variables
        js += "connectHub('{0}','{1}')".format(q.client_id, host)

        q.set_command(js)
        q.set_answer(*gen_answer(answer))
        return

    # TODO hardcoded while only one device type is supported
    smartdevice_type = "smartlights"

    # Fetch relevant data from the device_data table to perform an action on the lights
    device_data = cast(Optional[DeviceData], q.client_data(smartdevice_type))

    selected_light: Optional[str] = None
    hue_credentials: Optional[Dict[str, str]] = None

    if device_data is not None and smartdevice_type in device_data:
        dev = device_data[smartdevice_type]
        assert dev is not None
        selected_light = dev.get("selected_light")
        hue_credentials = dev.get("philips_hue")

    if not device_data:
        answer = "Snjalltæki hafa ekki verið sett upp"
        q.set_answer(*gen_answer(answer))
        return

    # Light on or off action
    if (result.qtype == "LightOn"
            or result.qtype == "LightOff") and selected_light == "philips_hue":

        onOrOff = "true" if result.qtype == "LightOn" else "false"

        stofn: Optional[str] = None

        for token in q.token_list or []:
            if token.txt == result.subject and token.has_meanings:
                stofn = token.meanings[0].stofn
                assert stofn is not None
                stofn = _FIX_MAP.get(stofn, stofn)

        js = read_jsfile("lightService.js")

        js += "main('{0}','{1}','{2}', {3});".format(
            hue_credentials["ipAddress"], hue_credentials["username"], stofn,
            onOrOff)

        answer = "{0} {1}".format(stofn, onOrOff)

        q.set_answer(*gen_answer(answer))
        q.set_command(js)
        return

    # Alter light dimmer action
    if result.qtype == "LightDim" and selected_light == "philips_hue":

        number = result.numbers[0]

        stofn = ""

        for token in q.token_list:
            if token.txt == result.subject:
                stofn = token.meanings[0].stofn
                stofn = _FIX_MAP.get(stofn, stofn)

        js = read_jsfile("lightService.js")
        js += "main('{0}','{1}','{2}', true, {3});".format(
            hue_credentials["ipAddress"], hue_credentials["username"], stofn,
            number)

        answer = stofn
        q.set_answer(*gen_answer(answer))
        q.set_command(js)
        return

    # Alter light color action
    if result.qtype == "LightColor" and selected_light == "philips_hue":
        stofn_name = None
        stofn_color = None

        for token in q.token_list or []:
            if token.txt == result.subject and token.has_meanings:
                stofn_name = token.meanings[0].stofn
                stofn_name = _FIX_MAP.get(stofn_name) or stofn_name
            if token.txt == result.color:
                for word_variation in token.meanings:
                    if (word_variation.ordfl == "lo"
                            and word_variation.stofn in _COLOR_NAME_TO_CIE):
                        stofn_color = word_variation.stofn
                        break

        js = read_jsfile("lightService.js")
        js += "main('{0}','{1}','{2}', true, null, {3});".format(
            hue_credentials["ipAddress"],
            hue_credentials["username"],
            stofn_name,
            _COLOR_NAME_TO_CIE[stofn_color.lower()],
        )

        answer = "{0} {1}".format(stofn_color, stofn_name)
        q.set_answer(*gen_answer(answer))
        q.set_command(js)
        return

    # Connected lights info action
    if result.qtype == "HubInfo" and "selected_light" == "philips_hue":
        answer = "Skal gert"

        js = read_jsfile("lightInfo.js")

        q.set_answer(*gen_answer(answer))
        q.set_command(js)
        return

    # Alter saturation action
    if result.qtype == "LightSaturation" and selected_light == "philips_hue":
        number = result.numbers[0]
        stofn = None

        for token in q.token_list or []:
            if token.txt == result.subject and token.has_meanings:
                stofn = token.meanings[0].stofn
                stofn = _FIX_MAP.get(stofn, stofn)

        js = read_jsfile("lightService.js")
        js += "main('{0}','{1}','{2}', undefined, undefined, undefined, {3});".format(
            hue_credentials["ipAddress"], hue_credentials["username"], stofn,
            number)

        answer = "{0} {1}".format(stofn, number)

        q.set_answer(*gen_answer(answer))
        q.set_command(js)
        return

    # No command was applicable: give up
    q.set_error("E_QUERY_NOT_UNDERSTOOD")