diff --git a/flaskapp/app.py b/flaskapp/app.py index 31a7105..fb0e604 100644 --- a/flaskapp/app.py +++ b/flaskapp/app.py @@ -2,11 +2,21 @@ import os import secrets +from typing import Optional +from typing import Type, List from flask import Flask +from flaskext.mysql import MySQL # type: ignore -from . import routes +from .routes import Routes +from . import pages from . import errors +from . import db + +routes: List[Type[Routes]] = [ + pages.RoutePages, + errors.RouteErrors, +] def create_app() -> Flask: @@ -26,14 +36,45 @@ def create_app() -> Flask: secrets.token_hex(32), ) + # Setup MySQL database + app.config.update({ + 'MYSQL_DATABASE_HOST': os.getenv('DB_HOST', 'localhost'), + 'MYSQL_DATABASE_PORT': parseint(os.getenv('DB_PORT'), 3306), + 'MYSQL_DATABASE_USER': os.getenv('DB_USER', 'root'), + 'MYSQL_DATABASE_PASSWORD': os.getenv('DB_PASSWORD', ''), + 'MYSQL_DATABASE_DB': os.getenv('DB_DATABASE', '${REPO_NAME_SNAKE}'), + }) + sql = MySQL(app) + db.set_db(sql) + # Create instance/ directory try: os.makedirs(app.instance_path) except OSError: pass - # Add routes - routes.add_routes(app) - errors.add_routes(app) + # Add all routes + for r in routes: + r(app).add_routes() return app + + +def parseint( + value: Optional[str], + default: int) -> Optional[int]: + """Parses integer from the string, + returns default value on error + + Args: + value (Optional[str]): String or None + default (int): Default value + + Returns: + Integer or None + """ + + try: + return int(value or default) + except ValueError: + return default diff --git a/flaskapp/db.py b/flaskapp/db.py new file mode 100644 index 0000000..296f28f --- /dev/null +++ b/flaskapp/db.py @@ -0,0 +1,25 @@ +"""Simple wrapper for getting/setting +db key in global Flask object called `g`""" + +from flask import g +from flaskext.mysql import MySQL # type: ignore + + +def set_db(db: MySQL) -> None: + """Add the database to `g` object + + Args: + db (MySQL): MySQL database + """ + + g.db = db + + +def get_db() -> MySQL: + """Get the database from `g` object + + Returns: + MySQL database + """ + + return g.db diff --git a/flaskapp/errors.py b/flaskapp/errors.py index d0382d6..77dc5f1 100644 --- a/flaskapp/errors.py +++ b/flaskapp/errors.py @@ -5,44 +5,43 @@ from pathlib import Path from flask import Flask from flask import render_template +from . import routes + # Add other HTTP error codes here CODES = [404, 500] -def add_routes(app: Flask) -> None: - """Add all error handlers +class RouteErrors(routes.Routes): + """Error handlers wrapped in Routes interface""" - Args: - app (Flask): Flask application - """ + def __init__(self, app: Flask) -> None: - tmpl_dir = app.template_folder - if tmpl_dir is None: - return + super().__init__(app) - tmpl = Path(__file__).parent / tmpl_dir + tmpl_dir = self.app.template_folder + if tmpl_dir is None: + return - for code in CODES: - add_handler(app, tmpl, code) + self.tmpl = Path(__file__).parent / tmpl_dir + def add_routes(self) -> None: + """Add all error handlers""" -def add_handler( - app: Flask, - tmpl: Path, - code: int) -> None: - """Add Flask app error handler. - Only for internal use + for code in CODES: + self.add_handler(code) - Args: - app (Flask): Flask application - file (str): Template filename - code (int): Error code - """ + def add_handler(self, code: int) -> None: + """Add Flask app error handler. + Only for internal use - file = f'{code}.html' + Args: + code (int): HTTP error code + """ - if (tmpl / file).exists(): + file = f'{code}.html' - @app.errorhandler(code) - def handler(_e): - return render_template(file), code + if (self.tmpl / file).exists(): + + @self.app.errorhandler(code) + def handler(_e): + return render_template(file), code diff --git a/flaskapp/pages.py b/flaskapp/pages.py new file mode 100644 index 0000000..e74b0da --- /dev/null +++ b/flaskapp/pages.py @@ -0,0 +1,15 @@ +"""Main Flask app routes""" + +from flask import render_template + +from . import routes + + +class RoutePages(routes.Routes): + """Main Flask app routes""" + + def add_routes(self) -> None: + + @self.app.route('/') + def index(): + return render_template('index.html') diff --git a/flaskapp/routes.py b/flaskapp/routes.py index 785b5dd..5aab94a 100644 --- a/flaskapp/routes.py +++ b/flaskapp/routes.py @@ -1,16 +1,22 @@ -"""Main Flask app routes""" +"""Interface for all routes classes""" + +import abc from flask import Flask -from flask import render_template -def add_routes(app: Flask) -> None: - """Add main routes +class Routes(abc.ABC): + """Flask app routes interface""" - Args: - app (Flask): Flask application - """ + def __init__(self, app: Flask) -> None: + """Flask app routes - @app.route('/') - def index(): - return render_template('index.html') + Args: + app (Flask): Flask application + """ + + self.app = app + + @abc.abstractmethod + def add_routes(self) -> None: + """Add routes""" diff --git a/pylintrc b/pylintrc index 93cccce..258b920 100644 --- a/pylintrc +++ b/pylintrc @@ -64,7 +64,7 @@ max-parents=7 max-public-methods=20 max-returns=6 max-statements=50 -min-public-methods=2 +min-public-methods=1 [STRING] check-quote-consistency=no @@ -144,6 +144,7 @@ good-names=i, j, k, f, + db, ex, Run, _ diff --git a/requirements.txt b/requirements.txt index 72e3f57..676759f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ flask==2.2.2 +flask-mysql==1.5.2 gunicorn==20.1.0