aboutsummaryrefslogtreecommitdiff
"""The Quality-Control Web Application entry point"""
import os
import sys
import logging
from pathlib import Path

from flask import Flask, request
from flask_session import Session

from uploader.oauth2.client import user_logged_in, authserver_authorise_uri

from . import session
from .base_routes import base
from .files.views import files
from .species import speciesbp
from .oauth2.views import oauth2
from .expression_data import exprdatabp
from .errors import register_error_handlers

def override_settings_with_envvars(
        app: Flask, ignore: tuple[str, ...]=tuple()) -> None:
    """Override settings in `app` with those in ENVVARS"""
    for setting in (key for key in app.config if key not in ignore):
        app.config[setting] = os.environ.get(setting) or app.config[setting]


def __log_gunicorn__(app: Flask) -> Flask:
    """Set up logging for the WSGI environment with GUnicorn"""
    logger = logging.getLogger("gunicorn.error")
    app.logger.handlers = logger.handlers
    app.logger.setLevel(logger.level)
    return app


def __log_dev__(app: Flask) -> Flask:
    """Set up logging for the development environment."""
    stderr_handler = logging.StreamHandler(stream=sys.stderr)
    app.logger.addHandler(stderr_handler)

    root_logger = logging.getLogger()
    root_logger.addHandler(stderr_handler)
    root_logger.setLevel(app.config["LOG_LEVEL"])

    return app


def setup_logging(app: Flask) -> Flask:
    """Set up logging for the application."""
    software, *_version_and_comments = os.environ.get(
        "SERVER_SOFTWARE", "").split('/')
    return __log_gunicorn__(app) if bool(software) else __log_dev__(app)


def create_app():
    """The application factory"""
    app = Flask(__name__)
    app.config.from_pyfile(
        Path(__file__).parent.joinpath("default_settings.py"))
    if "UPLOADER_CONF" in os.environ:
        app.config.from_envvar("UPLOADER_CONF") # Override defaults with instance path

    override_settings_with_envvars(app, ignore=tuple())

    secretsfile = app.config.get("UPLOADER_SECRETS", "").strip()
    if bool(secretsfile):
        secretsfile = Path(secretsfile).absolute()
        app.config["UPLOADER_SECRETS"] = secretsfile
        if secretsfile.exists():
            # Silently ignore secrets if the file does not exist.
            app.config.from_pyfile(secretsfile)

    setup_logging(app)

    # setup jinja2 symbols
    app.add_template_global(lambda : request.url, name="request_url")
    app.add_template_global(authserver_authorise_uri)
    app.add_template_global(lambda: app.config["GN2_SERVER_URL"],
                            name="gn2server_uri")
    app.add_template_global(user_logged_in)
    app.add_template_global(lambda : session.user_details()["email"], name="user_email")

    Session(app)

    # setup blueprints
    app.register_blueprint(base, url_prefix="/")
    app.register_blueprint(files, url_prefix="/files")
    app.register_blueprint(oauth2, url_prefix="/oauth2")
    app.register_blueprint(speciesbp, url_prefix="/species")

    register_error_handlers(app)
    return app