script-ui.py
· 24 KiB · Python
Raw
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import hashlib
import json
import logging
import re
import sys
import time
from datetime import datetime, timedelta, timezone
from typing import Any, Dict, List, Optional, TypedDict, Union
import requests
from requests import Response
sys.path.append("/opt/voicetech/integrations/online")
from config import Config
from io_manager import IOManager
from io_manager.io_manager.manager import InputParams
from logger import setup_loggers
logger = logging.getLogger(__name__)
DEBUG_MODE = False
ALLOWED_ACTIONS = [
"waterSupply", # Горячее и холодное водоснабжение
"waterWarm", # Горячее водоснабжение
"waterCold", # Холодное водоснабжение
"electricitySupply", # Электроснабжение
"gasSupply", # Газоснабжение
"heating", # Теплоснабжение
]
EXCEPTION_WORDS = ["семьи", "семья"]
EXCEPTION_STREETS = {
"сако ванцети": "сакко ванцетти",
"сако иванцети": "сакко ванцетти",
"сакой ванцети": "сакко ванцетти",
"3сентября": "3 сентября",
"3воинская": "3 воинская",
"25лет октября": "25 лет октября",
"30лет октября": "30 лет октября",
"40лет комсомола": "40 лет комсомола",
"1905года": "1905 года",
"905года": "1905 года",
"каратаева": "коротаева",
"3интернационала": "III Интернационала",
"3интернационал": "III Интернационала",
"пауза": "краузе",
"кошелова": "кошевого",
"том усинская": "томьусинская",
"мальчишенское": "мочищенское",
"головщика": "галущака",
}
DELETE_WORDS = [
"дзержинский",
"железнодорожный",
"заельцовский",
"калининский",
"кировский",
"ленинский",
"октябрьский",
"первомайский",
"советский",
"центральный",
"район",
"р.",
"город",
"г.",
"номер",
"новосибирск",
]
FILTER_STREETS = {
"Сухарная улица 113": {"exclude": ["1-я", "2-я", "3-я"]},
"Сухарная улица 80": {"exclude": ["1-я", "2-я", "3-я"]},
}
SERVICE_WORDS = {
"улица": "ул",
"ул.": "ул",
"дом": "д",
"д.": "д",
"корпус": "к",
"корп": "к",
"корп.": "к",
}
class OutageResponse(TypedDict):
response: Response
outages: Optional[List]
def normalize_spaces(text: str) -> str:
return re.sub(r"\s+", " ", text).strip()
def normalize_service_words(address: str) -> str:
words = address.split()
result = []
for word in words:
result.append(SERVICE_WORDS.get(word, word))
return " ".join(result)
def remove_trash_words(address: str, trash_words: List[str] = None) -> str:
delete_words = DELETE_WORDS if trash_words is None else trash_words
pattern = r"\b(?:" + "|".join(map(re.escape, delete_words)) + r")\b"
return re.sub(pattern, "", address)
def process_corpus(address: str) -> str:
"""
Все варианты корпуса -> кN
"""
patterns = [
# 43к5
(r"(\d+)к\.?\s*(\d+[а-я]?)", r"\1 к\2"),
# к 5
(r"\bк\.?\s*(\d+[а-я]?)\b", r"к\1"),
]
for pattern, repl in patterns:
address = re.sub(pattern, repl, address)
return address
def glue_house_letter(address: str) -> str:
"""
43 а -> 43а
"""
return re.sub(
r"(\d+)\s+([а-яa-z])\b",
r"\1\2",
address,
flags=re.IGNORECASE,
)
def remove_flat(address: str) -> str:
patterns = [
r"\bквартира\b.*",
r"\bкв\.?\b.*",
]
for pattern in patterns:
address = re.sub(pattern, "", address)
return address
def replace_numbers(address: str, numbers: list[dict]) -> str:
"""
numbers:
[
{
"position": 3,
"length": 2,
"calculated": "43"
}
]
"""
if not numbers:
return address
parts = address.split()
for number in numbers:
pos = number["position"]
if parts[pos] in EXCEPTION_WORDS:
continue
parts[pos] = number["calculated"]
rng = slice(pos + 1, pos + number["length"])
parts[rng] = ["$"] * (number["length"] - 1)
parts = [x for x in parts if x != "$"]
return " ".join(parts)
def w2n(see_host: str, address) -> List[Optional[dict]]:
see_url = f"{see_host}/see/entities/number?text={address}"
headers = {"Accept": "application/json"}
response = requests.get(see_url, headers=headers)
if response.status_code != 200:
return []
else:
json = response.json()
if not json:
return []
return json["number"]
def correct_address(see_host: str, address: str) -> str:
address = address.lower()
address = address.replace(".", ". ")
address = remove_trash_words(address)
address = remove_flat(address)
address = normalize_service_words(address)
address = process_corpus(address)
numbers = w2n(see_host, address)
if numbers:
address = replace_numbers(address, numbers)
address = glue_house_letter(address)
address = remove_trash_words(address, ["ул", "д"])
address = address.replace(",", " ")
address = normalize_spaces(address)
return address
def normalize_address(es_host: str, address: str) -> Dict[str, str]:
query = {
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": address,
"fields": ["address"],
"fuzziness": "AUTO",
"operator": "and",
"prefix_length": 2,
}
}
],
"should": [
{
"match_phrase": {
"address": {
"query": address,
"slop": 3,
"boost": 2,
}
}
},
],
}
},
"size": 1,
}
es_index = "address_registry"
es_url = f"{es_host}/{es_index}/_search"
response = requests.post(es_url, json=query)
if response.status_code != 200:
return None
else:
if response.json()["hits"]["total"]["value"] == 0:
return {"address": "", "district_name": ""}
else:
return response.json()["hits"]["hits"][0]["_source"]
def build_sql_where(address: Dict[str, str]) -> str:
address_parts = address["address"].split()
for i, part in enumerate(address_parts):
for char in part:
if char.isdigit():
address_parts[i] = f" {part} "
break
return "%" + "%".join(address_parts) + "%"
def get_outages(address: Dict[str, str]) -> List[dict]:
where = build_sql_where(address)
params = {
"where": f"address like '{where}' and (start_date::date <= CURRENT_DATE and end_date::date >= CURRENT_DATE)",
"text": "",
"geometry": "",
"spatialRel": " ",
"objectIds": "",
"orderByFields": "",
"outFields": "",
"f": "json",
"geometryType": " ",
"inSR": "",
"groupByFieldsForStatistics": "",
"outSR": "",
"outStatistics": "",
"resultOffset": "",
"resultRecordCount": "",
"time": "",
}
response = requests.get(
"https://map.novo-sibirsk.ru/elitegis/rest/services/maps/disconnections/MapServer/101/query",
params=params,
timeout=10,
verify=False
)
response.raise_for_status()
result = []
data = response.json()
for feature in data["features"]:
feature_address = feature["value"].replace(",", "")
if address["address"] in feature_address:
result.append(feature["attributes"])
if not result:
return []
type_map = {}
system_map = {}
time_map = {}
def get_map(code_values: Dict[str, Any]):
return {value["code"]: value["name"] for value in code_values}
for field in data["fields"]:
if field["name"] == "type_id":
type_map = get_map(field["domain"]["codedValues"])
if field["name"] == "system_id":
system_map = get_map(field["domain"]["codedValues"])
if field["name"] == "time_category":
time_map = get_map(field["domain"]["codedValues"])
actions_map = {
"Горячее водоснабжение": "waterWarm",
"Холодное водоснабжение и канализация": "waterCold",
"Электроснабжение": "electricitySupply",
"Газоснабжение": "gasSupply",
"Теплоснабжение": "heating",
}
for record in result:
record["address"] = (
record["address"]
.replace(",", "")
.replace(f"({record['district_name']})", "")
)
record["type"] = type_map[record["type_id"]]
record["system"] = system_map[record["system_id"]]
record["time"] = time_map[record["time_category"]]
record["actionS"] = actions_map[system_map[record["system_id"]]]
record.pop("oid", "")
record.pop("type_id", "")
record.pop("system_id", "")
record.pop("1", "")
return result
def get_actions_list(actionS: str, replace_water_supply: bool = True) -> List[str]:
actionS_list: List[str] = re.split(r"[,\s;]+", actionS)
if replace_water_supply:
if "waterSupply" in actionS_list:
actionS_list.remove("waterSupply")
if "waterCold" not in actionS_list:
actionS_list.append("waterCold")
if "waterWarm" not in actionS_list:
actionS_list.append("waterWarm")
return actionS_list
def culling_unworthy(outages: List[dict], address: Dict[str, str]) -> List[dict]:
worthy = []
pattern = re.compile(
r"""
^.*? # Имя улицы (любые символы до номера дома)
(?P<house> # Группа для номера дома
\d+ # Обязательные цифры номера
[а-яА-Я]? # Опциональный литер дома (1 буква)
(?:/\d+[а-яА-Я]?)? # Опциональная дробь с литером
)
(?:\s+к(?P<building> # Группа для корпуса
\d+ # Обязательные цифры корпуса
[а-яА-Я]? # Опциональный литер корпуса
(?:/\d+[а-яА-Я]?)? # Опциональная дробь корпуса
)?)?$ # Вся группа корпуса необязательна
""",
re.VERBOSE | re.IGNORECASE,
)
match = pattern.search(address["address"])
if match:
house = match.group("house")
building = match.group("building")
for outage in outages:
o_address = outage["address"]
if o_address[-1] == " ":
o_address = o_address[:-1]
o_match = pattern.search(o_address)
if o_match:
o_house = o_match.group("house")
o_building = o_match.group("building")
if o_house == house and o_building == building:
worthy.append(outage)
else:
continue
if len(worthy) == 0:
numbers = re.findall(r"\d+", address["address"])
for outage in outages:
outage_numbers = re.findall(r"\d+", outage["address"])
if len(outage_numbers) == len(numbers) and set(outage_numbers) == set(
numbers
):
worthy.append(outage)
worthy = [out for out in outages if out["time"] == "Текущие"]
if address["address"] in FILTER_STREETS:
for exc in FILTER_STREETS[address["address"]]["exclude"]:
worthy = [el for el in worthy if exc not in el["address"]]
return worthy
def rename_waterCold(outages: List[dict]) -> List[dict]:
for outage in outages:
if outage["actionS"] == "waterCold":
outage["system"] = "Холодное водоснабжение"
return outages
def build_info(outages: List[dict]) -> str:
# system type date_range
# 0 0 0 -> одно отключение
# 1 0 0 -> два отключения
# 1 1 0 -> два отключения с разными типами
# 1 0 1 -> два отключения с разным временем
# 1 1 1 -> два отключения с разным типом и временем
diff_type = False
diff_range_date = False
o_type = outages[0]["type"]
o_start_date = outages[0]["start_date"]
o_end_date = outages[0]["end_date"]
for outage in outages:
if outage["start_date"] != o_start_date or outage["end_date"] != o_end_date:
diff_range_date = True
if outage["type"] != o_type:
diff_type = True
def get_data_range(start_date: int, end_date: int) -> str:
months_genitive = {
1: "января",
2: "февраля",
3: "марта",
4: "апреля",
5: "мая",
6: "июня",
7: "июля",
8: "августа",
9: "сентября",
10: "октября",
11: "ноября",
12: "декабря",
}
start_date = datetime.fromtimestamp(int(str(start_date)[:10]))
end_date = datetime.fromtimestamp(int(str(end_date)[:10]))
start_month = months_genitive[start_date.month]
end_month = months_genitive[end_date.month]
if start_date.hour == 0 and start_date.minute == 0:
start_date_str = start_date.strftime(
f"%d {start_month} с двенадцати часов ночи"
)
else:
start_date_str = start_date.strftime(f"%d {start_month} с %H:%M")
if end_date.hour == 0 and end_date.minute == 0:
end_date_str = end_date.strftime(
f"по %d {end_month} до двенадцати часов ночи"
)
else:
end_date_str = end_date.strftime(f"по %d {end_month} до %H:%M")
return f"{start_date_str} {end_date_str}"
if len(outages) == 1 and not diff_type and not diff_range_date:
# {type} {system} {date_range}.
result = f"{o_type} {outages[0]['system']} {get_data_range(o_start_date, o_end_date)}"
elif len(outages) > 1 and not diff_type and not diff_range_date:
# если оотключения два
# {type} {system1} и {system2} {date_range}.
# если отключений больше
# {type} {system1}, {system2}, {system3} {date_range}.
separator = " и " if len(outages) == 2 else ", "
result = f"{o_type} {separator.join([o['system'] for o in outages])} {get_data_range(o_start_date, o_end_date)}"
elif len(outages) > 1 and diff_type and not diff_range_date:
# {type1} {system1}, {type2} {system2} {date_range} ...
result = f"""{", ".join([f"{o['type']} {o['system']}" for o in outages])} {get_data_range(o_start_date, o_end_date)}"""
elif len(outages) > 1:
# {type1} {system1} {date_range1}, а также {type2} {system2} {date_range2} ...
result = ", а также ".join([
f"{o['type']} {o['system']} {get_data_range(o['start_date'], o['end_date'])}"
for o in outages
])
return result
def check_outage(outages: List[dict], actionS_list: str) -> Dict[str, str]:
target_outages = []
other_outages = []
for outage in outages:
if outage["actionS"] in actionS_list:
target_outages.append(outage)
else:
other_outages.append(outage)
result = {}
if target_outages:
result["result"] = "info_yes"
result["info"] = build_info(target_outages)
else:
result["result"] = "info_no"
info_map = {
"waterWarm": "Горячее водоснабжение",
"waterCold": "Холодное водоснабжение",
"electricitySupply": "Электроснабжение",
"gasSupply": "Газоснабжение",
"heating": "Теплоснабжение",
}
result["info"] = ", ".join([info_map[a] for a in actionS_list])
if other_outages:
result["result"] = result["result"] + "_other_yes"
result["info_other"] = build_info(other_outages)
else:
result["result"] = result["result"] + "_other_no"
return result
def push_elastic_log(
es_host: str,
log_data: InputParams,
outage_response: Optional[Union[Response, dict]] = None,
redis_data: Optional[Dict[str, Any]] = None,
) -> None:
if not redis_data:
redis_data = {}
channel_type = "telephony" if log_data.phone else "text"
nsk_offset = timezone(timedelta(hours=7))
date = datetime.now(nsk_offset).isoformat()
if isinstance(outage_response, Response) and outage_response:
request = json.loads(outage_response.request.body.decode("utf-8"))
elif isinstance(outage_response, dict):
request = outage_response
else:
request = {}
actions_map = {
"waterWarm": "Горячее водоснабжение",
"waterCold": "Холодное водоснабжение",
"electricitySupply": "Электроснабжение",
"gasSupply": "Газоснабжение",
"heating": "Теплоснабжение",
"waterSupply": "Нет воды",
}
actions_list = get_actions_list(
actionS=log_data.data["actionS"], replace_water_supply=False
)
log = {
"dialog_id": log_data.interaction_id,
"start_date": date,
"end_date": date,
"phone": log_data.phone,
"actionS": ", ".join([
actions_map[a] if a in actions_map else a for a in actions_list
]),
"address": log_data.data["address"],
"request": json.dumps(request, ensure_ascii=False),
"info": log_data.data.get("info", ""),
"result": log_data.data["result"],
"info_other": redis_data.get("info_other", ""),
"channel_type": channel_type,
}
id = hashlib.md5(json.dumps(log).encode("utf-8")).hexdigest()
requests.post(
url=es_host + "/integrations/events/" + id,
headers={"Content-Type": "application/json"},
json=log,
timeout=5,
)
def proccessing_ending_word(info: str) -> str:
exception_words = ["Плановое", "Аварийное", "отключение"]
for exception_word in exception_words:
bad_word = exception_word[: len(exception_word) - 1] + "$" + exception_word[-1]
info = info.replace(exception_word, bad_word)
result_info = (
info
.replace("ие", "ия")
.replace("ое", "ого")
.replace("ее", "его")
.replace("$", "")
)
return result_info
def move_word_to_beginning(address: str, word: str) -> str:
filtered_address = [w for w in address.split() if w.lower() != word.lower()]
result = " ".join(filtered_address)
return f"{word} {result}".strip()
def main() -> None:
integrations_config = Config("integrations.conf")
es_host = integrations_config.get_param("elasticsearch_host")
see_host = integrations_config.get_param("see_host")
logpath = integrations_config.get_param("integrations_logpath_mask")
if DEBUG_MODE:
logpath = "./edds_debug.log"
setup_loggers(logger, level=logging.WARNING, filename=logpath, log_by_day=True)
io_manager = IOManager(logger=logger)
input_params = io_manager.input()
# second round
input_params.data.pop("info_other", "")
c_address = correct_address(see_host=see_host, address=input_params.data["address"])
# Может прийти только число, без улицы
if c_address.isdigit():
push_elastic_log(
log_data=input_params,
redis_data=input_params.data.copy(),
outage_response={"address_before_correct": c_address},
es_host=es_host,
)
return io_manager.output(
is_error=True, goto=False, data=input_params.data_from_args
)
for exception_street in EXCEPTION_STREETS:
if exception_street in c_address:
c_address = c_address.replace(
exception_street, EXCEPTION_STREETS[exception_street]
)
address = normalize_address(es_host=es_host, address=c_address)
# Пробуем достать адрес из addressS
if not address["address"]:
c_address = correct_address(
see_host=see_host, address=input_params.data["addressS"]
)
# Может прийти только число, без улицы
if c_address.isdigit():
push_elastic_log(
log_data=input_params,
redis_data=input_params.data.copy(),
outage_response={"address_before_correct": c_address},
es_host=es_host,
)
return io_manager.output(
is_error=True, goto=False, data=input_params.data_from_args
)
for exception_street in EXCEPTION_STREETS:
if exception_street in c_address:
c_address = c_address.replace(
exception_street, EXCEPTION_STREETS[exception_street]
)
address = normalize_address(es_host=es_host, address=c_address)
if not address["address"]:
input_params.data["result"] = "address_not_found"
push_elastic_log(
log_data=input_params,
redis_data=input_params.data.copy(),
outage_response={"address_before_correct": c_address},
es_host=es_host,
)
return io_manager.output(
is_error=True, goto=False, data=input_params.data_from_args
)
# Адрес для озвучки
address_for_voice = address["address"]
WORDS_TO_MOVE = ["бульвар", "площадь", "площадь", "поселок", "улица", "территория"]
for word in WORDS_TO_MOVE:
if word in address_for_voice:
address_for_voice = move_word_to_beginning(address_for_voice, word)
input_params.data["address"] = address_for_voice
input_params.data_from_args["address"] = address_for_voice
try:
outages = get_outages(address=address)
except TimeoutError:
input_params.data["result"] = "portal_timeout"
return io_manager.output(is_error=True, goto=False, data=input_params.data)
if len(outages) == 0:
input_params.data_from_args["result"] = input_params.data["result"] = (
"info_no_other_no"
)
push_elastic_log(
log_data=input_params,
redis_data=input_params.data.copy(),
outage_response={"address": address["address"]},
es_host=es_host,
)
return io_manager.output(
is_error=False, goto=True, data=input_params.data_from_args
)
actionS_list = get_actions_list(input_params.data["actionS"])
actionS_list = [actionS for actionS in actionS_list if actionS in ALLOWED_ACTIONS]
outages = culling_unworthy(outages=outages, address=address)
outages = rename_waterCold(outages=outages)
result = check_outage(outages=outages, actionS_list=actionS_list)
input_params.data["result"] = result["result"]
target_info = proccessing_ending_word(result["info"])
input_params.data["info"] = target_info
if "info_other" in result:
other_info = proccessing_ending_word(result["info_other"])
input_params.data["info_other"] = other_info
push_elastic_log(
log_data=input_params,
redis_data=input_params.data,
outage_response={"address": address["address"]},
es_host=es_host,
)
return io_manager.output(is_error=False, goto=True, data=input_params.data)
main()
| 1 | #!/usr/bin/env python3 |
| 2 | # -*- coding: UTF-8 -*- |
| 3 | import hashlib |
| 4 | import json |
| 5 | import logging |
| 6 | import re |
| 7 | import sys |
| 8 | import time |
| 9 | from datetime import datetime, timedelta, timezone |
| 10 | from typing import Any, Dict, List, Optional, TypedDict, Union |
| 11 | |
| 12 | import requests |
| 13 | from requests import Response |
| 14 | |
| 15 | sys.path.append("/opt/voicetech/integrations/online") |
| 16 | from config import Config |
| 17 | from io_manager import IOManager |
| 18 | from io_manager.io_manager.manager import InputParams |
| 19 | from logger import setup_loggers |
| 20 | |
| 21 | logger = logging.getLogger(__name__) |
| 22 | DEBUG_MODE = False |
| 23 | |
| 24 | ALLOWED_ACTIONS = [ |
| 25 | "waterSupply", # Горячее и холодное водоснабжение |
| 26 | "waterWarm", # Горячее водоснабжение |
| 27 | "waterCold", # Холодное водоснабжение |
| 28 | "electricitySupply", # Электроснабжение |
| 29 | "gasSupply", # Газоснабжение |
| 30 | "heating", # Теплоснабжение |
| 31 | ] |
| 32 | EXCEPTION_WORDS = ["семьи", "семья"] |
| 33 | EXCEPTION_STREETS = { |
| 34 | "сако ванцети": "сакко ванцетти", |
| 35 | "сако иванцети": "сакко ванцетти", |
| 36 | "сакой ванцети": "сакко ванцетти", |
| 37 | "3сентября": "3 сентября", |
| 38 | "3воинская": "3 воинская", |
| 39 | "25лет октября": "25 лет октября", |
| 40 | "30лет октября": "30 лет октября", |
| 41 | "40лет комсомола": "40 лет комсомола", |
| 42 | "1905года": "1905 года", |
| 43 | "905года": "1905 года", |
| 44 | "каратаева": "коротаева", |
| 45 | "3интернационала": "III Интернационала", |
| 46 | "3интернационал": "III Интернационала", |
| 47 | "пауза": "краузе", |
| 48 | "кошелова": "кошевого", |
| 49 | "том усинская": "томьусинская", |
| 50 | "мальчишенское": "мочищенское", |
| 51 | "головщика": "галущака", |
| 52 | } |
| 53 | |
| 54 | DELETE_WORDS = [ |
| 55 | "дзержинский", |
| 56 | "железнодорожный", |
| 57 | "заельцовский", |
| 58 | "калининский", |
| 59 | "кировский", |
| 60 | "ленинский", |
| 61 | "октябрьский", |
| 62 | "первомайский", |
| 63 | "советский", |
| 64 | "центральный", |
| 65 | "район", |
| 66 | "р.", |
| 67 | "город", |
| 68 | "г.", |
| 69 | "номер", |
| 70 | "новосибирск", |
| 71 | ] |
| 72 | FILTER_STREETS = { |
| 73 | "Сухарная улица 113": {"exclude": ["1-я", "2-я", "3-я"]}, |
| 74 | "Сухарная улица 80": {"exclude": ["1-я", "2-я", "3-я"]}, |
| 75 | } |
| 76 | |
| 77 | SERVICE_WORDS = { |
| 78 | "улица": "ул", |
| 79 | "ул.": "ул", |
| 80 | "дом": "д", |
| 81 | "д.": "д", |
| 82 | "корпус": "к", |
| 83 | "корп": "к", |
| 84 | "корп.": "к", |
| 85 | } |
| 86 | |
| 87 | |
| 88 | class OutageResponse(TypedDict): |
| 89 | response: Response |
| 90 | outages: Optional[List] |
| 91 | |
| 92 | |
| 93 | def normalize_spaces(text: str) -> str: |
| 94 | return re.sub(r"\s+", " ", text).strip() |
| 95 | |
| 96 | |
| 97 | def normalize_service_words(address: str) -> str: |
| 98 | words = address.split() |
| 99 | |
| 100 | result = [] |
| 101 | |
| 102 | for word in words: |
| 103 | result.append(SERVICE_WORDS.get(word, word)) |
| 104 | |
| 105 | return " ".join(result) |
| 106 | |
| 107 | |
| 108 | def remove_trash_words(address: str, trash_words: List[str] = None) -> str: |
| 109 | delete_words = DELETE_WORDS if trash_words is None else trash_words |
| 110 | pattern = r"\b(?:" + "|".join(map(re.escape, delete_words)) + r")\b" |
| 111 | return re.sub(pattern, "", address) |
| 112 | |
| 113 | |
| 114 | def process_corpus(address: str) -> str: |
| 115 | """ |
| 116 | Все варианты корпуса -> кN |
| 117 | """ |
| 118 | |
| 119 | patterns = [ |
| 120 | # 43к5 |
| 121 | (r"(\d+)к\.?\s*(\d+[а-я]?)", r"\1 к\2"), |
| 122 | # к 5 |
| 123 | (r"\bк\.?\s*(\d+[а-я]?)\b", r"к\1"), |
| 124 | ] |
| 125 | |
| 126 | for pattern, repl in patterns: |
| 127 | address = re.sub(pattern, repl, address) |
| 128 | |
| 129 | return address |
| 130 | |
| 131 | |
| 132 | def glue_house_letter(address: str) -> str: |
| 133 | """ |
| 134 | 43 а -> 43а |
| 135 | """ |
| 136 | |
| 137 | return re.sub( |
| 138 | r"(\d+)\s+([а-яa-z])\b", |
| 139 | r"\1\2", |
| 140 | address, |
| 141 | flags=re.IGNORECASE, |
| 142 | ) |
| 143 | |
| 144 | |
| 145 | def remove_flat(address: str) -> str: |
| 146 | patterns = [ |
| 147 | r"\bквартира\b.*", |
| 148 | r"\bкв\.?\b.*", |
| 149 | ] |
| 150 | |
| 151 | for pattern in patterns: |
| 152 | address = re.sub(pattern, "", address) |
| 153 | |
| 154 | return address |
| 155 | |
| 156 | |
| 157 | def replace_numbers(address: str, numbers: list[dict]) -> str: |
| 158 | """ |
| 159 | numbers: |
| 160 | [ |
| 161 | { |
| 162 | "position": 3, |
| 163 | "length": 2, |
| 164 | "calculated": "43" |
| 165 | } |
| 166 | ] |
| 167 | """ |
| 168 | |
| 169 | if not numbers: |
| 170 | return address |
| 171 | |
| 172 | parts = address.split() |
| 173 | |
| 174 | for number in numbers: |
| 175 | pos = number["position"] |
| 176 | |
| 177 | if parts[pos] in EXCEPTION_WORDS: |
| 178 | continue |
| 179 | |
| 180 | parts[pos] = number["calculated"] |
| 181 | |
| 182 | rng = slice(pos + 1, pos + number["length"]) |
| 183 | parts[rng] = ["$"] * (number["length"] - 1) |
| 184 | |
| 185 | parts = [x for x in parts if x != "$"] |
| 186 | |
| 187 | return " ".join(parts) |
| 188 | |
| 189 | |
| 190 | def w2n(see_host: str, address) -> List[Optional[dict]]: |
| 191 | see_url = f"{see_host}/see/entities/number?text={address}" |
| 192 | headers = {"Accept": "application/json"} |
| 193 | response = requests.get(see_url, headers=headers) |
| 194 | if response.status_code != 200: |
| 195 | return [] |
| 196 | else: |
| 197 | json = response.json() |
| 198 | |
| 199 | if not json: |
| 200 | return [] |
| 201 | |
| 202 | return json["number"] |
| 203 | |
| 204 | |
| 205 | def correct_address(see_host: str, address: str) -> str: |
| 206 | |
| 207 | address = address.lower() |
| 208 | |
| 209 | address = address.replace(".", ". ") |
| 210 | |
| 211 | address = remove_trash_words(address) |
| 212 | |
| 213 | address = remove_flat(address) |
| 214 | |
| 215 | address = normalize_service_words(address) |
| 216 | |
| 217 | address = process_corpus(address) |
| 218 | |
| 219 | numbers = w2n(see_host, address) |
| 220 | |
| 221 | if numbers: |
| 222 | address = replace_numbers(address, numbers) |
| 223 | |
| 224 | address = glue_house_letter(address) |
| 225 | |
| 226 | address = remove_trash_words(address, ["ул", "д"]) |
| 227 | |
| 228 | address = address.replace(",", " ") |
| 229 | |
| 230 | address = normalize_spaces(address) |
| 231 | |
| 232 | return address |
| 233 | |
| 234 | |
| 235 | def normalize_address(es_host: str, address: str) -> Dict[str, str]: |
| 236 | query = { |
| 237 | "query": { |
| 238 | "bool": { |
| 239 | "must": [ |
| 240 | { |
| 241 | "multi_match": { |
| 242 | "query": address, |
| 243 | "fields": ["address"], |
| 244 | "fuzziness": "AUTO", |
| 245 | "operator": "and", |
| 246 | "prefix_length": 2, |
| 247 | } |
| 248 | } |
| 249 | ], |
| 250 | "should": [ |
| 251 | { |
| 252 | "match_phrase": { |
| 253 | "address": { |
| 254 | "query": address, |
| 255 | "slop": 3, |
| 256 | "boost": 2, |
| 257 | } |
| 258 | } |
| 259 | }, |
| 260 | ], |
| 261 | } |
| 262 | }, |
| 263 | "size": 1, |
| 264 | } |
| 265 | |
| 266 | es_index = "address_registry" |
| 267 | |
| 268 | es_url = f"{es_host}/{es_index}/_search" |
| 269 | |
| 270 | response = requests.post(es_url, json=query) |
| 271 | |
| 272 | if response.status_code != 200: |
| 273 | return None |
| 274 | else: |
| 275 | if response.json()["hits"]["total"]["value"] == 0: |
| 276 | return {"address": "", "district_name": ""} |
| 277 | else: |
| 278 | return response.json()["hits"]["hits"][0]["_source"] |
| 279 | |
| 280 | |
| 281 | def build_sql_where(address: Dict[str, str]) -> str: |
| 282 | address_parts = address["address"].split() |
| 283 | for i, part in enumerate(address_parts): |
| 284 | for char in part: |
| 285 | if char.isdigit(): |
| 286 | address_parts[i] = f" {part} " |
| 287 | break |
| 288 | |
| 289 | return "%" + "%".join(address_parts) + "%" |
| 290 | |
| 291 | |
| 292 | def get_outages(address: Dict[str, str]) -> List[dict]: |
| 293 | where = build_sql_where(address) |
| 294 | params = { |
| 295 | "where": f"address like '{where}' and (start_date::date <= CURRENT_DATE and end_date::date >= CURRENT_DATE)", |
| 296 | "text": "", |
| 297 | "geometry": "", |
| 298 | "spatialRel": " ", |
| 299 | "objectIds": "", |
| 300 | "orderByFields": "", |
| 301 | "outFields": "", |
| 302 | "f": "json", |
| 303 | "geometryType": " ", |
| 304 | "inSR": "", |
| 305 | "groupByFieldsForStatistics": "", |
| 306 | "outSR": "", |
| 307 | "outStatistics": "", |
| 308 | "resultOffset": "", |
| 309 | "resultRecordCount": "", |
| 310 | "time": "", |
| 311 | } |
| 312 | response = requests.get( |
| 313 | "https://map.novo-sibirsk.ru/elitegis/rest/services/maps/disconnections/MapServer/101/query", |
| 314 | params=params, |
| 315 | timeout=10, |
| 316 | verify=False |
| 317 | ) |
| 318 | response.raise_for_status() |
| 319 | |
| 320 | result = [] |
| 321 | |
| 322 | data = response.json() |
| 323 | |
| 324 | for feature in data["features"]: |
| 325 | feature_address = feature["value"].replace(",", "") |
| 326 | if address["address"] in feature_address: |
| 327 | result.append(feature["attributes"]) |
| 328 | |
| 329 | if not result: |
| 330 | return [] |
| 331 | |
| 332 | type_map = {} |
| 333 | system_map = {} |
| 334 | time_map = {} |
| 335 | |
| 336 | def get_map(code_values: Dict[str, Any]): |
| 337 | return {value["code"]: value["name"] for value in code_values} |
| 338 | |
| 339 | for field in data["fields"]: |
| 340 | if field["name"] == "type_id": |
| 341 | type_map = get_map(field["domain"]["codedValues"]) |
| 342 | if field["name"] == "system_id": |
| 343 | system_map = get_map(field["domain"]["codedValues"]) |
| 344 | if field["name"] == "time_category": |
| 345 | time_map = get_map(field["domain"]["codedValues"]) |
| 346 | |
| 347 | actions_map = { |
| 348 | "Горячее водоснабжение": "waterWarm", |
| 349 | "Холодное водоснабжение и канализация": "waterCold", |
| 350 | "Электроснабжение": "electricitySupply", |
| 351 | "Газоснабжение": "gasSupply", |
| 352 | "Теплоснабжение": "heating", |
| 353 | } |
| 354 | |
| 355 | for record in result: |
| 356 | record["address"] = ( |
| 357 | record["address"] |
| 358 | .replace(",", "") |
| 359 | .replace(f"({record['district_name']})", "") |
| 360 | ) |
| 361 | record["type"] = type_map[record["type_id"]] |
| 362 | record["system"] = system_map[record["system_id"]] |
| 363 | record["time"] = time_map[record["time_category"]] |
| 364 | record["actionS"] = actions_map[system_map[record["system_id"]]] |
| 365 | record.pop("oid", "") |
| 366 | record.pop("type_id", "") |
| 367 | record.pop("system_id", "") |
| 368 | record.pop("1", "") |
| 369 | |
| 370 | return result |
| 371 | |
| 372 | |
| 373 | def get_actions_list(actionS: str, replace_water_supply: bool = True) -> List[str]: |
| 374 | actionS_list: List[str] = re.split(r"[,\s;]+", actionS) |
| 375 | |
| 376 | if replace_water_supply: |
| 377 | if "waterSupply" in actionS_list: |
| 378 | actionS_list.remove("waterSupply") |
| 379 | if "waterCold" not in actionS_list: |
| 380 | actionS_list.append("waterCold") |
| 381 | if "waterWarm" not in actionS_list: |
| 382 | actionS_list.append("waterWarm") |
| 383 | |
| 384 | return actionS_list |
| 385 | |
| 386 | |
| 387 | def culling_unworthy(outages: List[dict], address: Dict[str, str]) -> List[dict]: |
| 388 | worthy = [] |
| 389 | pattern = re.compile( |
| 390 | r""" |
| 391 | ^.*? # Имя улицы (любые символы до номера дома) |
| 392 | (?P<house> # Группа для номера дома |
| 393 | \d+ # Обязательные цифры номера |
| 394 | [а-яА-Я]? # Опциональный литер дома (1 буква) |
| 395 | (?:/\d+[а-яА-Я]?)? # Опциональная дробь с литером |
| 396 | ) |
| 397 | (?:\s+к(?P<building> # Группа для корпуса |
| 398 | \d+ # Обязательные цифры корпуса |
| 399 | [а-яА-Я]? # Опциональный литер корпуса |
| 400 | (?:/\d+[а-яА-Я]?)? # Опциональная дробь корпуса |
| 401 | )?)?$ # Вся группа корпуса необязательна |
| 402 | """, |
| 403 | re.VERBOSE | re.IGNORECASE, |
| 404 | ) |
| 405 | |
| 406 | match = pattern.search(address["address"]) |
| 407 | if match: |
| 408 | house = match.group("house") |
| 409 | building = match.group("building") |
| 410 | |
| 411 | for outage in outages: |
| 412 | o_address = outage["address"] |
| 413 | if o_address[-1] == " ": |
| 414 | o_address = o_address[:-1] |
| 415 | o_match = pattern.search(o_address) |
| 416 | if o_match: |
| 417 | o_house = o_match.group("house") |
| 418 | o_building = o_match.group("building") |
| 419 | if o_house == house and o_building == building: |
| 420 | worthy.append(outage) |
| 421 | else: |
| 422 | continue |
| 423 | |
| 424 | if len(worthy) == 0: |
| 425 | numbers = re.findall(r"\d+", address["address"]) |
| 426 | for outage in outages: |
| 427 | outage_numbers = re.findall(r"\d+", outage["address"]) |
| 428 | if len(outage_numbers) == len(numbers) and set(outage_numbers) == set( |
| 429 | numbers |
| 430 | ): |
| 431 | worthy.append(outage) |
| 432 | |
| 433 | worthy = [out for out in outages if out["time"] == "Текущие"] |
| 434 | |
| 435 | if address["address"] in FILTER_STREETS: |
| 436 | for exc in FILTER_STREETS[address["address"]]["exclude"]: |
| 437 | worthy = [el for el in worthy if exc not in el["address"]] |
| 438 | |
| 439 | return worthy |
| 440 | |
| 441 | |
| 442 | def rename_waterCold(outages: List[dict]) -> List[dict]: |
| 443 | for outage in outages: |
| 444 | if outage["actionS"] == "waterCold": |
| 445 | outage["system"] = "Холодное водоснабжение" |
| 446 | return outages |
| 447 | |
| 448 | |
| 449 | def build_info(outages: List[dict]) -> str: |
| 450 | # system type date_range |
| 451 | # 0 0 0 -> одно отключение |
| 452 | # 1 0 0 -> два отключения |
| 453 | # 1 1 0 -> два отключения с разными типами |
| 454 | # 1 0 1 -> два отключения с разным временем |
| 455 | # 1 1 1 -> два отключения с разным типом и временем |
| 456 | diff_type = False |
| 457 | diff_range_date = False |
| 458 | |
| 459 | o_type = outages[0]["type"] |
| 460 | o_start_date = outages[0]["start_date"] |
| 461 | o_end_date = outages[0]["end_date"] |
| 462 | |
| 463 | for outage in outages: |
| 464 | if outage["start_date"] != o_start_date or outage["end_date"] != o_end_date: |
| 465 | diff_range_date = True |
| 466 | if outage["type"] != o_type: |
| 467 | diff_type = True |
| 468 | |
| 469 | def get_data_range(start_date: int, end_date: int) -> str: |
| 470 | months_genitive = { |
| 471 | 1: "января", |
| 472 | 2: "февраля", |
| 473 | 3: "марта", |
| 474 | 4: "апреля", |
| 475 | 5: "мая", |
| 476 | 6: "июня", |
| 477 | 7: "июля", |
| 478 | 8: "августа", |
| 479 | 9: "сентября", |
| 480 | 10: "октября", |
| 481 | 11: "ноября", |
| 482 | 12: "декабря", |
| 483 | } |
| 484 | start_date = datetime.fromtimestamp(int(str(start_date)[:10])) |
| 485 | end_date = datetime.fromtimestamp(int(str(end_date)[:10])) |
| 486 | start_month = months_genitive[start_date.month] |
| 487 | end_month = months_genitive[end_date.month] |
| 488 | |
| 489 | if start_date.hour == 0 and start_date.minute == 0: |
| 490 | start_date_str = start_date.strftime( |
| 491 | f"%d {start_month} с двенадцати часов ночи" |
| 492 | ) |
| 493 | else: |
| 494 | start_date_str = start_date.strftime(f"%d {start_month} с %H:%M") |
| 495 | |
| 496 | if end_date.hour == 0 and end_date.minute == 0: |
| 497 | end_date_str = end_date.strftime( |
| 498 | f"по %d {end_month} до двенадцати часов ночи" |
| 499 | ) |
| 500 | else: |
| 501 | end_date_str = end_date.strftime(f"по %d {end_month} до %H:%M") |
| 502 | |
| 503 | return f"{start_date_str} {end_date_str}" |
| 504 | |
| 505 | if len(outages) == 1 and not diff_type and not diff_range_date: |
| 506 | # {type} {system} {date_range}. |
| 507 | result = f"{o_type} {outages[0]['system']} {get_data_range(o_start_date, o_end_date)}" |
| 508 | |
| 509 | elif len(outages) > 1 and not diff_type and not diff_range_date: |
| 510 | # если оотключения два |
| 511 | # {type} {system1} и {system2} {date_range}. |
| 512 | # если отключений больше |
| 513 | # {type} {system1}, {system2}, {system3} {date_range}. |
| 514 | separator = " и " if len(outages) == 2 else ", " |
| 515 | result = f"{o_type} {separator.join([o['system'] for o in outages])} {get_data_range(o_start_date, o_end_date)}" |
| 516 | |
| 517 | elif len(outages) > 1 and diff_type and not diff_range_date: |
| 518 | # {type1} {system1}, {type2} {system2} {date_range} ... |
| 519 | result = f"""{", ".join([f"{o['type']} {o['system']}" for o in outages])} {get_data_range(o_start_date, o_end_date)}""" |
| 520 | |
| 521 | elif len(outages) > 1: |
| 522 | # {type1} {system1} {date_range1}, а также {type2} {system2} {date_range2} ... |
| 523 | result = ", а также ".join([ |
| 524 | f"{o['type']} {o['system']} {get_data_range(o['start_date'], o['end_date'])}" |
| 525 | for o in outages |
| 526 | ]) |
| 527 | |
| 528 | return result |
| 529 | |
| 530 | |
| 531 | def check_outage(outages: List[dict], actionS_list: str) -> Dict[str, str]: |
| 532 | target_outages = [] |
| 533 | other_outages = [] |
| 534 | |
| 535 | for outage in outages: |
| 536 | if outage["actionS"] in actionS_list: |
| 537 | target_outages.append(outage) |
| 538 | else: |
| 539 | other_outages.append(outage) |
| 540 | |
| 541 | result = {} |
| 542 | |
| 543 | if target_outages: |
| 544 | result["result"] = "info_yes" |
| 545 | result["info"] = build_info(target_outages) |
| 546 | else: |
| 547 | result["result"] = "info_no" |
| 548 | info_map = { |
| 549 | "waterWarm": "Горячее водоснабжение", |
| 550 | "waterCold": "Холодное водоснабжение", |
| 551 | "electricitySupply": "Электроснабжение", |
| 552 | "gasSupply": "Газоснабжение", |
| 553 | "heating": "Теплоснабжение", |
| 554 | } |
| 555 | result["info"] = ", ".join([info_map[a] for a in actionS_list]) |
| 556 | |
| 557 | if other_outages: |
| 558 | result["result"] = result["result"] + "_other_yes" |
| 559 | result["info_other"] = build_info(other_outages) |
| 560 | else: |
| 561 | result["result"] = result["result"] + "_other_no" |
| 562 | |
| 563 | return result |
| 564 | |
| 565 | |
| 566 | def push_elastic_log( |
| 567 | es_host: str, |
| 568 | log_data: InputParams, |
| 569 | outage_response: Optional[Union[Response, dict]] = None, |
| 570 | redis_data: Optional[Dict[str, Any]] = None, |
| 571 | ) -> None: |
| 572 | if not redis_data: |
| 573 | redis_data = {} |
| 574 | |
| 575 | channel_type = "telephony" if log_data.phone else "text" |
| 576 | nsk_offset = timezone(timedelta(hours=7)) |
| 577 | date = datetime.now(nsk_offset).isoformat() |
| 578 | |
| 579 | if isinstance(outage_response, Response) and outage_response: |
| 580 | request = json.loads(outage_response.request.body.decode("utf-8")) |
| 581 | elif isinstance(outage_response, dict): |
| 582 | request = outage_response |
| 583 | else: |
| 584 | request = {} |
| 585 | |
| 586 | actions_map = { |
| 587 | "waterWarm": "Горячее водоснабжение", |
| 588 | "waterCold": "Холодное водоснабжение", |
| 589 | "electricitySupply": "Электроснабжение", |
| 590 | "gasSupply": "Газоснабжение", |
| 591 | "heating": "Теплоснабжение", |
| 592 | "waterSupply": "Нет воды", |
| 593 | } |
| 594 | actions_list = get_actions_list( |
| 595 | actionS=log_data.data["actionS"], replace_water_supply=False |
| 596 | ) |
| 597 | |
| 598 | log = { |
| 599 | "dialog_id": log_data.interaction_id, |
| 600 | "start_date": date, |
| 601 | "end_date": date, |
| 602 | "phone": log_data.phone, |
| 603 | "actionS": ", ".join([ |
| 604 | actions_map[a] if a in actions_map else a for a in actions_list |
| 605 | ]), |
| 606 | "address": log_data.data["address"], |
| 607 | "request": json.dumps(request, ensure_ascii=False), |
| 608 | "info": log_data.data.get("info", ""), |
| 609 | "result": log_data.data["result"], |
| 610 | "info_other": redis_data.get("info_other", ""), |
| 611 | "channel_type": channel_type, |
| 612 | } |
| 613 | |
| 614 | id = hashlib.md5(json.dumps(log).encode("utf-8")).hexdigest() |
| 615 | |
| 616 | requests.post( |
| 617 | url=es_host + "/integrations/events/" + id, |
| 618 | headers={"Content-Type": "application/json"}, |
| 619 | json=log, |
| 620 | timeout=5, |
| 621 | ) |
| 622 | |
| 623 | |
| 624 | def proccessing_ending_word(info: str) -> str: |
| 625 | exception_words = ["Плановое", "Аварийное", "отключение"] |
| 626 | for exception_word in exception_words: |
| 627 | bad_word = exception_word[: len(exception_word) - 1] + "$" + exception_word[-1] |
| 628 | info = info.replace(exception_word, bad_word) |
| 629 | result_info = ( |
| 630 | info |
| 631 | .replace("ие", "ия") |
| 632 | .replace("ое", "ого") |
| 633 | .replace("ее", "его") |
| 634 | .replace("$", "") |
| 635 | ) |
| 636 | |
| 637 | return result_info |
| 638 | |
| 639 | |
| 640 | def move_word_to_beginning(address: str, word: str) -> str: |
| 641 | filtered_address = [w for w in address.split() if w.lower() != word.lower()] |
| 642 | result = " ".join(filtered_address) |
| 643 | |
| 644 | return f"{word} {result}".strip() |
| 645 | |
| 646 | |
| 647 | def main() -> None: |
| 648 | integrations_config = Config("integrations.conf") |
| 649 | es_host = integrations_config.get_param("elasticsearch_host") |
| 650 | see_host = integrations_config.get_param("see_host") |
| 651 | logpath = integrations_config.get_param("integrations_logpath_mask") |
| 652 | |
| 653 | if DEBUG_MODE: |
| 654 | logpath = "./edds_debug.log" |
| 655 | |
| 656 | setup_loggers(logger, level=logging.WARNING, filename=logpath, log_by_day=True) |
| 657 | |
| 658 | io_manager = IOManager(logger=logger) |
| 659 | input_params = io_manager.input() |
| 660 | |
| 661 | # second round |
| 662 | input_params.data.pop("info_other", "") |
| 663 | |
| 664 | c_address = correct_address(see_host=see_host, address=input_params.data["address"]) |
| 665 | |
| 666 | # Может прийти только число, без улицы |
| 667 | if c_address.isdigit(): |
| 668 | push_elastic_log( |
| 669 | log_data=input_params, |
| 670 | redis_data=input_params.data.copy(), |
| 671 | outage_response={"address_before_correct": c_address}, |
| 672 | es_host=es_host, |
| 673 | ) |
| 674 | return io_manager.output( |
| 675 | is_error=True, goto=False, data=input_params.data_from_args |
| 676 | ) |
| 677 | |
| 678 | for exception_street in EXCEPTION_STREETS: |
| 679 | if exception_street in c_address: |
| 680 | c_address = c_address.replace( |
| 681 | exception_street, EXCEPTION_STREETS[exception_street] |
| 682 | ) |
| 683 | address = normalize_address(es_host=es_host, address=c_address) |
| 684 | |
| 685 | # Пробуем достать адрес из addressS |
| 686 | if not address["address"]: |
| 687 | c_address = correct_address( |
| 688 | see_host=see_host, address=input_params.data["addressS"] |
| 689 | ) |
| 690 | |
| 691 | # Может прийти только число, без улицы |
| 692 | if c_address.isdigit(): |
| 693 | push_elastic_log( |
| 694 | log_data=input_params, |
| 695 | redis_data=input_params.data.copy(), |
| 696 | outage_response={"address_before_correct": c_address}, |
| 697 | es_host=es_host, |
| 698 | ) |
| 699 | return io_manager.output( |
| 700 | is_error=True, goto=False, data=input_params.data_from_args |
| 701 | ) |
| 702 | |
| 703 | for exception_street in EXCEPTION_STREETS: |
| 704 | if exception_street in c_address: |
| 705 | c_address = c_address.replace( |
| 706 | exception_street, EXCEPTION_STREETS[exception_street] |
| 707 | ) |
| 708 | address = normalize_address(es_host=es_host, address=c_address) |
| 709 | |
| 710 | if not address["address"]: |
| 711 | input_params.data["result"] = "address_not_found" |
| 712 | push_elastic_log( |
| 713 | log_data=input_params, |
| 714 | redis_data=input_params.data.copy(), |
| 715 | outage_response={"address_before_correct": c_address}, |
| 716 | es_host=es_host, |
| 717 | ) |
| 718 | return io_manager.output( |
| 719 | is_error=True, goto=False, data=input_params.data_from_args |
| 720 | ) |
| 721 | |
| 722 | # Адрес для озвучки |
| 723 | address_for_voice = address["address"] |
| 724 | WORDS_TO_MOVE = ["бульвар", "площадь", "площадь", "поселок", "улица", "территория"] |
| 725 | |
| 726 | for word in WORDS_TO_MOVE: |
| 727 | if word in address_for_voice: |
| 728 | address_for_voice = move_word_to_beginning(address_for_voice, word) |
| 729 | |
| 730 | input_params.data["address"] = address_for_voice |
| 731 | input_params.data_from_args["address"] = address_for_voice |
| 732 | |
| 733 | try: |
| 734 | outages = get_outages(address=address) |
| 735 | except TimeoutError: |
| 736 | input_params.data["result"] = "portal_timeout" |
| 737 | return io_manager.output(is_error=True, goto=False, data=input_params.data) |
| 738 | |
| 739 | if len(outages) == 0: |
| 740 | input_params.data_from_args["result"] = input_params.data["result"] = ( |
| 741 | "info_no_other_no" |
| 742 | ) |
| 743 | |
| 744 | push_elastic_log( |
| 745 | log_data=input_params, |
| 746 | redis_data=input_params.data.copy(), |
| 747 | outage_response={"address": address["address"]}, |
| 748 | es_host=es_host, |
| 749 | ) |
| 750 | |
| 751 | return io_manager.output( |
| 752 | is_error=False, goto=True, data=input_params.data_from_args |
| 753 | ) |
| 754 | |
| 755 | actionS_list = get_actions_list(input_params.data["actionS"]) |
| 756 | actionS_list = [actionS for actionS in actionS_list if actionS in ALLOWED_ACTIONS] |
| 757 | outages = culling_unworthy(outages=outages, address=address) |
| 758 | outages = rename_waterCold(outages=outages) |
| 759 | result = check_outage(outages=outages, actionS_list=actionS_list) |
| 760 | |
| 761 | input_params.data["result"] = result["result"] |
| 762 | target_info = proccessing_ending_word(result["info"]) |
| 763 | input_params.data["info"] = target_info |
| 764 | |
| 765 | if "info_other" in result: |
| 766 | other_info = proccessing_ending_word(result["info_other"]) |
| 767 | input_params.data["info_other"] = other_info |
| 768 | |
| 769 | push_elastic_log( |
| 770 | log_data=input_params, |
| 771 | redis_data=input_params.data, |
| 772 | outage_response={"address": address["address"]}, |
| 773 | es_host=es_host, |
| 774 | ) |
| 775 | |
| 776 | return io_manager.output(is_error=False, goto=True, data=input_params.data) |
| 777 | |
| 778 | |
| 779 | main() |