From 0d558009959db44ec052fd6a363f81255315ca25 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Chauvel Date: Fri, 5 Sep 2025 00:05:52 -0500 Subject: [PATCH] put all strings into a separate module (strings.py), fixes #12 --- src/edit_python_pe/main.py | 80 +++++++++++++++-------------- src/edit_python_pe/strings.py | 82 ++++++++++++++++++++++++++++++ src/edit_python_pe/utils.py | 94 ++++++++++++++++------------------- 3 files changed, 168 insertions(+), 88 deletions(-) create mode 100644 src/edit_python_pe/strings.py diff --git a/src/edit_python_pe/main.py b/src/edit_python_pe/main.py index 6aaa7d3..69398ec 100755 --- a/src/edit_python_pe/main.py +++ b/src/edit_python_pe/main.py @@ -8,6 +8,14 @@ from textual.widgets import (Button, Input, ListItem, ListView, Select, Static, TextArea) +from .strings import (BUTTON_ADD, BUTTON_ADD_ALIAS, BUTTON_ADD_SOCIAL, + BUTTON_BACK, BUTTON_DELETE, BUTTON_QUIT, BUTTON_SAVE, + FORM_HEADER, LIST_TITLE, MESSAGE_EXIT, MESSAGE_QUIT, + PLACEHOLDER_ALIAS, PLACEHOLDER_CITY, PLACEHOLDER_EMAIL, + PLACEHOLDER_HOMEPAGE, PLACEHOLDER_NAME, + PLACEHOLDER_SOCIAL_URL, SECTION_ALIASES, SECTION_AVAIL, + SECTION_CONTRIB, SECTION_PYTHON, SECTION_SOCIAL, + SECTION_WHO) from .utils import (build_md_content, create_pr, fork_repo, get_repo, load_file_into_form) @@ -38,12 +46,12 @@ def compose(self) -> ComposeResult: def on_mount(self) -> None: # 1) Build the list portion - self.list_title = Static("Archivos en 'blog/members':") + self.list_title = Static(LIST_TITLE) self.list_view = ListView() - self.quit_list_button = Button("Salir", id="quit_list") + self.quit_list_button = Button(BUTTON_QUIT, id="quit_list") self.list_container.mount(self.list_title) - self.add_list_button = Button("Añadir", id="add_list") + self.add_list_button = Button(BUTTON_ADD, id="add_list") self.list_container.mount(self.list_view) self.list_container.mount(self.add_list_button) self.list_container.mount(self.quit_list_button) @@ -56,11 +64,11 @@ def on_mount(self) -> None: self.list_view.append(ListItem(Static(basename))) # 2) Build the form portion, hidden at first - self.form_header = Static("Formulario de Miembro", classes="header") - self.name_input = Input(placeholder="Nombre") - self.email_input = Input(placeholder="Correo electrónico") - self.city_input = Input(placeholder="Ciudad") - self.homepage_input = Input(placeholder="Página personal") + self.form_header = Static(FORM_HEADER, classes="header") + self.name_input = Input(placeholder=PLACEHOLDER_NAME) + self.email_input = Input(placeholder=PLACEHOLDER_EMAIL) + self.city_input = Input(placeholder=PLACEHOLDER_CITY) + self.homepage_input = Input(placeholder=PLACEHOLDER_HOMEPAGE) self.who_area = TextArea() self.python_area = TextArea() @@ -69,48 +77,42 @@ def on_mount(self) -> None: self.social_container = Vertical() self.alias_container = Vertical() - self.add_social_button = Button("Agregar Red Social", id="add_social") - self.add_alias_button = Button("Agregar Alias", id="add_alias") + self.add_social_button = Button(BUTTON_ADD_SOCIAL, id="add_social") + self.add_alias_button = Button(BUTTON_ADD_ALIAS, id="add_alias") - self.save_button = Button("Guardar", id="save") - self.back_button = Button("Atrás", id="back") - self.quit_button = Button("Salir", id="quit") + self.save_button = Button(BUTTON_SAVE, id="save") + self.back_button = Button(BUTTON_BACK, id="back") + self.quit_button = Button(BUTTON_QUIT, id="quit") # 3) Mount them in the form container self.form_container.mount(self.form_header) self.form_container.mount(self.name_input) self.form_container.mount(self.email_input) - self.form_container.mount( - Static("Redes Sociales", classes="subheader") - ) + self.form_container.mount(Static(SECTION_SOCIAL, classes="subheader")) self.form_container.mount(self.social_container) self.form_container.mount(self.add_social_button) - self.form_container.mount(Static("Aliases", classes="subheader")) + self.form_container.mount(Static(SECTION_ALIASES, classes="subheader")) self.form_container.mount(self.alias_container) self.form_container.mount(self.add_alias_button) self.form_container.mount(self.city_input) self.form_container.mount(self.homepage_input) - self.form_container.mount( - Static("¿Quién eres y a qué te dedicas?", classes="subheader") - ) + self.form_container.mount(Static(SECTION_WHO, classes="subheader")) self.form_container.mount(self.who_area) - self.form_container.mount( - Static("¿Cómo programas en Python?", classes="subheader") - ) + self.form_container.mount(Static(SECTION_PYTHON, classes="subheader")) self.form_container.mount(self.python_area) self.form_container.mount( Static( - "¿Tienes algún aporte a la comunidad de Python?", + SECTION_CONTRIB, classes="subheader", ) ) self.form_container.mount(self.contributions_area) self.form_container.mount( Static( - "¿Estás disponible para hacer mentoring, consultorías, charlas?", + SECTION_AVAIL, classes="subheader", ) ) @@ -177,7 +179,7 @@ def on_list_view_selected(self, event: ListView.Selected) -> None: def on_button_pressed(self, event: Button.Pressed) -> None: bid = event.button.id if bid == "quit_list": - self.exit("¡Hasta la próxima!") + self.exit(message=MESSAGE_EXIT) elif bid == "add_social": self.add_social_entry() elif bid == "add_alias": @@ -193,7 +195,7 @@ def on_button_pressed(self, event: Button.Pressed) -> None: self.clear_form() self.show_list() elif bid == "quit": - self.exit("Saliendo de la aplicación.") + self.exit(MESSAGE_QUIT) elif bid and bid.startswith("delete_social_"): index = int(bid.replace("delete_social_", "")) self.remove_social_entry(index) @@ -205,13 +207,13 @@ def add_social_entry(self) -> None: class SocialEntry(Horizontal): DEFAULT_CSS = """ SocialEntry Select { - width: 25%; + width: 25%; } SocialEntry Input { - width: 50%; + width: 50%; } SocialEntry Button { - width: 25%; + width: 25%; } """ @@ -229,10 +231,12 @@ def __init__(se, index): ("X", "x"), ("YouTube", "youtube"), ], - prompt="Red Social", + prompt="Social Network", + ) + se.url_input = Input(placeholder=PLACEHOLDER_SOCIAL_URL) + se.delete_btn = Button( + BUTTON_DELETE, id=f"delete_social_{index}" ) - se.url_input = Input(placeholder="URL de la red social") - se.delete_btn = Button("Eliminar", id=f"delete_social_{index}") def compose(se) -> ComposeResult: yield se.select @@ -258,18 +262,20 @@ def add_alias_entry(self) -> None: class AliasEntry(Horizontal): DEFAULT_CSS = """ AliasEntry Input { - width: 75%; + width: 75%; } AliasEntry Button { - width: 25%; + width: 25%; } """ def __init__(se, index): super().__init__() se.index = index - se.alias_input = Input(placeholder="Alias") - se.delete_btn = Button("Eliminar", id=f"delete_alias_{index}") + se.alias_input = Input(placeholder=PLACEHOLDER_ALIAS) + se.delete_btn = Button( + BUTTON_DELETE, id=f"delete_alias_{index}" + ) def compose(se) -> ComposeResult: yield se.alias_input diff --git a/src/edit_python_pe/strings.py b/src/edit_python_pe/strings.py new file mode 100644 index 0000000..f647083 --- /dev/null +++ b/src/edit_python_pe/strings.py @@ -0,0 +1,82 @@ +# Field, control, and message labels for edit_python_pe + +# List and form titles +LIST_TITLE = "Files in 'blog/members':" +FORM_HEADER = "Member Form" + +# Button labels +BUTTON_QUIT = "Quit" +BUTTON_ADD = "Add" +BUTTON_SAVE = "Save" +BUTTON_BACK = "Back" +BUTTON_ADD_SOCIAL = "Add Social Network" +BUTTON_ADD_ALIAS = "Add Alias" +BUTTON_DELETE = "Delete" + +# Input placeholders +PLACEHOLDER_NAME = "Name" +PLACEHOLDER_EMAIL = "Email" +PLACEHOLDER_CITY = "City" +PLACEHOLDER_HOMEPAGE = "Homepage" +PLACEHOLDER_SOCIAL_URL = "Social network URL" +PLACEHOLDER_ALIAS = "Alias" + +# Section headers +SECTION_SOCIAL = "Social Networks" +SECTION_ALIASES = "Aliases" +SECTION_WHO = "Who are you and what do you do?" +SECTION_PYTHON = "How do you program in Python?" +SECTION_CONTRIB = "Do you have any contributions to the Python community?" +SECTION_AVAIL = "Are you available for mentoring, consulting, talks?" + +# Messages +MESSAGE_PROMPT_FOR_GITHUB_TOKEN = ( + "Please enter your GitHub personal access token: " +) +MESSAGE_EXIT = "See you next time!" +MESSAGE_QUIT = "Exiting the application." +MESSAGE_FILE_READ_ERROR = "Error reading file {filename}: {error}" +MESSAGE_UNAUTHORIZED = "Unauthorized access. Please check your access token." +MESSAGE_REPO_NOT_FOUND = ( + "Repository not found. Please check your access token." +) +MESSAGE_FILE_EDITED_PR = ( + "File {name_file} edited, commit and changes sent to existing PR." +) +MESSAGE_FILE_SAVED_PR = "File {name_file} saved, commit and PR ready." +MESSAGE_CREATE_ENTRY = ( + "Creating a new entry to `blog/members` for {name} (alias: {first_alias})." +) +MESSAGE_CHANGE_ENTRY = ( + "Changing an entry to `blog/members` for {name} (alias: {first_alias})." +) +MESSAGE_LOAD_FILE_ERROR = "Error reading file {filename}: {error}" + +# build_md_content markdown dictionary (English keys, Spanish values for now) +MD_CONTENT = { + "yaml_start": "---", + "yaml_blogpost": "blogpost: true", + "yaml_date": "date: {date}", + "yaml_author": "author: {author}", + "yaml_location": "location: {city}", + "yaml_category": "category: members", + "yaml_language": "language: Español", + "yaml_image": "image: 1", + "yaml_excerpt": "excerpt: 1", + "yaml_end": "---", + "header_name": "# {name}", + "gravatar_block": '```{{gravatar}} {email}\n---\nwidth: 200\nclass: "member-gravatar"\n---\n```', + "social_block_start": "```{{raw}} html", + "social_ul_start": '", + "social_block_end": "```", + "aliases": ":Aliases: {aliases}", + "city": ":Ciudad: {city}", + "homepage": ":Homepage: {homepage}", + "section_about": "## Sobre mí", + "section_who": "### ¿Quién eres y a qué te dedicas?", + "section_python": "### ¿Cómo programas en Python?", + "section_contrib": "### ¿Tienes algún aporte a la comunidad de Python?", + "section_avail": "### ¿Estás disponible para hacer mentoring, consultorías, charlas?", +} diff --git a/src/edit_python_pe/utils.py b/src/edit_python_pe/utils.py index e61a21d..c153a20 100644 --- a/src/edit_python_pe/utils.py +++ b/src/edit_python_pe/utils.py @@ -15,6 +15,11 @@ if TYPE_CHECKING: from .main import MemberApp +from .strings import (MD_CONTENT, MESSAGE_FILE_EDITED_PR, + MESSAGE_FILE_SAVED_PR, MESSAGE_LOAD_FILE_ERROR, + MESSAGE_PROMPT_FOR_GITHUB_TOKEN, MESSAGE_REPO_NOT_FOUND, + MESSAGE_UNAUTHORIZED) + def _compute_file_name(aliases: list[str], name: str, email: str) -> str: # compute name_file @@ -118,20 +123,16 @@ def _get_alias(aliases: list[str], name: str) -> str: def get_repo() -> tuple[str, Repository]: - token = getpass.getpass( - "Por favor ingrese su access token personal de GitHub: " - ) + token = getpass.getpass(MESSAGE_PROMPT_FOR_GITHUB_TOKEN) g = Github(token) try: return token, g.get_repo("pythonpe/python.pe") except BadCredentialsException: - print("Acceso no autorizado. Por favor, verifique su token de acceso.") + print(MESSAGE_UNAUTHORIZED) exit(1) except GithubException: - print( - "Repositorio no encontrado. Por favor, verifique su token de acceso." - ) + print(MESSAGE_REPO_NOT_FOUND) exit(1) @@ -203,25 +204,23 @@ def create_pr( if pr_found: # Push to the PR branch (simulate, as actual branch logic may differ) remote.push([repo.head.name], callbacks=callbacks) - return f"Archivo {name_file} editado, commit y cambios enviados al PR existente." + return MESSAGE_FILE_EDITED_PR.format(name_file=name_file) else: - # Otherwise, create a new PR original_repo.create_pull( title=pr_title, body=pr_body, head=head_branch, base=base_branch, ) - return f"Archivo {name_file} guardado, commit y PR listo." + return MESSAGE_FILE_SAVED_PR.format(name_file=name_file) else: - # Otherwise, create a new PR original_repo.create_pull( title=pr_title, body=pr_body, head=head_branch, base=base_branch, ) - return f"Archivo {name_file} guardado, commit y PR listo." + return MESSAGE_FILE_SAVED_PR.format(name_file=name_file) def load_file_into_form(app: "MemberApp", filename: str) -> None: @@ -231,7 +230,9 @@ def load_file_into_form(app: "MemberApp", filename: str) -> None: try: content = _read_file(path_md) except Exception as e: - app.exit(f"Error al leer el archivo {filename}: {e}") + app.exit( + message=MESSAGE_LOAD_FILE_ERROR.format(filename=filename, error=e) + ) return app.clear_form() @@ -333,81 +334,72 @@ def build_md_content( availability: str, ) -> str: md_lines = [ - "---", - "blogpost: true", - f"date: {date.today().strftime("%d %b, %Y")}", - f"author: {_get_alias(aliases, name)}", - f"location: {city}", - "category: members", - "language: Español", - "image: 1", - "excerpt: 1", - "---", + MD_CONTENT["yaml_start"], + MD_CONTENT["yaml_blogpost"], + MD_CONTENT["yaml_date"].format( + date=date.today().strftime("%d %b, %Y") + ), + MD_CONTENT["yaml_author"].format(author=_get_alias(aliases, name)), + MD_CONTENT["yaml_location"].format(city=city), + MD_CONTENT["yaml_category"], + MD_CONTENT["yaml_language"], + MD_CONTENT["yaml_image"], + MD_CONTENT["yaml_excerpt"], + MD_CONTENT["yaml_end"], "", - f"# {name}", + MD_CONTENT["header_name"].format(name=name), "", - f"```{{gravatar}} {email}", - "---", - "width: 200", - 'class: "member-gravatar"', - "---", - "```", + MD_CONTENT["gravatar_block"].format(email=email), "", ] if socials: - md_lines.append("```{raw} html") - md_lines.append('") - md_lines.append("```") + md_lines.append(MD_CONTENT["social_ul_end"]) + md_lines.append(MD_CONTENT["social_block_end"]) md_lines.append("") if aliases: - md_lines.append(f":Aliases: {', '.join(aliases)}") + md_lines.append( + MD_CONTENT["aliases"].format(aliases=", ".join(aliases)) + ) md_lines.append("") if city: - md_lines.append(f":Ciudad: {city}") + md_lines.append(MD_CONTENT["city"].format(city=city)) md_lines.append("") if homepage: - md_lines.append(f":Homepage: {homepage}") + md_lines.append(MD_CONTENT["homepage"].format(homepage=homepage)) md_lines.append("") - md_lines.append("## Sobre mí") + md_lines.append(MD_CONTENT["section_about"]) md_lines.append("") if who: - md_lines.append("### ¿Quién eres y a qué te dedicas?") + md_lines.append(MD_CONTENT["section_who"]) md_lines.append("") md_lines.append(who) md_lines.append("") if python_: - md_lines.append("### ¿Cómo programas en Python?") + md_lines.append(MD_CONTENT["section_python"]) md_lines.append("") md_lines.append(python_) md_lines.append("") if contributions: - md_lines.append("### ¿Tienes algún aporte a la comunidad de Python?") + md_lines.append(MD_CONTENT["section_contrib"]) md_lines.append("") md_lines.append(contributions) md_lines.append("") if availability: - md_lines.append( - "### ¿Estás disponible para hacer mentoring, consultorías, charlas?" - ) + md_lines.append(MD_CONTENT["section_avail"]) md_lines.append("") md_lines.append(availability) md_lines.append("")