1
0
Fork 0
ynh-apps_tools/readme_generator/make_readme.py
github-actions[bot] ebb1b75bc3 🎨 Format Python code with Black (#2582)
Co-authored-by: Salamandar <6552989+Salamandar@users.noreply.github.com>
2024-09-12 14:48:36 +02:00

212 lines
7.3 KiB
Python
Executable file

#! /usr/bin/env python3
import sys
import os
import argparse
import json
from pathlib import Path
from copy import deepcopy
from typing import Dict, Optional, List, Tuple
import toml
from jinja2 import Environment, FileSystemLoader
from babel.support import Translations
from babel.messages.pofile import PoFileParser
from langcodes import Language
# add apps/tools to sys.path
sys.path.insert(0, str(Path(__file__).parent.parent))
from appslib import get_apps_repo
README_GEN_DIR = Path(__file__).resolve().parent
TRANSLATIONS_DIR = README_GEN_DIR / "translations"
def value_for_lang(values: Dict, lang: str):
if not isinstance(values, dict):
return values
if lang in values:
return values[lang]
elif "en" in values:
return values["en"]
else:
return list(values.values())[0]
def generate_READMEs(app_path: Path, apps_repo_path: Path):
if not app_path.exists():
raise Exception("App path provided doesn't exists ?!")
if (app_path / "manifest.json").exists():
manifest = json.load(open(app_path / "manifest.json"))
else:
manifest = toml.load(open(app_path / "manifest.toml"))
upstream = manifest.get("upstream", {})
catalog = toml.load((apps_repo_path / "apps.toml").open(encoding="utf-8"))
from_catalog = catalog.get(manifest["id"], {})
antifeatures_list = toml.load(
(apps_repo_path / "antifeatures.toml").open(encoding="utf-8")
)
if not upstream and not (app_path / "doc" / "DISCLAIMER.md").exists():
print(
"There's no 'upstream' key in the manifest, and doc/DISCLAIMER.md doesn't exists - therefore assuming that we shall not auto-update the README.md for this app yet."
)
return
poparser = PoFileParser({})
poparser.parse((README_GEN_DIR / "messages.pot").open(encoding="utf-8"))
# we only want to translate a README if all strings are translatables so we
# do this loop to detect which language provides a full translation
fully_translated_langs: List[str] = []
for available_translations in os.listdir(TRANSLATIONS_DIR):
translations = Translations.load(TRANSLATIONS_DIR, available_translations)
is_fully_translated = True
for sentence in poparser.catalog:
# ignore empty strings
if not sentence.strip():
continue
if sentence not in translations._catalog:
print(translations._catalog)
print(f"The sentence: {repr(sentence)} is not in the target catalog")
is_fully_translated = False
break
if not translations._catalog[sentence]:
print(f"The sentence: '{repr(sentence)}' is not translated")
is_fully_translated = False
break
if is_fully_translated:
fully_translated_langs.append(available_translations)
else:
print(
"WARNING: skip generating translated README for "
f"{Language(available_translations).language_name()} ({available_translations}) "
"because it is not fully translated yet."
)
fully_translated_langs.sort()
print(
f"Available languages for translation: {', '.join(fully_translated_langs) if fully_translated_langs else []}"
)
screenshots: List[str] = []
screenshots_dir = app_path / "doc" / "screenshots"
if screenshots_dir.exists():
for entry in sorted(screenshots_dir.iterdir()):
# only pick files (no folder) on the root of 'screenshots'
if not entry.is_file():
continue
# ignore '.gitkeep' or any file whose name begins with a dot
if entry.name.startswith("."):
continue
screenshots.append(str(entry.relative_to(app_path)))
def generate_single_README(lang_suffix: str, lang: str):
env = Environment(
loader=FileSystemLoader(README_GEN_DIR / "templates"),
extensions=["jinja2.ext.i18n"],
)
translations = Translations.load(TRANSLATIONS_DIR, [lang])
env.install_gettext_translations(translations)
template = env.get_template("README.md.j2")
if (app_path / "doc" / f"DESCRIPTION{lang_suffix}.md").exists():
description = (
app_path / "doc" / f"DESCRIPTION{lang_suffix}.md"
).read_text()
# Fallback to english if maintainer too lazy to translate the description
elif (app_path / "doc" / "DESCRIPTION.md").exists():
description = (app_path / "doc" / "DESCRIPTION.md").read_text()
else:
description = None
disclaimer: Optional[str]
if (app_path / "doc" / f"DISCLAIMER{lang_suffix}.md").exists():
disclaimer = (app_path / "doc" / f"DISCLAIMER{lang_suffix}.md").read_text()
# Fallback to english if maintainer too lazy to translate the disclaimer idk
elif (app_path / "doc" / "DISCLAIMER.md").exists():
disclaimer = (app_path / "doc" / "DISCLAIMER.md").read_text()
else:
disclaimer = None
# TODO: Add url to the documentation... and actually create that documentation :D
antifeatures = {
a: deepcopy(antifeatures_list[a])
for a in from_catalog.get("antifeatures", [])
}
for k, v in antifeatures.items():
antifeatures[k]["title"] = value_for_lang(v["title"], lang)
if manifest.get("antifeatures", {}).get(k, None):
antifeatures[k]["description"] = value_for_lang(
manifest.get("antifeatures", {}).get(k, None), lang
)
else:
antifeatures[k]["description"] = value_for_lang(
antifeatures[k]["description"], lang
)
out: str = template.render(
lang=lang,
upstream=upstream,
description=description,
screenshots=screenshots,
disclaimer=disclaimer,
antifeatures=antifeatures,
manifest=manifest,
)
(app_path / f"README{lang_suffix}.md").write_text(out)
generate_single_README("", "en")
for lang in fully_translated_langs:
generate_single_README("_" + lang, lang)
links_to_other_READMEs = []
for language in fully_translated_langs:
translations = Translations.load(TRANSLATIONS_DIR, [language])
language_name_in_itself = Language.get(language).autonym()
links_to_other_READMEs.append(
(
f"README_{language}.md",
translations.gettext("Read the README in %(language)s")
% {"language": language_name_in_itself},
)
)
env = Environment(loader=FileSystemLoader(README_GEN_DIR / "templates"))
out: str = env.get_template("ALL_README.md.j2").render(
links_to_other_READMEs=links_to_other_READMEs
)
(app_path / "ALL_README.md").write_text(out)
def main():
parser = argparse.ArgumentParser(
description="Automatically (re)generate README for apps"
)
parser.add_argument(
"app_path", type=Path, help="Path to the app to generate/update READMEs for"
)
get_apps_repo.add_args(parser)
args = parser.parse_args()
apps_path = get_apps_repo.from_args(args)
generate_READMEs(args.app_path, apps_path)
if __name__ == "__main__":
main()