Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin blueprint registration #171

Merged
merged 25 commits into from Sep 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a0bb2c2
Let the root view ('/') be configurable by account role(s).
nhoening Aug 30, 2021
c14dcfc
enable more control over menu through FLEXMEASURES_LISTED_VIEWS, refa…
nhoening Aug 31, 2021
14ff213
add changelog entry
nhoening Aug 31, 2021
cf6239f
mention the FM version this requires in the docs
nhoening Aug 31, 2021
352c28a
Fix bug for Flask AnonymousUser not having an account associated with…
Flix6x Sep 1, 2021
5bc278f
Allow a plugin to define multiple Blueprints and relax naming assumpt…
Flix6x Sep 1, 2021
1c11c8f
Merge branch 'views-by-accounts' of github.com:SeitaBV/flexmeasures i…
nhoening Sep 2, 2021
516c266
a few smaller corrections from code review
nhoening Sep 2, 2021
7ef1585
support adding and deleting of roles
nhoening Sep 2, 2021
5ab5f39
use a better name for the function dealing with these config entries,…
nhoening Sep 2, 2021
b493b04
Swap lines so that the default root_view can be set in time (#165)
Flix6x Sep 2, 2021
68c14ca
also make FLEXMEASURES_PLATFORM_NAME flexible w.r.t. to account roles
nhoening Sep 2, 2021
9054722
merge
nhoening Sep 2, 2021
d03909e
Customize view titles and icons (#166)
Flix6x Sep 2, 2021
12a0e29
add MENU to menu-related config setting names
nhoening Sep 2, 2021
1ffcac6
separate the application of updates to the menu config, so we apply e…
nhoening Sep 2, 2021
b4d5f94
Revert "Allow a plugin to define multiple Blueprints and relax naming…
Flix6x Sep 3, 2021
1423cff
Typographical edits
Flix6x Sep 3, 2021
3d83ee2
Corrections in documentation of root view setting
Flix6x Sep 3, 2021
6f79923
Derive plugin version from module rather than from blueprint
Flix6x Sep 3, 2021
f14c207
Allow a plugin to define multiple Blueprints and relax naming assumpt…
Flix6x Sep 1, 2021
59bdd77
Merge remote-tracking branch 'origin/main' into plugin-blueprint-regi…
Flix6x Sep 3, 2021
e5d1142
Changelog entry
Flix6x Sep 3, 2021
58ab0b5
More nicely formatted list of plugins
Flix6x Sep 3, 2021
4f72dea
Add changelog warning
Flix6x Sep 3, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 4 additions & 2 deletions documentation/changelog.rst
Expand Up @@ -10,10 +10,12 @@ v0.6.0 | August XX, 2021

.. warning:: The config setting ``FLEXMEASURES_LISTED_VIEWS`` has been renamed to ``FLEXMEASURES_MENU_LISTED_VIEWS``.

.. warning:: Plugins now need to set their version on their module rather than on their blueprint. See the `documentation for writing plugins <https://flexmeasures.readthedocs.io/en/v0.6.0/dev/plugins.html>`_.

New features
-----------
* Analytics view offers grouping of all assets by location [see `PR #148 <http://www.github.com/SeitaBV/flexmeasures/pull/148>`_]
* Multi-tenancy: Supporting multiple customers per FlexMeasures server, by introducing the `Account` concept. Accounts have users and assets associated. [see `PR #159 <http://www.github.com/SeitaBV/flexmeasures/pull/159>`_ and `PR #163 <http://www.github.com/SeitaBV/flexmeasures/pull/163>`_]
* Multi-tenancy: Supporting multiple customers per FlexMeasures server, by introducing the `Account` concept. Accounts have users and assets associated. [see `PR #159 <http://www.github.com/SeitaBV/flexmeasures/pull/159>`_ and `PR #163 <http://www.github.com/SeitaBV/flexmeasures/pull/163>`_]
* In the UI, the root view ("/"), the platform name and the visible menu items can now be more tightly controlled (per account roles of the current user) [see also `PR #163 <http://www.github.com/SeitaBV/flexmeasures/pull/163>`_]
* Add (experimental) endpoint to post sensor data for any sensor. Also supports our ongoing integration with data internally represented using the `timely beliefs <https://github.com/SeitaBV/timely-beliefs>`_ lib [see `PR #147 <http://www.github.com/SeitaBV/flexmeasures/pull/147>`_]

Expand All @@ -22,10 +24,10 @@ Bugfixes

Infrastructure / Support
----------------------

* Add possibility to send errors to Sentry [see `PR #143 <http://www.github.com/SeitaBV/flexmeasures/pull/143>`_]
* Add CLI task to monitor if tasks ran successfully and recently enough [see `PR #146 <http://www.github.com/SeitaBV/flexmeasures/pull/146>`_]
* Document how to use a custom favicon in plugins [see `PR #152 <http://www.github.com/SeitaBV/flexmeasures/pull/152>`_]
* Allow plugins to register multiple Flask blueprints [see `PR #171 <http://www.github.com/SeitaBV/flexmeasures/pull/171>`_]
* Continue experimental integration with `timely beliefs <https://github.com/SeitaBV/timely-beliefs>`_ lib: link multiple sensors to a single asset [see `PR #157 <https://github.com/SeitaBV/flexmeasures/pull/157>`_]
* The experimental parts of the data model can now be visualised, as well, via `make show-data-model --uml --dev` [also in `PR #157 <https://github.com/SeitaBV/flexmeasures/pull/157>`_]

Expand Down
12 changes: 6 additions & 6 deletions documentation/dev/plugins.rst
Expand Up @@ -18,7 +18,7 @@ Use the config setting :ref:`plugin-config` to point to your plugin(s).
Here are the assumptions FlexMeasures makes to be able to import your Blueprint:

- The plugin folder contains an ``__init__.py`` file.
- In this init, you define a Blueprint object called ``<plugin folder>_bp``.
- In that file, you define a Blueprint object (or several).

We'll refer to the plugin with the name of your plugin folder.

Expand All @@ -37,17 +37,17 @@ With the ``__init__.py`` below, plus the custom Jinja2 template, ``our_client``

.. code-block:: python

__version__ = "2.0"

from flask import Blueprint, render_template, abort

from flask_security import login_required
from flexmeasures.ui.utils.view_utils import render_flexmeasures_template


our_client_bp = Blueprint('our_client', 'our_client',
our_client_bp = Blueprint('our_client', __name__,
template_folder='templates')

our_client_bp.__version__ = "2.0"

# Showcase: Adding a view

@our_client_bp.route('/')
Expand Down Expand Up @@ -79,7 +79,7 @@ With the ``__init__.py`` below, plus the custom Jinja2 template, ``our_client``

.. note:: You can overwrite FlexMeasures routing in your plugin. In our example above, we are using the root route ``/``. FlexMeasures registers plugin routes before its own, so in this case visiting the root URL of your app will display this plugged-in view (the same you'd see at `/my-page`).

.. note:: The ``__version__`` attribute on our blueprint object is being displayed in the standard FlexMeasures UI footer, where we show loaded plugins. Of course, it can also be useful for your own maintenance.
.. note:: The ``__version__`` attribute on our module is being displayed in the standard FlexMeasures UI footer, where we show loaded plugins. Of course, it can also be useful for your own maintenance.


The template would live at ``<some_folder>/our_client/templates/my_page.html``, which works just as other FlexMeasures templates (they are Jinja2 templates):
Expand All @@ -90,7 +90,7 @@ The template would live at ``<some_folder>/our_client/templates/my_page.html``,

{% set active_page = "my-page" %}

{% block title %} Our client Dashboard {% endblock %}
{% block title %} Our client dashboard {% endblock %}

{% block divs %}

Expand Down
2 changes: 1 addition & 1 deletion flexmeasures/ui/utils/view_utils.py
Expand Up @@ -71,7 +71,7 @@ def render_flexmeasures_template(html_filename: str, **variables):
) = get_git_description()
app_start_time = current_app.config.get("START_TIME")
variables["app_running_since"] = time_utils.naturalized_datetime_str(app_start_time)
variables["loaded_plugins"] = ",".join(
variables["loaded_plugins"] = ", ".join(
f"{p_name} (v{p_version})"
for p_name, p_version in current_app.config.get("LOADED_PLUGINS", {}).items()
)
Expand Down
23 changes: 18 additions & 5 deletions flexmeasures/utils/app_utils.py
Expand Up @@ -5,7 +5,7 @@
from importlib.abc import Loader

import click
from flask import Flask, current_app, redirect
from flask import Blueprint, Flask, current_app, redirect
from flask.cli import FlaskGroup, with_appcontext
from flask_security import current_user
import sentry_sdk
Expand Down Expand Up @@ -183,7 +183,7 @@ def register_plugins(app: Flask):

Assumptions:
- Your plugin folders contains an __init__.py file.
- In this init, you define a Blueprint object called <plugin folder>_bp
- In that file, you define a Blueprint object (or several).

We'll refer to the plugins with the name of your plugin folders (last part of the path).
"""
Expand Down Expand Up @@ -214,9 +214,22 @@ def register_plugins(app: Flask):
sys.modules[plugin_name] = module
assert isinstance(spec.loader, Loader)
spec.loader.exec_module(module)
plugin_blueprint = getattr(module, f"{plugin_name}_bp")
app.register_blueprint(plugin_blueprint)
plugin_version = getattr(plugin_blueprint, "__version__", "0.1")

# Look for blueprints in the plugin's main __init__ module and register them
plugin_blueprints = [
getattr(module, a)
for a in dir(module)
if isinstance(getattr(module, a), Blueprint)
]
if not plugin_blueprints:
app.logger.warning(
f"No blueprints found for plugin {plugin_name} at {plugin_path}."
)
continue
for plugin_blueprint in plugin_blueprints:
app.register_blueprint(plugin_blueprint)

plugin_version = getattr(module, "__version__", "0.1")
app.config["LOADED_PLUGINS"][plugin_name] = plugin_version
app.logger.info(f"Loaded plugins: {app.config['LOADED_PLUGINS']}")
sentry_sdk.set_context("plugins", app.config.get("LOADED_PLUGINS", {}))