2021-05-26 18:37:26 +02:00
#! /usr/bin/env python3
2021-05-10 17:23:40 +02:00
2024-08-11 22:55:33 +02:00
import sys
2024-03-22 01:53:19 +01:00
import os
2021-04-28 20:11:42 +02:00
import argparse
2021-04-23 20:01:40 +02:00
import json
2021-05-26 18:39:42 +02:00
from pathlib import Path
2024-03-07 00:39:38 +01:00
from copy import deepcopy
2021-04-23 20:01:40 +02:00
2024-03-08 04:16:36 +01:00
from typing import Dict , Optional , List , Tuple
2024-03-07 01:00:40 +01:00
2024-02-07 14:49:55 +01:00
import toml
2021-04-28 20:11:42 +02:00
from jinja2 import Environment , FileSystemLoader
2024-03-20 05:37:19 +01:00
from babel . support import Translations
2024-03-22 01:53:19 +01:00
from babel . messages . pofile import PoFileParser
2024-03-22 06:21:07 +01:00
from langcodes import Language
2021-04-28 20:11:42 +02:00
2024-08-11 22:55:33 +02:00
# add apps/tools to sys.path
sys . path . insert ( 0 , str ( Path ( __file__ ) . parent . parent ) )
from appslib import get_apps_repo
2024-03-13 14:48:52 +01:00
README_GEN_DIR = Path ( __file__ ) . resolve ( ) . parent
2024-06-06 16:26:22 +02:00
TRANSLATIONS_DIR = README_GEN_DIR / " translations "
2024-02-07 14:49:55 +01:00
2024-06-08 13:22:16 +02:00
2024-03-07 01:00:40 +01:00
def value_for_lang ( values : Dict , lang : str ) :
2024-03-07 00:33:28 +01:00
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 ]
2021-04-23 20:01:40 +02:00
2021-05-28 17:17:16 +02:00
2024-08-13 09:36:36 +02:00
def generate_READMEs ( app_path : Path , apps_repo_path : Path ) :
2021-05-26 18:43:26 +02:00
if not app_path . exists ( ) :
2021-04-28 20:14:48 +02:00
raise Exception ( " App path provided doesn ' t exists ?! " )
2021-04-23 20:01:40 +02:00
2024-03-13 14:48:52 +01:00
if ( app_path / " manifest.json " ) . exists ( ) :
2023-01-11 09:58:45 +01:00
manifest = json . load ( open ( app_path / " manifest.json " ) )
else :
manifest = toml . load ( open ( app_path / " manifest.toml " ) )
2021-05-21 20:12:00 +02:00
upstream = manifest . get ( " upstream " , { } )
2024-08-13 09:36:36 +02:00
catalog = toml . load ( ( apps_repo_path / " apps.toml " ) . open ( encoding = " utf-8 " ) )
2024-03-07 00:50:01 +01:00
from_catalog = catalog . get ( manifest [ " id " ] , { } )
2022-02-23 07:36:19 +01:00
2024-03-07 00:50:01 +01:00
antifeatures_list = toml . load (
2024-08-13 09:36:36 +02:00
( apps_repo_path / " antifeatures.toml " ) . open ( encoding = " utf-8 " )
2024-03-07 00:50:01 +01:00
)
2022-02-23 08:18:05 +01:00
2021-05-26 18:43:26 +02:00
if not upstream and not ( app_path / " doc " / " DISCLAIMER.md " ) . exists ( ) :
2021-05-26 18:46:29 +02:00
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. "
)
2021-05-21 20:12:00 +02:00
return
2024-03-22 01:53:19 +01:00
poparser = PoFileParser ( { } )
2024-06-06 16:26:22 +02:00
poparser . parse ( ( README_GEN_DIR / " messages.pot " ) . open ( encoding = " utf-8 " ) )
2024-03-22 01:53:19 +01:00
# 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 ] = [ ]
2024-06-06 16:26:22 +02:00
for available_translations in os . listdir ( TRANSLATIONS_DIR ) :
translations = Translations . load ( TRANSLATIONS_DIR , available_translations )
2024-03-22 01:53:19 +01:00
is_fully_translated = True
for sentence in poparser . catalog :
# ignore empty strings
if not sentence . strip ( ) :
continue
if sentence not in translations . _catalog :
2024-03-24 05:45:21 +01:00
print ( translations . _catalog )
print ( f " The sentence: { repr ( sentence ) } is not in the target catalog " )
2024-03-22 01:53:19 +01:00
is_fully_translated = False
break
if not translations . _catalog [ sentence ] :
2024-03-24 05:45:21 +01:00
print ( f " The sentence: ' { repr ( sentence ) } ' is not translated " )
2024-03-22 01:53:19 +01:00
is_fully_translated = False
break
if is_fully_translated :
fully_translated_langs . append ( available_translations )
2024-03-22 07:06:21 +01:00
else :
print (
" WARNING: skip generating translated README for "
f " { Language ( available_translations ) . language_name ( ) } ( { available_translations } ) "
" because it is not fully translated yet. "
)
2024-03-22 01:53:19 +01:00
fully_translated_langs . sort ( )
2024-03-24 05:45:38 +01:00
print (
f " Available languages for translation: { ' , ' . join ( fully_translated_langs ) if fully_translated_langs else [ ] } "
)
2024-03-22 01:53:19 +01:00
2024-03-13 14:48:52 +01:00
screenshots : List [ str ] = [ ]
2021-04-23 20:01:40 +02:00
2024-03-13 14:48:52 +01:00
screenshots_dir = app_path / " doc " / " screenshots "
if screenshots_dir . exists ( ) :
2024-06-23 14:24:40 +02:00
for entry in sorted ( screenshots_dir . iterdir ( ) ) :
2024-03-13 14:48:52 +01:00
# 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 ) ) )
2024-03-13 03:22:39 +01:00
2024-03-22 03:01:22 +01:00
def generate_single_README ( lang_suffix : str , lang : str ) :
2024-03-24 02:14:43 +01:00
env = Environment (
loader = FileSystemLoader ( README_GEN_DIR / " templates " ) ,
extensions = [ " jinja2.ext.i18n " ] ,
)
2024-06-06 16:26:22 +02:00
translations = Translations . load ( TRANSLATIONS_DIR , [ lang ] )
2024-03-24 02:14:43 +01:00
env . install_gettext_translations ( translations )
2024-03-22 03:01:22 +01:00
template = env . get_template ( " README.md.j2 " )
2021-04-23 20:01:40 +02:00
2021-08-22 16:57:07 +02:00
if ( app_path / " doc " / f " DESCRIPTION { lang_suffix } .md " ) . exists ( ) :
2024-03-07 00:50:01 +01:00
description = (
app_path / " doc " / f " DESCRIPTION { lang_suffix } .md "
) . read_text ( )
2021-08-22 16:57:07 +02:00
# 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
2024-03-07 01:00:40 +01:00
disclaimer : Optional [ str ]
2021-05-26 18:43:26 +02:00
if ( app_path / " doc " / f " DISCLAIMER { lang_suffix } .md " ) . exists ( ) :
disclaimer = ( app_path / " doc " / f " DISCLAIMER { lang_suffix } .md " ) . read_text ( )
2021-04-28 20:14:48 +02:00
# Fallback to english if maintainer too lazy to translate the disclaimer idk
2021-05-26 18:43:26 +02:00
elif ( app_path / " doc " / " DISCLAIMER.md " ) . exists ( ) :
disclaimer = ( app_path / " doc " / " DISCLAIMER.md " ) . read_text ( )
2021-04-28 20:14:48 +02:00
else :
disclaimer = None
2021-04-23 20:01:40 +02:00
2022-02-23 08:18:05 +01:00
# TODO: Add url to the documentation... and actually create that documentation :D
2024-03-07 00:50:01 +01:00
antifeatures = {
a : deepcopy ( antifeatures_list [ a ] )
for a in from_catalog . get ( " antifeatures " , [ ] )
}
2022-08-05 16:39:38 +02:00
for k , v in antifeatures . items ( ) :
2024-03-07 00:50:01 +01:00
antifeatures [ k ] [ " title " ] = value_for_lang ( v [ " title " ] , lang )
2022-08-05 16:39:38 +02:00
if manifest . get ( " antifeatures " , { } ) . get ( k , None ) :
2024-03-07 00:50:01 +01:00
antifeatures [ k ] [ " description " ] = value_for_lang (
manifest . get ( " antifeatures " , { } ) . get ( k , None ) , lang
)
2022-05-30 13:59:54 +02:00
else :
2024-03-07 00:50:01 +01:00
antifeatures [ k ] [ " description " ] = value_for_lang (
antifeatures [ k ] [ " description " ] , lang
)
2022-02-23 08:18:05 +01:00
2024-03-07 01:00:40 +01:00
out : str = template . render (
2021-05-26 18:46:29 +02:00
lang = lang ,
upstream = upstream ,
2021-08-22 16:57:07 +02:00
description = description ,
2021-05-26 18:46:29 +02:00
screenshots = screenshots ,
disclaimer = disclaimer ,
2022-08-05 16:39:38 +02:00
antifeatures = antifeatures ,
2021-05-26 18:46:29 +02:00
manifest = manifest ,
)
2021-05-26 18:43:26 +02:00
( app_path / f " README { lang_suffix } .md " ) . write_text ( out )
2021-04-28 20:14:48 +02:00
2024-03-22 03:01:22 +01:00
generate_single_README ( " " , " en " )
for lang in fully_translated_langs :
generate_single_README ( " _ " + lang , lang )
2024-03-22 06:21:07 +01:00
links_to_other_READMEs = [ ]
for language in fully_translated_langs :
2024-06-06 16:26:22 +02:00
translations = Translations . load ( TRANSLATIONS_DIR , [ language ] )
2024-03-22 06:21:07 +01:00
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 } ,
)
)
2024-03-24 02:14:43 +01:00
env = Environment ( loader = FileSystemLoader ( README_GEN_DIR / " templates " ) )
2024-03-22 06:21:07 +01:00
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 )
2021-04-28 20:14:48 +02:00
2024-08-13 09:36:36 +02:00
def main ( ) :
2024-09-12 14:48:36 +02:00
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 "
)
2024-08-11 22:55:33 +02:00
get_apps_repo . add_args ( parser )
2021-04-28 20:14:48 +02:00
args = parser . parse_args ( )
2024-08-11 22:55:33 +02:00
apps_path = get_apps_repo . from_args ( args )
2024-08-13 09:36:36 +02:00
generate_READMEs ( args . app_path , apps_path )
if __name__ == " __main__ " :
main ( )