# -- coding: utf-8 -- # # This file is part of Flask-Notifications # Copyright (C) 2015 CERN. # # Flask-Notifications is free software; you can redistribute it and/or modify # it under the terms of the Revised BSD License; see LICENSE file for # more details.
Flask-Notifications¶
Flask-Notifications is a Flask extension that provides a generic real-time notification framework.
Contents¶
Installation¶
Flask-Notifications is on PyPI so all you need is:
$ pip install Flask-Notifications
The development version can be downloaded from its page at GitHub.
$ git clone https://github.com/inveniosoftware/flask-notifications.git
$ cd flask-notifications
$ python setup.py develop
$ ./run-tests.sh
Usage¶
This guide assumes that you have successfully installed Flask-Notifications
package already. If not, please follow the Installation
instructions first.
Building a simple notification system¶
Flask-Notifications
provides a simple API to build your own real-time
notification system. In this guide, we will see how to build such a system
easily in a few steps.
First, we create the Flask
application and initialise the Notifications
extension. Flask-Notifications
depends upon Celery and Redis. The first
one is used for task processing and the second one for the Pub/Sub primitives.
Then, we reuse Redis as a broker too.
In case you want to use another broker as RabbitMQ, you can implement the
Pub/Sub or Fan-Out pattern by yourself by extending the Backend
type.
from flask import Flask
from flask_notifications import Notifications
app = Flask(__name__)
notifications = Notifications(app=app)
or:
from flask import Flask
from flask_notifications import Notifications
app = Flask(__name__)
notifications = Notifications()
notifications.init_app(app=app)
or:
from flask import Flask
from flask_notifications import Notifications
# Corresponding information for brokers and Celery
config = {...}
celery = FlaskCeleryExt(app).celery
redis = StrictRedis(host=redis_host)
app = Flask(__name__)
notifications = Notifications()
notifications.init_app(app=app, celery=celery, broker=redis)
Now, we create a EventHub
. A hub is composed of a filter and a list of consumers.
When an event is sent to the hub, the filter is applied to that event. If it
passes, it is sent to all the registered consumers.
An EventHub
requires a label as a parameter. This label cannot be randomized.
In order to make a reference to a hub, one should get first his identifier, which
is not the same as the label.
test_hub = notifications.create_hub("TestHub")
test_hub_id = user_hub.hub_id
The next step is to set up our hub. Let’s say we want to aggregate in that hub all the events with the “test” type and which are sent from now on.
import datetime
now = datetime.now()
from flask_notifications.filters.with_event_type import WithEventType
from flask_notifications.filters.before_date import BeforeDate
event_hub.filter_by(
WithEventType("test") & Not(BeforeDate(now))
)
This creates a composed filter with those requirements. Any EventFilter
can
be composed using the bitwise operators (&
, |
and ^
) -it’s not possible to use
the logical operators and
, or
and xor
because Python2.7 does not allow to
override his behaviour-.
Now, we register some consumers to our hub.
@event_hub.register_consumer(celery_task_name="app.write_to_file")
def write_to_file(event, *args, **kwargs):
with open("events.log", "a+w") as f:
f.write(str(event))
push_consumer = PushConsumer(redis, event_hub_id)
event_hub.register_consumer(push_consumer)
When registering a function using the decorator, it is very important to specify
the celery_task_name
relatively to your application to help the workers to
detect the function. More information here.
If you feel like to write a complex consumer, you can extend the the Consumer
interface. Also, this interface has some hooks. One before consuming the event
and the other after. This is very handy when you want to confirm that an event
has been stored and hence send it to a database to persist it.
The only missing step is to send notifications.
event = Event(None, "test", "This event will pass the filter",
"This is the body of the test", sender="system")
notifications.send(event.to_json())
event = Event(None, "system", "This event will not pass the filter",
"This is the body of the test", sender="system")
notifications.send(event.to_json())
Event
is a dictionary with a predefined model. If you would like to
add your own fields and filter them, you just need to add the field to
the Event
and create a new filter by extending EventFilter
.
You should now be able to emulate this example in your own Flask applications. For more information, please read the Architecture guide, check the Predefined Consumers section, the Configuration and peruse the API.
Architecture¶
The application is composed of two main parts: the main program and the workers. These workers will process asynchronously all the consumers in the hubs.
As we are defining functions using the @register_consumer
decorator,
the workers need to know and register this function as well. Therefore, a worker
imports the main program and compiles it. It is very important not to have
any randomized value because they won’t match neither in the main application
nor the program.
If you are going to use predefined consumers, you need to add the necessary configuration values to the Flask configuration.
Configuration¶
Flask-Notifications
only needs one parameter in the Flask configuration: BACKEND.
This option points to the Python path of a subclass of Backend
. By default,
it uses RedisBackend
, but you can add your own implementation of Backend
using other brokers like RabbitMQ. You just need to make you sure that the option
has the right path to the class in order to be imported by the Notifications module.
config = {
...,
# Default option
"BACKEND": "flask_notifications.pubsub.redis_pubsub.RedisPubSub",
}
Also, Flask-Notifications
uses the JSON serializer and deserializer to
pass the events to the consumers. So, it is important that you allow the json
serializer in the Celery configuration by using the following options (you can
add any serializer that you want, the important thing is to enable the json
serializer):
config = {
...,
"CELERY_ACCEPT_CONTENT": ["application/json"],
"CELERY_TASK_SERIALIZER": "json",
"CELERY_RESULT_SERIALIZER": "json",
}
Predefined Consumers¶
The predefined consumers exist to fulfil simple needs like sending an email or writing a log. You can use them in your code by importing and registering them.
For more complex consumers, you may create your own by extending a predefined
consumer or creating a new one extending Consumer
.
Current predefined consumers:
-
class
push_consumer.
PushConsumer
¶
-
class
log_consumer.
LogConsumer
¶
-
class
flaskemail_consumer.
FlaskEmailConsumer
¶
-
class
flaskmail_consumer.
FlaskMailConsumer
¶
API¶
If you are looking for information on a specific function, class or method, this part of the documentation is for you.
Flask extension¶
Contributing¶
Bug reports, feature requests, and other contributions are welcome. If you find a demonstrable problem that is caused by the code of this library, please:
- Search for already reported problems.
- Check if the issue has been fixed or is still reproducible on the latest master branch.
- Create an issue with a test case.
If you create a feature branch, you can run the tests to ensure everything is operating correctly:
$ ./run-tests.sh
License¶
Flask-Notifications is free software; you can redistribute it and/or modify it under the terms of the Revised BSD License quoted below.
Copyright (C) 2015 CERN.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an Intergovernmental Organization or submit itself to any jurisdiction.
Authors¶
Flask-Notifications is developed for use in Invenio digital library software.
Contact Invenio at info@inveniosoftware.org
- Jorge Vicente Cantero <jorgevc@fastmail.es>