GwWebPattern   A
last analyzed

Complexity

Total Complexity 2

Size/Duplication

Total Lines 10
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
rs 10
wmc 2

1 Method

Rating   Name   Duplication   Size   Complexity  
A __init__() 0 8 2
1
import os
2
import logging
3
from flask import Flask, render_template
4
from flask_babel import Babel
5
from docutils.core import publish_parts
6
7
from groundwork.patterns import GwBasePattern
8
9
from groundwork_web.patterns.gw_web_pattern.server import ServerManagerApplication, ServerManagerPlugin
10
from groundwork_web.patterns.gw_web_pattern.context import ContextManagerApplication, ContextManagerPlugin
11
from groundwork_web.patterns.gw_web_pattern.route import RouteManagerApplication, RouteManagerPlugin
12
from groundwork_web.patterns.gw_web_pattern.menu import MenuApplication, MenuPlugin
13
14
15
class GwWebPattern(GwBasePattern):
16
17
    def __init__(self, *args, **kwargs):
18
        super(GwWebPattern, self).__init__(*args, **kwargs)
19
        if not hasattr(self.app, "web"):
20
            self.app.web = WebApplication(self.app)
21
22
        #: Instance of :class:`~.WebPlugin`.
23
        #: Provides functions to manage web based objects
24
        self.web = WebPlugin(self)
25
26
27
class WebPlugin:
28
    def __init__(self, plugin):
29
        self.plugin = plugin
30
        self.app = plugin.app
31
        self.log = plugin.log
32
33
        self.servers = ServerManagerPlugin(plugin)
34
        self.contexts = ContextManagerPlugin(plugin)
35
        self.routes = RouteManagerPlugin(plugin)
36
        self.menus = MenuPlugin(plugin)
37
38
        self.plugin.signals.connect(receiver="%s_gw_web_activation" % self.plugin.name,
39
                                    signal="plugin_activate_pre",
40
                                    function=self.__activate_gw_web,
41
                                    description="Initialised gw_web (flask) during plugin activation "
42
                                                "of %s" % self.plugin.name,
43
                                    sender=self.plugin)
44
45
    @property
46
    def flask(self):
47
        return self.app.web.flask
48
49
    @flask.setter
50
    def flask(self, value):
51
        self.app.web.flask = value
52
53
    def __activate_gw_web(self, plugin, *args, **kwargs):
54
        self.app.web.init_flask()
55
        # We need to send "gw_web_loaded" every time a plugin got activated with inherited GwWebPattern.
56
        # We can not send it only once during the "real/final" flask initialisation, because a plugin may be loaded
57
        # later and was not able to register a receiver for "gw_web_loaded" before
58
        # the "real/final" flask init execution.
59
        # So "gw_web_loaded" indicates that gw_web is ready for usage. Even if it was called x times.
60
        # The plugin, which has registered a receiver, is responsible for not executing related code each time
61
        # "gw_web_loaded" is send.
62
        self.app.signals.send("gw_web_loaded", self.app)
63
64
    def render(self, template, plugin=None, **kwargs):
65
        if plugin is None:
66
            plugin = self.plugin
67
68
        return self.app.web.render(template, plugin, **kwargs)
69
70
71
class WebApplication:
72
    def __init__(self, app):
73
        self.app = app
74
        self.log = logging.getLogger(__name__)
75
        self.flask = None
76
        self.flask_babel = None
77
78
        self.servers = ServerManagerApplication(app)
79
        self.contexts = ContextManagerApplication(app)
80
        self.routes = RouteManagerApplication(app)
81
        self.menus = MenuApplication(app)
82
83
        self.app.signals.register("gw_web_loaded", self.app, "Indicates that gw_web (incl. flask) was loaded correctly"
84
                                                             "Mostly used to load other flask extensions after this.")
85
86
    def init_flask(self):
87
        """
88
        Initialises and configures flask, if not already done.
89
        :return: None
90
        """
91
        if self.flask is None:
92
            self.flask = Flask(self.app.name)
93
94
            # Inject send_signal() to jinja templates
95
            # Use it like {{ send_signal("my_signal") }}
96
            self.flask.jinja_env.globals.update(send_signal=self.app.signals.send)
97
98
            self.flask.jinja_env.globals.update(app=self.app)
99
100
            self.flask.jinja_env.globals.update(get_menu=self.__get_menu)
101
            self.flask.jinja_env.globals.update(get_config=self.app.config.get)
102
            self.flask.jinja_env.globals.update(rst2html=self.__rst2html)
103
104
            # Adds {%break/continue %} statement to jinja
105
            self.flask.jinja_env.add_extension('jinja2.ext.loopcontrols')
106
107
            # Lets set the secret key for flask. This should be set in configuration files, so that
108
            # signed cookies are still valid if the server got restarted.
109
            # If there is no such parameter available, we create a temporary key, which is only
110
            # available during server runtime.
111
            self.flask.secret_key = self.app.config.get("FLASK_SECRET_KEY", os.urandom(24))
112
113
            for key, value in self.app.config.__dict__.items():
114
                if key.startswith("FLASK_"):
115
                    self.flask.config[key[6:]] = value
116
117
            self.flask.config["SERVER_NAME"] = self.app.config.get("FLASK_SERVER_NAME", "127.0.0.1:5000")
118
            self.log.info("Using FLASK_SERVER_NAME=%s" % self.flask.config.get("SERVER_NAME"))
119
120
            # Load flask extensions
121
            self.flask_babel = Babel(self.flask)
122
123
    def __get_menu(self, cluster="base"):
124
        return self.menus.get(cluster=cluster)
125
126
    def __rst2html(self, document, part="body"):
127
        if document is not None and type(document) == str:
128
            doc_rendered = publish_parts(document, writer_name="html")
129
            if part not in doc_rendered.keys():
130
                raise KeyError("%s is not a valid key for part parameter of rst2html.\nValid options: " %
131
                               (part, ",".join(doc_rendered.keys())))
132
            return doc_rendered[part]
133
        return document
134
135
    def render(self, template, plugin=None, **kwargs):
136
        """
137
        Renders a template and returns a strings, which represents the rendered data.
138
139
        Internally render_template() from flask is used.
140
141
        :param template: Name of the template
142
        :param kwargs: Optional key-word arguments, which get passed to the template engine.
143
        :return: Rendered template as string
144
        """
145
146
        return render_template(template, app=self.app, plugin=plugin, **kwargs)
147