Example #1
0
def _setup_app_core(app):
    """
    Setup application core for given MyDojo application. The application core
    contains following features:

        * Error handlers
        * Default routes
        * Additional custom Jinja template variables
        * Additional custom Jinja template macros

    :param mydojo.base.MyDojoApp app: MyDojo application to be modified.
    :return: Modified MyDojo application
    :rtype: mydojo.base.MyDojoApp
    """
    @app.errorhandler(400)
    def eh_badrequest(err):  # pylint: disable=locally-disabled,unused-variable
        """Flask error handler to be called to service HTTP 400 error."""
        return mydojo.errors.error_handler_switch(400, err)

    @app.errorhandler(403)
    def eh_forbidden(err):  # pylint: disable=locally-disabled,unused-variable
        """Flask error handler to be called to service HTTP 403 error."""
        return mydojo.errors.error_handler_switch(403, err)

    @app.errorhandler(404)
    def eh_page_not_found(err):  # pylint: disable=locally-disabled,unused-variable
        """Flask error handler to be called to service HTTP 404 error."""
        return mydojo.errors.error_handler_switch(404, err)

    @app.errorhandler(405)
    def eh_method_not_allowed(err):  # pylint: disable=locally-disabled,unused-variable
        """Flask error handler to be called to service HTTP 405 error."""
        return mydojo.errors.error_handler_switch(405, err)

    @app.errorhandler(410)
    def eh_gone(err):  # pylint: disable=locally-disabled,unused-variable
        """Flask error handler to be called to service HTTP 410 error."""
        return mydojo.errors.error_handler_switch(410, err)

    @app.errorhandler(500)
    def eh_internal_server_error(err):  # pylint: disable=locally-disabled,unused-variable
        """Flask error handler to be called to service HTTP 500 error."""
        flask.current_app.logger.critical(
            "Internal Server Error:\n%s",
            ''.join(traceback.TracebackException(*sys.exc_info()).format()))
        return mydojo.errors.error_handler_switch(500, err)

    @app.before_request
    def before_request():  # pylint: disable=locally-disabled,unused-variable
        """
        Use Flask`s :py:func:`flask.Flask.before_request` hook for performing
        various usefull tasks before each request.
        """
        flask.g.requeststart = datetime.datetime.utcnow()

    @app.context_processor
    def jinja_inject_variables():  # pylint: disable=locally-disabled,unused-variable
        """
        Inject additional variables into Jinja2 global template namespace.
        """
        return dict(
            mydojo_appname='MyDojo',
            mydojo_appslogan=flask_babel.lazy_gettext(
                'My personal Internet dojo'),
            mydojo_version=mydojo.__version__,
            mydojo_current_app=app,
            mydojo_current_view=app.get_endpoint_class(flask.request.endpoint,
                                                       True),
            mydojo_navbar_main=app.navbar_main,
            mydojo_logger=app.logger,
            mydojo_cdt_utc=datetime.datetime.utcnow(),
            mydojo_cdt_local=datetime.datetime.now(),
        )

    @app.context_processor
    def jinja2_inject_functions():  # pylint: disable=locally-disabled,unused-variable,too-many-locals
        """
        Register additional helpers into Jinja2 global template namespace.
        """
        def get_modules_dict():
            """
            Return dictionary of all registered application pluggable modules.
            """
            return flask.current_app.blueprints

        def get_endpoints_dict():
            """
            Return dictionary of all registered application view endpoints.
            """
            return flask.current_app.view_functions

        def get_endpoint_class(endpoint):
            """
            Return class reference to given view endpoint.

            :param str endpoint: Name of the view endpoint.
            """
            return app.get_endpoint_class(endpoint)

        def check_endpoint_exists(endpoint):
            """
            Check, that given application view endpoint exists and is registered within
            the application.

            :param str endpoint: Name of the view endpoint.
            :return: ``True`` in case endpoint exists, ``False`` otherwise.
            :rtype: bool
            """
            return app.has_endpoint(endpoint)

        def get_icon(icon_name, default_icon='missing-icon'):
            """
            Get HTML icon markup for given icon. The icon will be looked up in
            the :py:const:`mydojo.const.FA_ICONS` lookup table.

            :param str icon_name: Name of the icon.
            :param str default_icon: Name of the default icon.
            :return: Icon including HTML markup.
            :rtype: flask.Markup
            """
            return flask.Markup(
                mydojo.const.FA_ICONS.get(
                    icon_name, mydojo.const.FA_ICONS.get(default_icon)))

        def get_country_flag(country):
            """
            Get URL to static country flag file.

            :param str country: Name of the icon.
            :return: Country including HTML markup.
            :rtype: flask.Markup
            """
            if not mydojo.const.CRE_COUNTRY_CODE.match(country):
                return get_icon('flag')

            return flask.Markup('<img src="{}">'.format(
                flask.url_for(
                    'design.static',
                    filename='images/country-flags/flags-iso/shiny/16/{}.png'.
                    format(country.upper()))))

        def get_timedelta(tstamp):
            """
            Get timedelta from current UTC time and given datetime object.

            :param datetime.datetime: Datetime of the lower timedelta boundary.
            :return: Timedelta object.
            :rtype: datetime.timedelta
            """
            return datetime.datetime.utcnow() - tstamp

        def get_datetime_utc():
            """
            Get current UTC datetime.

            :return: Curent UTC datetime.
            :rtype: datetime.datetime
            """
            return datetime.datetime.utcnow()

        def get_datetime_local():
            """
            Get current local timestamp.

            :return: Curent local timestamp.
            :rtype: datetime.datetime
            """
            return datetime.datetime.now()

        def check_file_exists(filename):
            """
            Check, that given file exists in the filesystem.

            :param str filename: Name of the file to check.
            :return: Existence flag as ``True`` or ``False``.
            :rtype: bool
            """
            return os.path.isfile(filename)

        def include_raw(filename):
            """
            Include given file in raw form directly into the generated content.
            This may be usefull for example for including JavaScript files
            directly into the HTML page.
            """
            return jinja2.Markup(
                app.jinja_loader.get_source(app.jinja_env, filename)[0])

        return dict(get_modules_dict=get_modules_dict,
                    get_endpoints_dict=get_endpoints_dict,
                    get_endpoint_class=get_endpoint_class,
                    check_endpoint_exists=check_endpoint_exists,
                    get_icon=get_icon,
                    get_country_flag=get_country_flag,
                    get_timedelta=get_timedelta,
                    get_datetime_utc=get_datetime_utc,
                    get_datetime_local=get_datetime_local,
                    check_file_exists=check_file_exists,
                    include_raw=include_raw)

    class MyDojoJSONEncoder(flask.json.JSONEncoder):
        """
        Custom JSON encoder for converting anything into JSON strings.
        """
        def default(self, obj):  # pylint: disable=locally-disabled,method-hidden,arguments-differ
            try:
                if isinstance(obj, datetime.datetime):
                    return obj.isoformat() + 'Z'
            except:  # pylint: disable=locally-disabled,bare-except
                pass
            try:
                return obj.to_dict()
            except:  # pylint: disable=locally-disabled,bare-except
                pass
            try:
                return str(obj)
            except:  # pylint: disable=locally-disabled,bare-except
                pass
            return flask.json.JSONEncoder.default(self, obj)

    app.json_encoder = MyDojoJSONEncoder

    @app.route('/mydojo-main.js')
    def mainjs():  # pylint: disable=locally-disabled,unused-variable
        """
        Default route for main application JavaScript file.
        """
        return flask.make_response(flask.render_template('mydojo-main.js'),
                                   200, {'Content-Type': 'text/javascript'})

    # Initialize JSGlue plugin for using `flask.url_for()` method in JavaScript.
    jsglue = flask_jsglue.JSGlue()
    jsglue.init_app(app)

    return app
