from flask import current_app, Flask, g, request, session from pathlib import Path from typing import Iterable, Union from . import api, constants, jinjafilters, models, utils, views def create_app(config_paths:Iterable[Union[str, Path]]=None, **config_overrides) -> Flask: """ App factory. Returns Flask application object. Application configuration is defined by files in :config_paths:. :config_paths: Iterable of absolute paths to config files. These will be loaded sequentially. Settings in the first file may be overridden by subsequent files. :config_overrides: Kwargs become configuration overrides that take precedence over anything in :config_paths:. These should typically only be used in tests. """ config_paths = config_paths or [] app = Flask(__name__, root_path=Path(__file__).parent.parent) for i, absolute_path in enumerate(config_paths): print('{} config from {}'.format('Loading' if i == 0 else 'Extending', absolute_path)) app.config.from_pyfile(absolute_path) for key, val in config_overrides.items(): app.config[key] = val # Jinja globals # Each given name here becomes available as a variable anywhere within templates app.jinja_env.globals['models'] = models app.jinja_env.globals['constants'] = constants app.jinja_env.globals['datums'] = constants # Initialize database with app.app_context(): models.db.init_app(app) # Register jinja filters app.register_blueprint(jinjafilters.bp) # Register view blueprints app.register_blueprint(views.public.bp) app.register_blueprint(views.admin.bp, url_prefix='/admin') app.register_blueprint(views.setup.bp, url_prefix='/setup') # Register API blueprints app.register_blueprint(api.public.bp, url_prefix='/api') app.register_blueprint(api.admin.bp, url_prefix='/api/admin') app.register_blueprint(api.auth.bp, url_prefix='/api/auth') # Before/after request registration app.before_request(_before_request) # Register error handlers #TODO Better error handling #app.register_error_handler(401, views.errorhandling.error_40x) #app.register_error_handler(403, views.errorhandling.error_40x) #app.register_error_handler(404, views.errorhandling.error_40x) #app.register_error_handler(405, views.errorhandling.error_40x) #app.register_error_handler(500, views.errorhandling.error_500) #app.register_error_handler(Exception, views.errorhandling.unhandled_exception) # Register core function routes #TODO Serve static resources #app.add_url_rule('/', 'serve_static_resource', serve_static_resource) return app def _before_request(): g.user = None g.timezone = current_app.config['TIMEZONE'] g.root_url_full = f"{current_app.config['SCHEME']}://{current_app.config['ROOT_URL']}" if any(session.get(key, None) for key in ('logged_in', 'user_id', 'user_type')): if all(session.get(key, None) for key in ('logged_in', 'user_id', 'user_type')): g.user = models.User.query.filter_by( active=True, user_id=session['user_id'], user_type=session['user_type'], ).first() if not g.user: session.pop('logged_in') session.pop('user_id') session.pop('user_type') g.user = None