"""
thekraf.flaskapp
================
Flask web backend app
"""
from datetime import datetime
import os
import flask
from flask_bootstrap import Bootstrap
# from flask_mail import Mail
from flask_misaka import Misaka
from flask_principal import Principal
from flask_security import Security, SQLAlchemyUserDatastore
from flask_sqlalchemy import SQLAlchemy
# from flask_sslify import SSLify
from werkzeug.datastructures import ImmutableDict
from thekraf import Multilogr
import thekraf.config as cfg
import thekraf.db.models as mdls
from thekraf.flaskapp.baseview import ViewBase
from thekraf.flaskapp.forms import LoginUserForm, RegisterUserForm
logr = Multilogr.get_logr(cfg.FlaskAppConfig.LOGGER_NAME)
[docs]class FlaskWithHamlish(flask.Flask):
"""Adds support for Hamlish-Jinja to Flask"""
jinja_options = ImmutableDict(
extensions=flask.Flask.jinja_options['extensions'] + [
'hamlish_jinja.HamlishExtension'
]
)
[docs]class JSONEncoderWrapper(object):
"""Add support for more types to the json encoder
if `default` and `for_json` are used, then if an object has a
`for_json` attribute, it will be used. Otherwise, it will fallback on
`default`
flask uses a `TaggedJSONSerializer` on sessions which will be try
converting various types before giving `default` a try.
"""
def __new__(cls, *args, **kwargs):
kwargs.update(dict(
default=cls.default_override,
for_json=True,
))
return flask.json.JSONEncoder(*args, **kwargs)
@staticmethod
[docs] def default_override(o):
# Create a dict of object instance variables and properties
try:
# Include instance variables
d = dict(o.__dict__)
# Include properties
d.update({k: getattr(o, k)
for k, v in o.__class__.__dict__.items()
if isinstance(v, property)})
except (AttributeError, TypeError):
pass
else:
return d
if isinstance(o, set):
return list(o)
if isinstance(o, datetime):
return str(o)
# If this method couldn't convert the object
raise TypeError(repr(o) + " is not JSON serializable")
[docs]def create_app(config_obj=cfg.FlaskAppConfig):
"""Flask app factory
Returns:
flask.app: An app configured for various extensions
"""
app = FlaskWithHamlish(__name__)
app.config.from_object(config_obj)
app.json_encoder = JSONEncoderWrapper
# Override the flask logger
# Note that logging the web server underneath has to be done separately
app._logger = logr
# Config hamlish
app.jinja_env.hamlish_mode = 'debug'
app.jinja_env.hamlish_file_extensions = ('.hamlish',)
app.jinja_env.hamlish_enable_div_shortcut = True
# Create a random secret key used for session security, unless already set
if not os.path.exists(cfg.SECRET_KEY_PATH):
with open(cfg.SECRET_KEY_PATH, 'wb') as fp:
fp.write(os.urandom(24))
with open(cfg.SECRET_KEY_PATH, 'rb') as fp:
app.secret_key = fp.read()
# SSLify(app)
Bootstrap(app)
# mail = Mail(app)
sqla_db = SQLAlchemy(app)
user_ds = SQLAlchemyUserDatastore(sqla_db, mdls.User, mdls.Role)
security = Security(app, user_ds, register_form=RegisterUserForm,
login_form=LoginUserForm)
security.register_context_processor(ViewBase.ctx_processor)
security.login_context_processor(ViewBase.ctx_processor)
Principal(app)
Misaka(app)
return app