def answer_for_location(loc): # 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) descr = None # Special handling of Icelandic locations since we have more info # about them and street/locality names need to be declined. if country_code == "IS": # We received a street name from the API if street: descr = street_desc(street, num, locality) # We at least have a locality (e.g. "Reykjavík") elif locality: descr = iceprep_for_placename(locality) + " " + locality # Only country else: descr = country_desc("IS") # The provided location is abroad. else: sdesc = ("á " + street) if street else "" if num and street: sdesc += " " + num locdesc = ("{0} {1}".format(iceprep_for_placename(locality), locality) if locality else "") # "[á Boulevard St. Germain] [í París] [í Frakklandi]" descr = "{0} {1} {2}".format(sdesc, locdesc, country_desc(country_code)).strip() if not descr: # Fall back on the formatted address string provided by Google descr = "á " + top.get("formatted_address") response = dict(answer=descr) voice = "Þú ert {0}".format(_addr4voice(descr)) answer = descr[0].upper() + descr[1:] return response, answer, voice
def street_desc(street_nom: str, street_num: int, locality_nom: str) -> str: """ Generate description of being on a particular (Icelandic) street with correct preposition and case + locality e.g. 'á Fiskislóð 31 í Reykjavík'. """ street_dat = None locality_dat = None # Start by looking up address in staðfangaskrá to get # the dative case of street name and locality. # This works better than BÍN lookup since not all street # names are present in BÍN. addrinfo = iceaddr_lookup(street_nom, placename=locality_nom, limit=1) if len(addrinfo): street_dat = addrinfo[0]["heiti_tgf"] if locality_nom and locality_nom == addrinfo[0]["stadur_nf"]: locality_dat = addrinfo[0]["stadur_tgf"] # OK, if staðfangaskrá can't help us, try to use BÍN to # get dative version of name. Some names given by Google's # API are generic terms such as "Göngustígur" and the like. if not street_dat: street_dat = nom2dat(street_nom) if not locality_dat: locality_dat = nom2dat(locality_nom) # Create street descr. ("á Fiskislóð 31") street_comp = iceprep_for_street(street_nom) + " " + street_dat if street_num: street_comp += " " + str(street_num) # Append locality if available ("í Reykjavík") if locality_dat: ldesc = iceprep_for_placename(locality_nom) + " " + locality_dat street_comp += " " + ldesc return street_comp
def _locality_desc(locality_nom: str) -> str: """ Return an appropriate preposition plus a locality name in dative case """ locality_dat = nom2dat(locality_nom) return iceprep_for_placename(locality_nom) + " " + locality_dat
def test_geo(): """ Test geography and location-related functions in geo.py """ from geo import ( icelandic_city_name, continent_for_country, coords_for_country, coords_for_street_name, country_name_for_isocode, isocode_for_country_name, icelandic_addr_info, lookup_city_info, parse_address_string, iceprep_for_street, iceprep_for_placename, iceprep_for_country, iceprep_for_cc, capitalize_placename, distance, in_iceland, code_for_us_state, coords_for_us_state_code, location_info, ) assert icelandic_city_name("London") == "Lundúnir" assert icelandic_city_name("Rome") == "Róm" assert continent_for_country("IS") == "EU" assert continent_for_country("no") == "EU" assert continent_for_country("MX") == "NA" assert coords_for_country("DE") is not None assert coords_for_country("it") is not None assert coords_for_street_name("Austurstræti") is not None assert coords_for_street_name("Háaleitisbraut") is not None assert country_name_for_isocode("DE", lang="is") == "Þýskaland" assert country_name_for_isocode("DE") == "Þýskaland" assert isocode_for_country_name("Danmörk", lang="is") == "DK" assert isocode_for_country_name("Danmörk", lang="IS") == "DK" assert isocode_for_country_name("Noregur") == "NO" addr_info = icelandic_addr_info("Fiskislóð 31") assert addr_info and addr_info["stadur_tgf"] == "Reykjavík" # Test city info lookup city_info = lookup_city_info("Kænugarður") assert city_info and len( city_info) == 1 and city_info[0]["country"] == "UA" city_info = lookup_city_info("Kaupmannahöfn") assert city_info and len( city_info) == 1 and city_info[0]["country"] == "DK" city_info = lookup_city_info("Pjongjang") assert city_info and len( city_info) == 1 and city_info[0]["country"] == "KP" city_info = lookup_city_info("Pyongyang") assert city_info and len( city_info) == 1 and city_info[0]["country"] == "KP" # Test address string parsing assert parse_address_string(" Fiskislóð 31") == { "street": "Fiskislóð", "number": 31, "letter": "", } assert parse_address_string("Öldugata 19c ") == { "street": "Öldugata", "number": 19, "letter": "c", } assert parse_address_string(" Dúfnahólar 10 ") == { "street": "Dúfnahólar", "number": 10, "letter": "", } # Test prepositions for street names assert iceprep_for_street("Öldugata") == "á" assert iceprep_for_street("Fiskislóð") == "á" assert iceprep_for_street("Austurstræti") == "í" assert iceprep_for_street("Hamrahlíð") == "í" # Test prepositions for placenames assert iceprep_for_placename("Dalvík") == "á" assert iceprep_for_placename("Akureyri") == "á" assert iceprep_for_placename("Ísafjörður") == "á" assert iceprep_for_placename("Reykjavík") == "í" assert iceprep_for_placename("Hafnarfjörður") == "í" assert iceprep_for_placename("London") == "í" assert iceprep_for_placename("Dyflinni") == "í" # Test prepositions for countries assert iceprep_for_country("Ítalía") == "á" assert iceprep_for_country("Ísland") == "á" assert iceprep_for_country("Þýskaland") == "í" assert iceprep_for_country("Japan") == "í" assert iceprep_for_country("spánn") == "á" # Test prepositions for countries, queried by CC assert iceprep_for_cc("IS") == "á" assert iceprep_for_cc("US") == "í" assert iceprep_for_cc("ES") == "á" assert iceprep_for_cc("es") == "á" # Test placename capitalization assert capitalize_placename("ríó de janeiro") == "Ríó de Janeiro" assert capitalize_placename("vík í mýrdal") == "Vík í Mýrdal" assert capitalize_placename("Vík í mýrdal") == "Vík í Mýrdal" assert capitalize_placename("frankfúrt am main") == "Frankfúrt am Main" assert capitalize_placename("mið-afríkulýðveldið") == "Mið-Afríkulýðveldið" assert capitalize_placename("Norður-kórea") == "Norður-Kórea" assert capitalize_placename("norður-Kórea") == "Norður-Kórea" assert capitalize_placename( "bosnía og hersegóvína") == "Bosnía og Hersegóvína" assert capitalize_placename("Norður-Makedónía") == "Norður-Makedónía" # Distance assert int(distance((64.141439, -21.943944), (65.688131, -18.102528))) == 249 assert in_iceland((66.462205, -15.968417)) assert not in_iceland((62.010846, -6.776709)) assert not in_iceland((62.031342, -18.539553)) # US States assert code_for_us_state("Flórída") == "FL" assert code_for_us_state("Norður-Karólína") == "NC" assert code_for_us_state("Kalifornía") == "CA" assert coords_for_us_state_code("CA") == [36.778261, -119.417932] # Generic location info lookup functions assert "country" in location_info("Reykjavík", "placename") assert "continent" in location_info("Minsk", "placename") assert location_info("Japan", "country")["continent"] == "AS" assert location_info("Danmörk", "country")["continent"] == "EU" assert location_info("Mexíkó", "country")["continent"] == "NA" assert location_info("ísafjörður", "placename")["continent"] == "EU" assert location_info("Meðalfellsvatn", "placename")["country"] == "IS" assert location_info("Georgía", "country")["country"] != "US" assert location_info("Virginía", "placename")["country"] == "US" assert location_info("Norður-Dakóta", "country")["country"] == "US" assert location_info("Kænugarður", "placename")["continent"] == "EU" assert location_info("Fiskislóð 31", "address")["country"] == "IS"
def _format_flight_answer(flights: FlightList) -> Dict[str, str]: """ Takes in a list of flights and returns a dict containing a formatted answer and text for a voice line. Each flight should contain the attributes: 'No': Flight number 'DisplayName': Name of airport/city 'api_airport': Name of Icelandic airport/city 'flight_time': Time of departure/arrival 'Departure': True if departing from api_airport, else False 'Status': Info on flight status (e.g. whether it's cancelled) """ airport: str api_airport: str flight_dt: Optional[datetime] answers: List[str] = [] voice_lines: List[str] = [] for flight in flights: airport = icelandic_city_name(capitalize_placename(flight.get("DisplayName", ""))) api_airport = icelandic_city_name(capitalize_placename(flight.get("api_airport", ""))) flight_dt = flight.get("flight_time") if flight_dt is None or airport == "" or api_airport == "": continue flight_date_str = flight_dt.strftime("%-d. %B") flight_time_str = flight_dt.strftime("%H:%M") if flight.get("Departure"): airport = NounPhrase(airport).genitive or airport api_airport = NounPhrase(api_airport).dative or api_airport # Catch cancelled flights if ( isinstance(flight.get("Status"), str) and "aflýst" in str(flight["Status"]).lower() ): line = f"Flugi {flight.get('No')} frá {api_airport} til {airport} er aflýst." else: line = ( f"Flug {flight.get('No')} til {airport} " f"flýgur frá {api_airport} {flight_date_str} " f"klukkan {flight_time_str} að staðartíma." ) else: airport = NounPhrase(airport).dative or airport prep = iceprep_for_placename(api_airport) api_airport = NounPhrase(api_airport).dative or api_airport if ( isinstance(flight.get("Status"), str) and "aflýst" in str(flight["Status"]).lower() ): line = f"Flugi {flight.get('No')} frá {airport} til {api_airport} er aflýst." else: line = ( f"Flug {flight.get('No')} frá {airport} " f"lendir {prep} {api_airport} {flight_date_str} " f"klukkan {flight_time_str} að staðartíma." ) voice_line = re.sub(r" \d+\. ", " " + _DAY_INDEX_ACC[flight_dt.day] + " ", line) answers.append(line) voice_lines.append(voice_line) return { "answer": "<br/>".join(answers).strip(), "voice": _BREAK_SSML.join(voice_lines).strip(), }
qdata = dict(full=name.title(), first=fn, gender=gender) q.set_client_data("name", qdata) # Generate answer voice = answ.replace(",", "") q.set_answer(dict(answer=answ), answ, voice) q.query_is_command() return True return False def _addr2str(addr: Dict[str, str], case: str = "nf") -> str: """ Format address canonically given dict w. address info. """ assert case in ["nf", "þgf"] prep = iceprep_for_placename(addr["placename"]) astr = "{0} {1} {2} {3}".format( addr["street"], addr["number"], prep, addr["placename"] ) if case == "þgf": try: n = NounPhrase(astr) if n: astr = n.dative or astr except Exception: pass return numbers_to_neutral(astr) _WHATS_MY_ADDR = frozenset( (
def handle_plain_text(q: Query) -> bool: """Handle a plain text query, contained in the q parameter which is an instance of the query.Query class. Returns True if the query was handled, and in that case the appropriate properties on the Query instance have been set, such as the answer and the query type (qtype). If the query is not recognized, returns False.""" ql = q.query_lower.rstrip("?") # Timezone being asked about tz = None # Whether user asked for the time in a particular location specific_desc = None if ql in _TIME_QUERIES: # Use location to determine time zone tz = timezone4loc(q.location, fallback="IS") else: locq = [x for x in _TIME_IN_LOC_QUERIES if ql.startswith(x.lower())] if not locq: return False # Not matching any time queries # This is a query about the time in a particular location, i.e. country or city # Cut away question prefix, leaving only loc name loc = ql[len(locq[0]):].strip() if not loc: return False # No location string # Intelligently capitalize country/city/location name loc = capitalize_placename(loc) # Look up nominative loc_nom = NounPhrase(loc).nominative or loc prep = "í" # Check if loc is a recognised country or city name cc = isocode_for_country_name(loc_nom) if cc and cc in country_timezones: # Look up country timezone # Use the first timezone although some countries have more than one # The timezone list returned by pytz is ordered by "dominance" tz = country_timezones[cc][0] prep = iceprep_for_cc(cc) else: # It's not a country name, look up in city database info = lookup_city_info(loc_nom) if info: top = info[0] location = ( cast(float, top.get("lat_wgs84")), cast(float, top.get("long_wgs84")), ) tz = timezone4loc(location) prep = iceprep_for_placename(loc_nom) if tz: # "Klukkan í Lundúnum er" - Used for voice answer dat = NounPhrase(loc_nom).dative or loc specific_desc = "Klukkan {0} {1} er".format(prep, dat) else: # Unable to find the specified location q.set_qtype(_TIME_QTYPE) q.set_key(loc) q.set_answer( *gen_answer("Ég gat ekki flett upp staðsetningunni {0}".format( icequote(loc)))) return True # We have a timezone. Return formatted answer. if tz: now = datetime.now(timezone(tz)) desc = specific_desc or "Klukkan er" # Create displayable answer answer = "{0:02}:{1:02}".format(now.hour, now.minute) # A detailed response object is usually a list or a dict response = dict(answer=answer) # A voice answer is a plain string that will be # passed as-is to a voice synthesizer voice = "{0} {1}:{2:02}.".format(desc, now.hour, now.minute) q.set_qtype(_TIME_QTYPE) q.set_key(tz) # Query key is the timezone q.set_answer(response, answer, voice) return True return False