Skip to content

Commit

Permalink
Merge pull request #36 from davewalker5/flask-app-factory
Browse files Browse the repository at this point in the history
Flask app factory
  • Loading branch information
davewalker5 authored Feb 19, 2022
2 parents 9c8d641 + 050915e commit 59f2906
Show file tree
Hide file tree
Showing 17 changed files with 143 additions and 146 deletions.
12 changes: 6 additions & 6 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
FROM python:3.10-slim-bullseye AS runtime

COPY naturerecorderpy-1.0.22.0 /opt/naturerecorderpy-1.0.22.0
COPY naturerecorderpy-1.0.23.0 /opt/naturerecorderpy-1.0.23.0

WORKDIR /opt/naturerecorderpy-1.0.22.0
WORKDIR /opt/naturerecorderpy-1.0.23.0

RUN apt-get update -y
RUN pip install -r requirements.txt
RUN pip install nature_recorder-1.0.22-py3-none-any.whl
RUN pip install nature_recorder-1.0.23-py3-none-any.whl

ENV NATURE_RECORDER_DATA_FOLDER=/var/opt/naturerecorderpy-1.0.22.0
ENV NATURE_RECORDER_DB=/var/opt/naturerecorderpy-1.0.22.0/naturerecorder.db
ENV NATURE_RECORDER_DATA_FOLDER=/var/opt/naturerecorderpy-1.0.23.0
ENV NATURE_RECORDER_DB=/var/opt/naturerecorderpy-1.0.23.0/naturerecorder.db

ENTRYPOINT [ "python" ]
CMD [ "-m", "naturerec_web" ]
CMD [ "-m", "naturerec_web", "production" ]
56 changes: 5 additions & 51 deletions docker/random_secret.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import os
import shutil
import sys


Expand All @@ -12,67 +11,22 @@ def get_project_path():
return os.path.dirname(os.path.dirname(__file__))


def get_docker_path():
"""
Get the path to the project's Docker build folder
:return: The path to the docker folder
"""
return os.path.join(get_project_path(), "docker")


def get_web_app_source_path():
"""
Return the path to the folder containing the source for the web app
:return: The path to the web app source folder
"""
return os.path.join(get_project_path(), "src", "naturerec_web")


def replace_secret(file_content):
"""
Replace the secret key placeholder in the specified text with a new, random, secret key
:param file_content: File content as a string
:return: Updated file content
"""
secret_key = os.urandom(32).hex()
return file_content.replace("some secret key", secret_key)


def set_web_app_secret():
"""
Replace the secret key in the web application Python source file
"""
# Get the source and backup file
file = os.path.join(get_web_app_source_path(), "naturerecorder.py")
backup = os.path.join(get_docker_path(), "naturerecorder.py")

# Check the backup file doesn't already exist
if os.path.exists(backup):
raise FileExistsError(f"{backup} already exists")

# Backup the original file and make sure it's backed up OK
shutil.copy(file, backup, follow_symlinks=False)
if not os.path.exists(backup):
raise FileNotFoundError(f"{backup} not found")

# Read the content of the original file
with open(file, mode="rt", encoding="utf-8") as f:
file_content = f.read()

# Replace the secret key with a new random key and rewrite the file
updated_content = replace_secret(file_content)
file = os.path.join(get_project_path(), "data", ".env")
with open(file, mode="wt", encoding="utf-8") as f:
f.write(updated_content)
f.writelines([
f"SECRET_KEY={os.urandom(32).hex()}\n"
])


if __name__ == "__main__":
try:
set_web_app_secret()
sys.exit(0)

except (FileExistsError, FileNotFoundError) as e:
except BaseException as e:
print(f"Error: {e}")
sys.exit(1)
5 changes: 5 additions & 0 deletions docs/source/naturerec_web/home_blueprint.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
home_blueprint.py
=================

.. automodule:: naturerec_web.home.home_blueprint
:members:
1 change: 1 addition & 0 deletions docs/source/naturerec_web/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ The naturerec_web Package
:maxdepth: 2
:caption: Contents:

home_blueprint
locations_blueprint
categories_blueprint
species_blueprint
Expand Down
4 changes: 2 additions & 2 deletions features/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from selenium.webdriver.common.by import By
from selenium.common.exceptions import ElementNotInteractableException, NoSuchElementException
from flask_app_runner import FlaskAppRunner
from naturerec_web import app as nature_recorder_app
from naturerec_web import create_app
from naturerec_model.model.database import Engine
from naturerec_model.model.utils import get_project_path

