3.3. Creating own web content

The final chapter is all about creating your first own view with groundwork-web.

Therefore we will create a simple HTML page, which presents the changes of each watcher and has a button to delete the history of each watcher.

The final result will look like this:

../_images/own_web_view.png

3.3.1. Registering route and menu entry

Open the file csv_document_plugin.py and perform the following changes:

Add first we need to import some basic flask functions and the need GwWebPatter:

1
2
import os
from groundwork.patterns import GwDocumentsPattern

Then we add this Pattern to our plugin:

1

And finally we add all the magic to activate():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
        self.NewRow = None

    def activate(self):
        this_dir = os.path.dirname(__file__)
        content_path = os.path.join(this_dir, 'csv_document_content.rst')
        with open(content_path, 'r') as doc_content:
            self.documents.register(name="CsvDocument",
                                    content=doc_content.read(),
                                    description="Stores pass csv watcher activities")

        self.signals.connect("csv_archive_receiver", "csv_watcher_change",
                             self._archive_csv_change, "listen to changes to archive them.")

        self.db = self.databases.register(self.app.config.get("HISTORY_DATABASE_NAME", "csv_history"),
                                          self.app.config.get("HISTORY_DATABASE_CONNECTION", "sqlite://"),
                                          self.app.config.get("HISTORY_DATABASE_DESCRIPTION", "Stores csv history"))
        self.CsvFile, self.Version, self.MissingRow, self.NewRow = get_models(self.db)
        self.db.classes.register(self.CsvFile)
        self.db.classes.register(self.Version)
        self.db.classes.register(self.MissingRow)
        self.db.classes.register(self.NewRow)
        self.db.create_all()

        if self.app.web.contexts.get("csv") is None:
            self.web.contexts.register(name="csv",
                                       template_folder=os.path.join(os.path.dirname(__file__), "templates"),
                                       static_folder=os.path.join(os.path.dirname(__file__), "static"),
                                       url_prefix="/csv",
                                       description="context for csv tasks")

        self.web.routes.register(url="/history",
                                 methods=["GET", "POST"],
                                 endpoint=self._history_view,
                                 context="csv")

        try:
            menu_csv = self.web.menus.register(name="CSV", link="#")
        except Exception:
            menu_csv = self.web.menus.get("CSV")

        with self.app.web.flask.app_context():

In lines 22-27 we register a context, which gets used in line 29 during registration of our view.

Our view shall handle the url /history and allows the methods GET and POST. POST is need for our delte button later. We also need to add a function/endpoint, which gets executed, if a request to the configured url is incoming.

In lines 34-40 we define a menu entry to make our new web-view easy accessible.

3.3.2. View function

Now we add the view itself to our plugin:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
            menu_csv.register("History", link=url_for("csv._history_view"))

    def _history_view(self):

        if request.method == 'POST':
            csv_file = request.form['csv_file']
            csv_file_object = self.CsvFile.query.filter_by(name=csv_file).first()
            versions = self.Version.query.filter_by(csv_file=csv_file_object).all()
            for version in versions:
                self.db.session.delete(version)
            self.db.commit()
            flash("Versions of %s deleted" % request.form['csv_file'])
        watchers = self.get_csv_history()

Lines 11+12 are the most important ones, because line 11 gets the history data and line 12 renders our HTML page.

The function self.web.render takes as fist argument the HTML template file name. After this we can provide as many keyword based arguments as we like. They will be available inside our template.

Lines 3-10 cares about the correct history cleaning, if the button gets pressed. The code gets executed only, if the request is coming via POST (Line 3). After deletion, we want to inform the user that everything got deleted as requests. So we use flash().

3.3.3. Template

And finally lets create our HTML template file.

Inside the folder of our plugin CsvDocumentPlugin, we create a folder called templates. And there we add a file csv_history.html with the following content.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{% extends 'master.html' %}

{% block body %}
<h1>CSV History <small>View and edit history</small></h1>

<h2>History</h2>

{% for watcher in watchers %}
<h3>{{watcher.name}}</h3>
    <form action="" method="post">
        <input type="hidden" name="csv_file" value="{{watcher.name}}">
        <input type="submit" value="Clean history">
    </form>

    {% for version in watcher.version %}
        <h4>{{version.version}}</h4>
        <h5>Missing rows</h5>
        {% for row in version.missing_row %}
            {% for key,value in row.row.items() %}
            {{ key }} : <b>{{value}}</b>
            {% endfor %}
            <br>
        {% endfor %}

        <h5>New rows</h5>
        {% for row in version.new_row %}
            {% for key,value in row.row.items() %}
            {{ key }} : <b>{{value}}</b>
            {% endfor %}
            <br>
        {% endfor %}

    {% endfor %}


{% endfor %}
{% endblock %}

The template uses the jinja template language, which is quite easy to read.

In Line 8 we request the first time our variable watchers, which is available because we have care about this in the function render() above.

We uses some for-loops to output our data with {{ .. }}.

3.3.4. Final words

That’s it. Everything is done and running and we are able to see our watchers working.

If you have any questions or ideas, please get in contact with us. The easiest way is by creating an issue on the github pages of groundwork-tutorial or groundwork .