Example #2
0
# @Date:   2017-06-28 15:31:51
# @Last modified by:   Brian Cherinka
# @Last Modified time: 2018-06-05 11:29:46

from __future__ import print_function, division, absolute_import
from flask_featureflags import FeatureFlag
from raven.contrib.flask import Sentry
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from flask_profiler import Profiler
from flask_caching import Cache
import flask_jsglue as jsg
import logging

# JS Glue (allows use of Flask.url_for inside javascript)
jsglue = jsg.JSGlue()

# Feature Flags (allows turning on/off of features)
flags = FeatureFlag()

# Sentry error logging
sentry = Sentry(logging=True, level=logging.ERROR)

# Route Rate Limiter
limiter = Limiter(key_func=get_remote_address)

# Flask Profiler
profiler = Profiler()

# Flask Cache
cache = Cache()
Example #3
0
def create_app(debug=False, local=False, use_profiler=True):

    #from marvin.api import theapi as api
    from marvin.api.cube import CubeView
    from marvin.api.maps import MapsView
    from marvin.api.modelcube import ModelCubeView
    from marvin.api.plate import PlateView
    from marvin.api.rss import RSSView
    from marvin.api.spaxel import SpaxelView
    from marvin.api.query import QueryView
    from marvin.api.general import GeneralRequestsView
    from marvin.web.controllers.index import index
    from marvin.web.controllers.galaxy import galaxy
    from marvin.web.controllers.search import search
    from marvin.web.controllers.plate import plate
    from marvin.web.controllers.images import images
    from marvin.web.controllers.users import users

    # ----------------------------------
    # Create App
    marvin_base = os.environ.get('MARVIN_BASE', 'marvin2')
    app = Flask(__name__, static_url_path='/{0}/static'.format(marvin_base))
    api = Blueprint("api", __name__, url_prefix='/{0}/api'.format(marvin_base))
    app.debug = debug
    jsg.JSGLUE_JS_PATH = '/{0}/jsglue.js'.format(marvin_base)
    jsglue = jsg.JSGlue(app)

    # Add Marvin Logger
    app.logger.addHandler(log)

    # Setup the profile configuration
    app.config["flask_profiler"] = {
        "enabled": True,
        "storage": {
            "engine": "sqlite",
            "FILE": os.path.join(os.environ['MARVIN_DIR'],
                                 'flask_profiler.sql')
        },
        'endpointRoot': '{0}/profiler'.format(marvin_base),
        "basicAuth": {
            "enabled": False
        }
    }

    # ----------------------------------
    # Initialize logging + Sentry + UWSGI config for Production Marvin
    if app.debug is False:

        # ----------------------------------------------------------
        # Set up getsentry.com logging - only use when in production
        dsn = os.environ.get('SENTRY_DSN', None)
        app.config['SENTRY_DSN'] = dsn
        sentry = Sentry(app, logging=True, level=logging.ERROR)

        # --------------------------------------
        # Configuration when running under uWSGI
        try:
            import uwsgi
            app.use_x_sendfile = True
        except ImportError:
            pass
    else:
        sentry = None
        config.use_sentry = False
        config.add_github_message = False

    # Change the implementation of "decimal" to a C-based version (much! faster)
    #
    # This is producing this error [ Illegal value: Decimal('3621.59598486') ]on some of the remote API tests with getSpectrum
    # Turning this off for now
    #
    # try:
    #    import cdecimal
    #    sys.modules["decimal"] = cdecimal
    # except ImportError:
    #    pass  # no available

    # Find which connection to make
    connection = getDbMachine()
    local = (connection == 'local') or local

    # ----------------------------------
    # Set some environment variables
    config._inapp = True
    os.environ['SAS_REDUX'] = 'sas/mangawork/manga/spectro/redux'
    os.environ['SAS_ANALYSIS'] = 'sas/mangawork/manga/spectro/analysis'
    os.environ['SAS_SANDBOX'] = 'sas/mangawork/manga/sandbox'
    release = os.environ.get('MARVIN_RELEASE', 'mangawork')
    os.environ[
        'SAS_PREFIX'] = 'marvin2' if release == 'mangawork' else 'dr13/marvin'
    url_prefix = '/marvin2' if local else '/{0}'.format(marvin_base)

    # ----------------------------------
    # Load the appropriate Flask configuration file for debug or production
    if app.debug:
        if local:
            server_config_file = os.path.join(
                os.path.dirname(os.path.abspath(__file__)), 'configuration',
                'localhost.cfg')
        elif connection == 'utah':
            server_config_file = os.path.join(
                os.path.dirname(os.path.abspath(__file__)), 'configuration',
                'utah.cfg')
        else:
            server_config_file = None
            app.logger.debug(
                "Trying to run in debug mode, but not running on a development machine that has database access."
            )
            # sys.exit(1)
    else:
        try:
            import uwsgi
            server_config_file = os.path.join(
                os.path.dirname(os.path.abspath(__file__)), 'configuration',
                uwsgi.opt['flask-config-file'])
        except ImportError:
            app.logger.debug(
                "Trying to run in production mode, but not running under uWSGI. You might try running again with the '--debug' flag."
            )
            sys.exit(1)

    if server_config_file:
        app.logger.info('Loading config file: {0}'.format(server_config_file))
        app.config.from_pyfile(server_config_file)

    # ----------------------------------
    # Initialize feature flags
    feature_flags = FeatureFlag(app)
    # configFeatures(debug=app.debug)

    # Update any config parameters
    app.config["UPLOAD_FOLDER"] = os.environ.get("MARVIN_DATA_DIR", None)
    app.config["LIB_PATH"] = os.path.join(
        os.path.dirname(os.path.abspath(__file__)), 'lib')

    # Add lib directory as a new static path
    @app.route('/{0}/lib/<path:filename>'.format(marvin_base))
    def lib(filename):
        return send_from_directory(app.config["LIB_PATH"], filename)

    # Register update global session
    @app.before_request
    def global_update():
        ''' updates the global session / config '''
        updateGlobalSession()
        # send_request()

    # ----------------
    # Error Handling
    # ----------------

    def _is_api(request):
        ''' Checks if the error comes from the api '''
        return request.blueprint == 'api' or 'api' in request.url

    @app.errorhandler(404)
    def page_not_found(error):
        name = 'Page Not Found'
        if _is_api(request):
            return make_error_json(error, name, 404)
        else:
            return make_error_page(app, name, 404, sentry=sentry)

    @app.errorhandler(500)
    def internal_server_error(error):
        name = 'Internal Server Error'
        if _is_api(request):
            return make_error_json(error, name, 500)
        else:
            return make_error_page(app, name, 500, sentry=sentry)

    @app.errorhandler(400)
    def bad_request(error):
        name = 'Bad Request'
        if _is_api(request):
            return make_error_json(error, name, 400)
        else:
            return make_error_page(app, name, 400, sentry=sentry)

    @app.errorhandler(405)
    def method_not_allowed(error):
        name = 'Method Not Allowed'
        if _is_api(request):
            return make_error_json(error, name, 405)
        else:
            return make_error_page(app, name, 405, sentry=sentry)

    @app.errorhandler(422)
    def handle_unprocessable_entity(error):
        name = 'Unprocessable Entity'
        data = getattr(error, 'data')
        if data:
            # Get validations from the ValidationError object
            messages = data['messages']
        else:
            messages = ['Invalid request']

        if _is_api(request):
            return make_error_json(error, name, 422)
        else:
            return make_error_page(app,
                                   name,
                                   422,
                                   sentry=sentry,
                                   data=messages)

    # These should be moved into the api module but they need the api blueprint to be registered on
    # I had these in the api.__init__ with the api blueprint defined there as theapi
    # which was imported here, see line 39
    # This should all be moved into the Brain and abstracted since it is
    # general useful standardized stuff (what?)

    @api.errorhandler(422)
    def handle_unprocessable_entity(err):
        # webargs attaches additional metadata to the `data` attribute
        data = getattr(err, 'data')
        if data:
            # Get validations from the ValidationError object
            messages = data['messages']
        else:
            messages = ['Invalid request']
        return jsonify({
            'validation_errors': messages,
        }), 422

    # ----------------------------------
    # Registration
    config.use_sentry = False
    #
    # API route registration
    CubeView.register(api)
    MapsView.register(api)
    ModelCubeView.register(api)
    PlateView.register(api)
    RSSView.register(api)
    SpaxelView.register(api)
    GeneralRequestsView.register(api)
    QueryView.register(api)
    app.register_blueprint(api)

    # Web route registration
    app.register_blueprint(index, url_prefix=url_prefix)
    app.register_blueprint(galaxy, url_prefix=url_prefix)
    app.register_blueprint(search, url_prefix=url_prefix)
    app.register_blueprint(plate, url_prefix=url_prefix)
    app.register_blueprint(images, url_prefix=url_prefix)
    app.register_blueprint(users, url_prefix=url_prefix)

    # Register all custom Jinja filters in the file.
    app.register_blueprint(jinjablue)

    # Register error handlers
    app.register_blueprint(web)

    # Initialize the Flask-Profiler ; see results at localhost:portnumber/flask-profiler
    if use_profiler:
        try:
            flask_profiler.init_app(app)
        except Exception as e:
            pass

    return app