To learn more about design patterns, particularly the Model-View-Presenter (MVP) pattern, I am currently creating a CLI weight tracker application in Python. I would like you to review the code to determine if it adheres to the MVP pattern.
The code or business logic itself is not finished yet. The application can currently only display the version and initialize a database. However, for the purpose of reviewing the MVP implementation I believe the code is easier to follow without a ton of fucntions.
Additionally, the model.py
module currently holds all the business logic. In a real-world application I would separate that into distinct modules. But in this self-made MVP tutorial, I have chosen to focus primarily on the three conceptual aspects of the pattern.
Thanks in advance!
Module __init__.py
__app_name__ = "Weight tracker"
__version__ = "0.1.0"
DEFAULT_DATABASE_PATH = "./data/storage.json"
DEFAULT_CONFIG_PATH = "./data/.config.ini"
Module __main__.py
from weight_tracker import view, presenter
def main() -> None:
presenter.run()
if __name__ == "__main__":
main()
Module view.py
import typer
from typing import Optional
from weight_tracker import presenter, DEFAULT_DATABASE_PATH
app = typer.Typer()
class TextColor:
DEFAULT = typer.colors.BLUE
ERROR = typer.colors.RED
SUCCESS = typer.colors.GREEN
def mainloop() -> None:
app()
def exit_console() -> None:
raise typer.Exit()
def write_to_console(text: str, color=TextColor.DEFAULT) -> None:
typer.secho(
text,
fg=color,
)
@app.command(name="init")
def initialize_database(
db_path: Optional[str] = typer.Option(
DEFAULT_DATABASE_PATH,
"--db_path",
"-db",
help="Create a new database.",
callback=presenter.init_app,
)
) -> None:
return
@app.callback()
def global_options(
version: Optional[bool] = typer.Option(
None,
"--version",
"-v",
help="Show installed application version.",
callback=presenter.version_callback,
is_eager=True,
)
) -> None:
return
Module presenter.py
from weight_tracker import view
from weight_tracker import model
def init_app(db_path: str) -> None:
if db_path:
initialize_database(db_path)
initialize_configuration_file(db_path)
def initialize_database(db_path: str) -> None:
try:
model.create_empty_database(db_path)
view.write_to_console(f"Created empty storage at {db_path}", view.TextColor.SUCCESS)
except OSError:
view.write_to_console("Failed to create storage.json. file", view.TextColor.ERROR)
def initialize_configuration_file(db_path: str) -> None:
try:
model.write_db_path_to_config(db_path)
view.write_to_console(f"Updated database path to {db_path}", view.TextColor.SUCCESS)
except OSError:
view.write_to_console("Failed to create config.ini file.", view.TextColor.ERROR)
def version_callback(called: bool) -> None:
if called:
view.write_to_console(model.get_version())
view.exit_console()
def run() -> None:
view.mainloop()
Module model.py
from weight_tracker import __app_name__, __version__
from pathlib import Path
EMPTY_LIST = "[]"
# Application model
def get_version() -> str:
return f"{__app_name__} v{__version__}"
# Database model
def create_empty_database(db_path: str) -> None:
Path(db_path).write_text(EMPTY_LIST)
# Configuration model
def write_db_path_to_config(db_path: str) -> None:
... # todo