aboutsummaryrefslogtreecommitdiff
path: root/uploader/__init__.py
blob: 8b49ad511ed51a6d760f14fa2a9be3e39bd4b8e7 (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
"""The Quality-Control Web Application entry point"""
import os
import sys
import logging
from pathlib import Path
from typing import Optional

from flask import Flask, request

from cachelib import FileSystemCache

from gn_libs import jobs as gnlibs_jobs

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 .publications import pubbp
from .oauth2.views import oauth2
from .expression_data import exprdatabp
from .errors import register_error_handlers
from .background_jobs import background_jobs_bp

logging.basicConfig(
    format=("%(asctime)s%(filename)s:%(lineno)s%(levelname)s "
            "(%(thread)d:%(threadName)s): %(message)s")
)

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 setup_modules_logging(app_logger):
    """Setup module-level loggers to the same log-level as the application."""
    loglevel = logging.getLevelName(app_logger.getEffectiveLevel())

    def __setup__(logger_name):
        _logger = logging.getLogger(logger_name)
        _logger.setLevel(loglevel)

    __setup__("uploader.publications.models")
    __setup__("uploader.publications.datatables")


def create_app(config: Optional[dict] = None):
    """The application factory.

    config: dict
      Useful to override settings in the settings files and environment
      especially in environments such as testing."""
    if config is None:
        config = {}

    app = Flask(__name__)

    ### BEGIN: Application configuration
    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)
    app.config.update(config) # Override everything with passed in config
    ### END: Application configuration

    app.config["SESSION_CACHELIB"] = FileSystemCache(
        cache_dir=Path(app.config["SESSION_FILESYSTEM_CACHE_PATH"]).absolute(),
        threshold=int(app.config["SESSION_FILESYSTEM_CACHE_THRESHOLD"]),
        default_timeout=int(app.config["SESSION_FILESYSTEM_CACHE_TIMEOUT"]))

    setup_logging(app)
    setup_modules_logging(app.logger)

    # 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")
    app.register_blueprint(pubbp, url_prefix="/publications")
    app.register_blueprint(background_jobs_bp, url_prefix="/background-jobs/")

    register_error_handlers(app)
    gnlibs_jobs.init_app(app)
    return app