Completed
Push — master ( c54497...3b5137 )
by Daniel
58s
created

FlaskProvider.register_context()   B

Complexity

Conditions 3

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
c 1
b 0
f 0
dl 0
loc 25
rs 8.8571
1
from docutils.core import publish_parts
2
3
from flask import Flask, render_template, Blueprint, request
4
from groundwork.patterns import GwCommandsPattern
5
6
from groundwork_web.patterns import GwWebPattern
7
from groundwork_web.patterns.gw_web_pattern.provider import BaseProvider
8
9
10
class GwWebFlask(GwWebPattern, GwCommandsPattern):
11
    def __init__(self, *args, **kwargs):
12
        self.name = kwargs.get("name", self.__class__.__name__)
13
14
        super().__init__(*args, **kwargs)
15
        self.flask_app = None
16
17
    def activate(self):
18
        self.flask_app = Flask(__name__)
19
20
        # Inject send_signal() to jinja templates
21
        # Use it like {{ send_signal("my_signal") }}
22
        self.flask_app.jinja_env.globals.update(send_signal=self.signals.send)
23
24
        self.flask_app.jinja_env.globals.update(get_menu=self.__get_menu)
25
26
        self.flask_app.jinja_env.globals.update(get_config=self.app.config.get)
27
28
        self.flask_app.jinja_env.globals.update(rst2html=self.__rst2html)
29
30
        # self.signals.register("web_menu", "signal to retrieve entries for the web menu")
31
        # self.signals.connect("test", "test_web", blub, description="test web signal")
32
33
        self.web.providers.register("flask", FlaskProvider(self.flask_app), "Flask web provider")
34
        self.web.servers.register("flask_debug", self.__start_flask_debug_server, "Starts the flask debug server")
35
36
    def deactivate(self):
37
        self.flask_app = None
38
39
    def __start_flask_debug_server(self):
40
        self.flask_app.run()
41
42
    def __get_menu(self, cluster="base"):
43
        return self.web.menus.get(cluster=cluster)
44
45
    def __rst2html(self, document, part="body"):
46
        if document is not None and type(document) == str:
47
            doc_rendered = publish_parts(document, writer_name="html")
48
            if part not in doc_rendered.keys():
49
                raise KeyError("%s is not a valid key for part parameter of rst2html.\nValid options: " %
50
                               (part, ",".join(doc_rendered.keys())))
51
52
            return doc_rendered[part]
53
        return document
54
55
56
class FlaskProvider(BaseProvider):
57
    """
58
    FlaskProvider, which maps general provider functions to flask specific functions and objects.
59
60
    For contexts, the flask blueprint mechanism is used and each router registration must provide a context.
61
62
    For render() flask's render_template() is used.
63
64
    The flask object itself is available under ``flask_app``. E.g. app.web.providers.get("flask").flask_app
65
    """
66
67
    def __init__(self, instance, *args, **kwargs):
68
        self.flask_app = instance
69
        self.blueprints = {}
70
        self.request = request
71
72
    def register_route(self, url, methods, endpoint, context, *arg, **kwargs):
73
        """
74
        Registers a route in flask.
75
76
        :param url: url for route, related to the context base url
77
        :param methods: List of allowed HTTP methods, e.g. ["GET", "POST"]
78
        :param endpoint: functions, which gets called, if route gets requested.
79
        :param context: The name of the context, under which the route shall be registered
80
        :param arg: Optional arguments
81
        :param kwargs: Optional key-word arguments
82
        :return: None
83
        """
84
        if context not in self.blueprints.keys():
85
            raise NameError("Context %s does not exist" % context)
86
87
        blueprint = self.blueprints[context]
88
        blueprint.add_url_rule(url, methods=methods, endpoint=endpoint.__name__, view_func=endpoint)
89
90
        # We have to (re-)register our blueprint to activate the route
91
        self.flask_app.register_blueprint(blueprint)
92
93
    def register_context(self, name, template_folder, static_folder, url_prefix, overwrite=False, *arg, **kwargs):
94
        """
95
        Registers a new context (aka blueprint in flask).
96
97
        :param name: Name of the context
98
        :param template_folder:  Location of the template folder
99
        :param static_folder:  Location of the static folder
100
        :param url_prefix: A prefix for all routes, which get registered for this context. E.G. url_prefix="/test",
101
        new route url = "my_page" --> url = /test/my_page
102
        :param overwrite: if True, an existing context gets overwritten without an error. Default is False
103
        :param arg: Optional arguments
104
        :param kwargs: Optional key-word arguments
105
        :return: None
106
        """
107
        if name in self.blueprints.keys() and not overwrite:
108
            raise NameError("Context %s already exists" % name)
109
110
        blueprint = Blueprint(name, __name__,
111
                              url_prefix=url_prefix,
112
                              subdomain=None,
113
                              template_folder=template_folder,
114
                              static_folder=static_folder,
115
                              static_url_path="/static/" + name)
116
        self.blueprints[name] = blueprint
117
        self.flask_app.register_blueprint(blueprint)
118
119
    def render(self, template, **kwargs):
120
        """
121
        Renders a template and returns a strings, which represents the rendered data.
122
123
        Internally render_template() from flask is used.
124
125
        :param template: Name of the template
126
        :param kwargs: Optional key-word arguments, which get passed to the template engine.
127
        :return: Rendered template as string
128
        """
129
130
        return render_template(template, **kwargs)
131
132