show ics howto in condfig flow

This commit is contained in:
5ila5
2024-08-15 17:28:31 +02:00
committed by 5ila5
parent 1ac5d108c6
commit 43cfcf6dff
4 changed files with 15589 additions and 2749 deletions

View File

@@ -6,6 +6,7 @@ import inspect
import json import json
import re import re
import site import site
from functools import lru_cache
from pathlib import Path from pathlib import Path
from types import ModuleType from types import ModuleType
from typing import Any, Callable, Tuple, TypedDict, TypeVar from typing import Any, Callable, Tuple, TypedDict, TypeVar
@@ -43,6 +44,14 @@ ARG_TRANSLATIONS_TO_KEEP = ["calendar_title"]
ARG_DESCRIPTIONS_TO_KEEP = ["calendar_title"] ARG_DESCRIPTIONS_TO_KEEP = ["calendar_title"]
ARG_GENERAL_KEYS_TO_KEEP = ["title", "description"] ARG_GENERAL_KEYS_TO_KEEP = ["title", "description"]
PACKAGE_DIR = (
Path(__file__).resolve().parents[0]
/ "custom_components"
/ "waste_collection_schedule"
)
SOURCE_DIR = PACKAGE_DIR / "waste_collection_schedule" / "source"
DOC_URL_BASE = "https://github.com/mampfes/hacs_waste_collection_schedule/blob/master"
class SourceInfo: class SourceInfo:
def __init__( def __init__(
@@ -138,6 +147,41 @@ class SourceInfo:
return self._custom_howto return self._custom_howto
class IcsSourceInfo(SourceInfo):
def __init__(
self,
filename: str,
title: str,
url: str,
country: str,
limit_params: list[str],
extra_info_default_params: dict[str, Any] = {},
custom_howto: dict[str, str] = {},
):
_, ics_sources = get_source_by_file("ics")
ics_source = ics_sources[0]
params = set(ics_source.params) - set(limit_params)
translations = ics_source.custom_param_translation
descriptions = ics_source.custom_param_description
for translation in [*translations.values(), *descriptions.values()]:
for param in list(translation.keys()):
if param not in params:
translation.pop(param)
super().__init__(
filename=filename,
module=None,
title=title,
url=url,
country=country,
params=list(params),
extra_info_default_params=extra_info_default_params,
custom_param_translation=translations,
custom_param_description=descriptions,
custom_howto=custom_howto,
)
class Section: class Section:
def __init__(self, section): def __init__(self, section):
self._section = section self._section = section
@@ -225,19 +269,12 @@ def main() -> None:
def browse_sources() -> list[SourceInfo]: def browse_sources() -> list[SourceInfo]:
"""Browse all .py files in the `source` directory""" """Browse all .py files in the `source` directory"""
package_dir = (
Path(__file__).resolve().parents[0]
/ "custom_components"
/ "waste_collection_schedule"
)
source_dir = package_dir / "waste_collection_schedule" / "source"
# add module directory to path # add module directory to path
site.addsitedir(str(package_dir)) site.addsitedir(str(PACKAGE_DIR))
files = filter( files = filter(
lambda x: x != "__init__", lambda x: x != "__init__",
map(lambda x: x.stem, source_dir.glob("*.py")), map(lambda x: x.stem, SOURCE_DIR.glob("*.py")),
) )
modules: dict[str, ModuleType] = {} modules: dict[str, ModuleType] = {}
@@ -245,56 +282,9 @@ def browse_sources() -> list[SourceInfo]:
# retrieve all data from sources # retrieve all data from sources
for f in files: for f in files:
# iterate through all *.py files in waste_collection_schedule/source module, sources_out = get_source_by_file(f)
module = importlib.import_module(f"waste_collection_schedule.source.{f}")
modules[f] = module modules[f] = module
sources += sources_out
title = module.TITLE
url = module.URL
country = getattr(module, "COUNTRY", f.split("_")[-1])
sig = inspect.signature(module.Source.__init__)
params = [param.name for param in sig.parameters.values()]
if "self" in params:
params.remove("self")
param_translations = getattr(module, "PARAM_TRANSLATIONS", {})
param_descriptions = getattr(module, "PARAM_DESCRIPTIONS", {})
howto = getattr(module, "HOW_TO_GET_ARGUMENTS_DESCRIPTION", {})
filename = f"/doc/source/{f}.md"
if title is not None:
sources.append(
SourceInfo(
filename=filename,
module=f,
title=title,
url=url,
country=country,
params=params,
custom_param_translation=param_translations,
custom_param_description=param_descriptions,
custom_howto=howto,
)
)
extra_info: list[ExtraInfoDict] | Callable[[], list[ExtraInfoDict]] = getattr(
module, "EXTRA_INFO", []
)
if callable(extra_info):
extra_info = extra_info()
for e in extra_info:
sources.append(
SourceInfo(
filename=filename,
module=f,
title=e.get("title", title),
url=e.get("url", url),
country=e.get("country", country),
params=params,
extra_info_default_params=e.get("default_params", {}),
custom_howto=howto,
)
)
update_awido_de(modules) update_awido_de(modules)
update_ctrace_de(modules) update_ctrace_de(modules)
@@ -306,6 +296,61 @@ def browse_sources() -> list[SourceInfo]:
return sources return sources
@lru_cache(maxsize=None)
def get_source_by_file(file: str) -> tuple[ModuleType, list[SourceInfo]]:
# iterate through all *.py files in waste_collection_schedule/source
module = importlib.import_module(f"waste_collection_schedule.source.{file}")
title = module.TITLE
url = module.URL
country = getattr(module, "COUNTRY", file.split("_")[-1])
sig = inspect.signature(module.Source.__init__)
params = [param.name for param in sig.parameters.values()]
if "self" in params:
params.remove("self")
param_translations = getattr(module, "PARAM_TRANSLATIONS", {})
param_descriptions = getattr(module, "PARAM_DESCRIPTIONS", {})
howto = getattr(module, "HOW_TO_GET_ARGUMENTS_DESCRIPTION", {})
filename = f"/doc/source/{file}.md"
sources = []
if title is not None:
sources.append(
SourceInfo(
filename=filename,
module=file,
title=title,
url=url,
country=country,
params=params,
custom_param_translation=param_translations,
custom_param_description=param_descriptions,
custom_howto=howto,
)
)
extra_info: list[ExtraInfoDict] | Callable[[], list[ExtraInfoDict]] = getattr(
module, "EXTRA_INFO", []
)
if callable(extra_info):
extra_info = extra_info()
for e in extra_info:
sources.append(
SourceInfo(
filename=filename,
module=file,
title=e.get("title", title),
url=e.get("url", url),
country=e.get("country", country),
params=params,
extra_info_default_params=e.get("default_params", {}),
custom_howto=howto,
)
)
return module, sources
def browse_ics_yaml() -> list[SourceInfo]: def browse_ics_yaml() -> list[SourceInfo]:
"""Browse all .yaml files which are descriptions for the ICS source""" """Browse all .yaml files which are descriptions for the ICS source"""
doc_dir = Path(__file__).resolve().parents[0] / "doc" doc_dir = Path(__file__).resolve().parents[0] / "doc"
@@ -338,13 +383,12 @@ def browse_ics_yaml() -> list[SourceInfo]:
country = data.get("country", f.stem.split("_")[-1]) country = data.get("country", f.stem.split("_")[-1])
# extract country code # extract country code
sources.append( sources.append(
SourceInfo( IcsSourceInfo(
filename=f"/doc/ics/{filename.name}", filename=f"/doc/ics/{filename.name}",
module=None,
title=data["title"], title=data["title"],
url=data["url"], url=data["url"],
country=country, country=country,
params=[], limit_params=[],
extra_info_default_params=data.get("default_params", {}), extra_info_default_params=data.get("default_params", {}),
custom_howto=howto, custom_howto=howto,
) )
@@ -352,13 +396,12 @@ def browse_ics_yaml() -> list[SourceInfo]:
if "extra_info" in data: if "extra_info" in data:
for e in data["extra_info"]: for e in data["extra_info"]:
sources.append( sources.append(
SourceInfo( IcsSourceInfo(
filename=f"/doc/ics/{filename.name}", filename=f"/doc/ics/{filename.name}",
module=None,
title=e.get("title", data["title"]), title=e.get("title", data["title"]),
url=e.get("url", data["url"]), url=e.get("url", data["url"]),
country=e.get("country", country), country=e.get("country", country),
params=[], limit_params=[],
extra_info_default_params=data.get("default_params", {}), extra_info_default_params=data.get("default_params", {}),
custom_howto=howto, custom_howto=howto,
) )
@@ -484,6 +527,8 @@ def get_custom_translations(
) -> Tuple[ ) -> Tuple[
dict[str, dict[str, dict[str, str | None]]], dict[str, dict[str, dict[str, str | None]]],
dict[str, dict[str, dict[str, str | None]]], dict[str, dict[str, dict[str, str | None]]],
dict[str, dict[str, str | None]],
dict[str, str],
]: ]:
"""gets all parameters and its custom translations for all languages """gets all parameters and its custom translations for all languages
@@ -491,38 +536,46 @@ def get_custom_translations(
countries (dict[str, list[SourceInfo]]): countries (dict[str, list[SourceInfo]]):
Returns: Returns:
Tuple[dict[str, dict[str, dict[str, str | None]]], dict[str, dict[str, dict[str, str | None]]]]: Translation dict[MODULE][PARAM][LANG][TRANSLATION|None], Description: dict[MODULE][PARAM][LANG][DESCRIPTION|None] Tuple[dict[str, dict[str, dict[str, str | None]]], dict[str, dict[str, dict[str, str | None]]]]: Param translation dict[MODULE][PARAM][LANG][TRANSLATION|None], Param Description: dict[MODULE][PARAM][LANG][DESCRIPTION|None], Source howto: dict[MODULE][LANG][HOWTO|None], Source doc url: dict[MODULE][URL]
""" """
param_translations: dict[str, dict[str, dict[str, str | None]]] = {} param_translations: dict[str, dict[str, dict[str, str | None]]] = {}
param_descriptions: dict[str, dict[str, dict[str, str | None]]] = {} param_descriptions: dict[str, dict[str, dict[str, str | None]]] = {}
source_howto: dict[str, dict[str, str | None]] = {}
source_doc_url: dict[str, str] = {}
for country in sorted(countries): for country in sorted(countries):
for e in sorted( for e in sorted(
countries[country], countries[country],
key=lambda e: (e.title.lower(), beautify_url(e.url), e.filename), key=lambda e: (e.title.lower(), beautify_url(e.url), e.filename),
): ):
module = e.module
if e.module is None: # ICS source if e.module is None: # ICS source
continue module = "ics_" + e.filename.split("/")[-1].removesuffix(".md")
if not e.module in param_translations:
param_translations[e.module] = {} source_doc_url[module] = DOC_URL_BASE + e.filename
if not e.module in param_descriptions:
param_descriptions[e.module] = {} if not module in param_translations:
param_translations[module] = {}
if not module in param_descriptions:
param_descriptions[module] = {}
for param in e.params: for param in e.params:
if param not in param_translations[e.module]: if param not in param_translations[module]:
param_translations[e.module][param] = {} param_translations[module][param] = {}
if param not in param_descriptions[e.module]: if param not in param_descriptions[module]:
param_descriptions[e.module][param] = {} param_descriptions[module][param] = {}
for lang, translations in e.custom_param_translation.items(): for lang, translations in e.custom_param_translation.items():
for param, translation in translations.items(): for param, translation in translations.items():
param_translations[e.module][param][lang] = translation param_translations[module][param][lang] = translation
for lang, descriptions in e.custom_param_description.items(): for lang, descriptions in e.custom_param_description.items():
for param, description in descriptions.items(): for param, description in descriptions.items():
param_descriptions[e.module][param][lang] = description param_descriptions[module][param][lang] = description
return param_translations, param_descriptions source_howto[module] = e.custom_howto
return param_translations, param_descriptions, source_howto, source_doc_url
def update_json( def update_json(
@@ -531,9 +584,13 @@ def update_json(
countries = countries.copy() countries = countries.copy()
countries["Generic"] = generics countries["Generic"] = generics
update_sources_json(countries) update_sources_json(countries)
(
param_translations,
param_descriptions,
source_howto,
source_doc_url,
) = get_custom_translations(countries)
param_translations, param_descriptions = get_custom_translations(countries)
for lang in LANGUAGES: for lang in LANGUAGES:
tranlation_file = ( tranlation_file = (
f"custom_components/waste_collection_schedule/translations/{lang}.json" f"custom_components/waste_collection_schedule/translations/{lang}.json"
@@ -621,6 +678,23 @@ def update_json(
"data_description" "data_description"
][param] = languages[lang] ][param] = languages[lang]
module_howto = source_howto.get(module, {})
howto_str = (
module_howto.get(lang, None) or module_howto.get("en", None) or ""
)
howto_str = format_howto(howto_str)
translations["config"]["step"][f"args_{module}"][
"description"
] = translations["config"]["step"]["args"]["description"].format(
howto=howto_str, docs_url=source_doc_url.get(module, "")
)
translations["config"]["step"][f"reconfigure_{module}"][
"description"
] = translations["config"]["step"]["reconfigure"]["description"].format(
howto=howto_str, docs_url=source_doc_url.get(module, "")
)
with open( with open(
tranlation_file, tranlation_file,
"w", "w",
@@ -629,6 +703,46 @@ def update_json(
json.dump(translations, f, indent=2, ensure_ascii=False) json.dump(translations, f, indent=2, ensure_ascii=False)
def format_howto(howto: str) -> str:
if not howto:
return ""
howto = "\n\n" + howto + "\n\n"
new_howto = ""
code_opened = False
code_buffer = ""
do_not_code = False
for c in howto:
match c:
case "`":
code_buffer += c if not do_not_code else "'"
code_opened = not code_opened
if not code_opened:
do_not_code = False
case "{":
code_buffer += "{"
do_not_code = True
case "}":
code_buffer += "}"
do_not_code = True
case "<":
if code_opened:
code_buffer += c
pass
case ">":
if code_opened:
code_buffer += c
case _:
code_buffer += c
if not code_opened:
new_howto += code_buffer
code_buffer = ""
if code_opened and do_not_code:
code_buffer = code_buffer.replace("`", "'")
return new_howto
def update_readme_md(countries: dict[str, list[SourceInfo]]): def update_readme_md(countries: dict[str, list[SourceInfo]]):
# generate country list # generate country list
str = "" str = ""