Expand All @@ -23,7 +23,7 @@ def start_flask_server(context):
:param context:
"""
context.flask_runner = FlaskAppRunner("127.0.0.1", 5000, nature_recorder_app)
context.flask_runner = FlaskAppRunner("127.0.0.1", 5000, create_app("development"))
context.flask_runner.start()
yield context.flask_runner

Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Pygments==2.10.0
pyOpenSSL==21.0.0
pyparsing==3.0.6
python-dateutil==2.8.2
python-dotenv==0.19.2
pytz==2021.3
pyzmq==22.3.0
requests==2.26.0
Expand Down
3 changes: 1 addition & 2 deletions run_web.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ source "$PROJECT_ROOT/venv/bin/activate"
export PYTHONPATH="$PROJECT_ROOT/src"
export NATURE_RECORDER_DB="$PROJECT_ROOT/data/naturerecorder.db"
export FLASK_ENV=development
export FLASK_APP=naturerecorder.py
cd src/naturerec_web && flask run
python -m naturerec_web
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def find_package_files(directory, remove_root):

setuptools.setup(
name="nature_recorder",
version="1.0.22",
version="1.0.23",
description="Wildlife sightings database",
packages=setuptools.find_packages("src"),
include_package_data=True,
Expand Down
3 changes: 3 additions & 0 deletions sql/set_zero_number_to_null.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
UPDATE Sightings
SET Number = NULL
WHERE Number = 0;
66 changes: 62 additions & 4 deletions src/naturerec_web/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,63 @@
from .naturerecorder import app
import os
from flask import Flask
from flask_login import LoginManager
from .home import home_bp
from .sightings import sightings_bp
from .export import export_bp
from .locations import locations_bp
from .categories import categories_bp
from .species import species_bp
from .status import status_bp
from .species_ratings import species_ratings_bp
from .life_list import life_list_bp
from .jobs import jobs_bp
from .reports import reports_bp
from .auth import auth_bp
from naturerec_model.logic import get_user


def create_app(environment="production"):
"""
Flask Application Factory
:return: An instance of the Flask application
"""
app = Flask("Nature Recorder",
static_folder=os.path.join(os.path.dirname(__file__), "static"),
template_folder=os.path.join(os.path.dirname(__file__), "templates"))

config_object = f"naturerec_web.config.{'ProductionConfig' if environment == 'production' else 'DevelopmentConfig'}"
app.config.from_object(config_object)

# Register the blueprints
app.secret_key = os.environ["SECRET_KEY"]
app.register_blueprint(home_bp, url_prefix="")
app.register_blueprint(sightings_bp, url_prefix='/sightings')
app.register_blueprint(export_bp, url_prefix='/export')
app.register_blueprint(locations_bp, url_prefix='/locations')
app.register_blueprint(categories_bp, url_prefix='/categories')
app.register_blueprint(species_bp, url_prefix='/species')
app.register_blueprint(status_bp, url_prefix='/status')
app.register_blueprint(species_ratings_bp, url_prefix='/species_ratings')
app.register_blueprint(life_list_bp, url_prefix='/life_list')
app.register_blueprint(jobs_bp, url_prefix='/jobs')
app.register_blueprint(reports_bp, url_prefix='/reports')
app.register_blueprint(auth_bp, url_prefix='/auth')

# Create the flask-login user manager
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app)

@login_manager.user_loader
def load_user(user_id):
"""
Method that returns a user given their ID
:param user_id: ID of the user to retrieve
:return: Instance of the User class for the specified user
"""
return get_user(int(user_id))

return app

__all__ = [
"app"
]
16 changes: 7 additions & 9 deletions src/naturerec_web/__main__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import os
from naturerec_web.naturerecorder import app
import sys
from naturerec_web import create_app

try:
if os.environ["FLASK_ENV"] == "development":
app.run(debug=True, use_reloader=True)
else:
app.run(host="0.0.0.0")
except KeyError:
app.run(host="0.0.0.0")
environment = sys.argv[1] if len(sys.argv) > 1 else "development"
if environment == "development":
create_app(environment).run(debug=True, use_reloader=True)
else:
create_app(environment).run(host="0.0.0.0")
25 changes: 25 additions & 0 deletions src/naturerec_web/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""
Flask configuration
"""

import os
from dotenv import load_dotenv

basedir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
if "NATURE_RECORDER_DATA_FOLDER" in os.environ:
env_file = os.path.join(os.environ["NATURE_RECORDER_DATA_FOLDER"], ".env")
else:
env_file = os.path.join(basedir, "data", ".env")
load_dotenv(env_file)


class BaseConfig:
SECRET_KEY = os.environ.get('SECRET_KEY')


class ProductionConfig(BaseConfig):
TESTING = False


class DevelopmentConfig(BaseConfig):
TESTING = True
5 changes: 5 additions & 0 deletions src/naturerec_web/home/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from naturerec_web.home.home_blueprint import home_bp

__all__ = [
"home_bp"
]
16 changes: 16 additions & 0 deletions src/naturerec_web/home/home_blueprint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""
The home blueprint supplies view functions and templates for the site home page
"""
from flask import Blueprint, redirect

home_bp = Blueprint("home", __name__, template_folder='templates')


@home_bp.route("/")
def home():
"""
Serve the home page for the site
:return: Rendered home page template
"""
return redirect("/sightings/edit")
68 changes: 0 additions & 68 deletions src/naturerec_web/naturerecorder.py

This file was deleted.

2 changes: 1 addition & 1 deletion src/naturerec_web/templates/menu.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="{{ url_for('home') }}">Nature Recorder</a>
<a class="navbar-brand" href="{{ url_for('home.home') }}">Nature Recorder</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
Expand Down
4 changes: 2 additions & 2 deletions tests/locust_tests/locustfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
from naturerec_model.model import create_database, get_data_path, Sighting
from naturerec_model.logic import list_locations, list_categories, list_species
from naturerec_model.data_exchange import SightingsImportHelper, StatusImportHelper
from naturerec_web import app as nature_recorder_app
from naturerec_web import create_app


flask_runner = FlaskAppRunner("127.0.0.1", 5000, nature_recorder_app)
flask_runner = FlaskAppRunner("127.0.0.1", 5000, create_app())


@events.test_start.add_listener
Expand Down

0 comments on commit 59f2906

Please sign in to comment.