mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
track_event() mechanism for analytics and plugins
* Closes #2240 * Documentation for event plugin hooks, refs #2240 * Include example track_event plugin in docs, refs #2240 * Tests for track_event() and register_events() hooks, refs #2240 * Initial documentation for core events, refs #2240 * Internals documentation for datasette.track_event()
This commit is contained in:
parent
890615b3f2
commit
bcc4f6bf1f
22 changed files with 614 additions and 10 deletions
|
|
@ -40,6 +40,8 @@ extensions = [
|
|||
if not os.environ.get("DISABLE_SPHINX_INLINE_TABS"):
|
||||
extensions += ["sphinx_inline_tabs"]
|
||||
|
||||
autodoc_member_order = "bysource"
|
||||
|
||||
extlinks = {
|
||||
"issue": ("https://github.com/simonw/datasette/issues/%s", "#%s"),
|
||||
}
|
||||
|
|
|
|||
14
docs/events.rst
Normal file
14
docs/events.rst
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
.. _events:
|
||||
|
||||
Events
|
||||
======
|
||||
|
||||
Datasette includes a mechanism for tracking events that occur while the software is running. This is primarily intended to be used by plugins, which can both trigger events and listen for events.
|
||||
|
||||
The core Datasette application triggers events when certain things happen. This page describes those events.
|
||||
|
||||
Plugins can listen for events using the :ref:`plugin_hook_track_event` plugin hook, which will be called with instances of the following classes (or additional classes registered by other plugins):
|
||||
|
||||
.. automodule:: datasette.events
|
||||
:members:
|
||||
:exclude-members: Event
|
||||
|
|
@ -63,5 +63,6 @@ Contents
|
|||
plugin_hooks
|
||||
testing_plugins
|
||||
internals
|
||||
events
|
||||
contributing
|
||||
changelog
|
||||
|
|
|
|||
|
|
@ -593,6 +593,26 @@ Using either of these pattern will result in the in-memory database being served
|
|||
|
||||
This removes a database that has been previously added. ``name=`` is the unique name of that database.
|
||||
|
||||
.. _datasette_track_event:
|
||||
|
||||
await .track_event(event)
|
||||
-------------------------
|
||||
|
||||
``event`` - ``Event``
|
||||
An instance of a subclass of ``datasette.events.Event``.
|
||||
|
||||
Plugins can call this to track events, using classes they have previously registered. See :ref:`plugin_event_tracking` for details.
|
||||
|
||||
The event will then be passed to all plugins that have registered to receive events using the :ref:`plugin_hook_track_event` hook.
|
||||
|
||||
Example usage, assuming the plugin has previously registered the ``BanUserEvent`` class:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
await datasette.track_event(
|
||||
BanUserEvent(user={"id": 1, "username": "cleverbot"})
|
||||
)
|
||||
|
||||
.. _datasette_sign:
|
||||
|
||||
.sign(value, namespace="default")
|
||||
|
|
|
|||
|
|
@ -1759,3 +1759,103 @@ top_canned_query(datasette, request, database, query_name)
|
|||
The name of the canned query.
|
||||
|
||||
Returns HTML to be displayed at the top of the canned query page.
|
||||
|
||||
.. _plugin_event_tracking:
|
||||
|
||||
Event tracking
|
||||
--------------
|
||||
|
||||
Datasette includes an internal mechanism for tracking analytical events. This can be used for analytics, but can also be used by plugins that want to listen out for when key events occur (such as a table being created) and take action in response.
|
||||
|
||||
Plugins can register to receive events using the ``track_event`` plugin hook.
|
||||
|
||||
They can also define their own events for other plugins to receive using the ``register_events`` plugin hook, combined with calls to the ``datasette.track_event(...)`` internal method.
|
||||
|
||||
.. _plugin_hook_track_event:
|
||||
|
||||
track_event(datasette, event)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``datasette`` - :ref:`internals_datasette`
|
||||
You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``.
|
||||
|
||||
``event`` - ``Event``
|
||||
Information about the event, represented as an instance of a subclass of the ``Event`` base class.
|
||||
|
||||
This hook will be called any time an event is tracked by code that calls the :ref:`datasette.track_event(...) <datasette_track_event>` internal method.
|
||||
|
||||
The ``event`` object will always have the following properties:
|
||||
|
||||
- ``name``: a string representing the name of the event, for example ``logout`` or ``create-table``.
|
||||
- ``actor``: a dictionary representing the actor that triggered the event, or ``None`` if the event was not triggered by an actor.
|
||||
- ``created``: a ``datatime.datetime`` object in the ``timezone.utc`` timezone representing the time the event object was created.
|
||||
|
||||
Other properties on the event will be available depending on the type of event. You can also access those as a dictionary using ``event.properties()``.
|
||||
|
||||
The events fired by Datasette core are :ref:`documented here <events>`.
|
||||
|
||||
This example plugin logs details of all events to standard error:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from datasette import hookimpl
|
||||
import json
|
||||
import sys
|
||||
|
||||
|
||||
@hookimpl
|
||||
def track_event(event):
|
||||
name = event.name
|
||||
actor = event.actor
|
||||
properties = event.properties()
|
||||
msg = json.dumps(
|
||||
{
|
||||
"name": name,
|
||||
"actor": actor,
|
||||
"properties": properties,
|
||||
}
|
||||
)
|
||||
print(msg, file=sys.stderr, flush=True)
|
||||
|
||||
|
||||
.. _plugin_hook_register_events:
|
||||
|
||||
register_events(datasette)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``datasette`` - :ref:`internals_datasette`
|
||||
You can use this to access plugin configuration options via ``datasette.plugin_config(your_plugin_name)``.
|
||||
|
||||
This hook should return a list of ``Event`` subclasses that represent custom events that the plugin might send to the ``datasette.track_event()`` method.
|
||||
|
||||
This example registers event subclasses for ``ban-user`` and ``unban-user`` events:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datasette import hookimpl, Event
|
||||
|
||||
|
||||
@dataclass
|
||||
class BanUserEvent(Event):
|
||||
name = "ban-user"
|
||||
user: dict
|
||||
|
||||
|
||||
@dataclass
|
||||
class UnbanUserEvent(Event):
|
||||
name = "unban-user"
|
||||
user: dict
|
||||
|
||||
|
||||
@hookimpl
|
||||
def register_events():
|
||||
return [BanUserEvent, UnbanUserEvent]
|
||||
|
||||
The plugin can then call ``datasette.track_event(...)`` to send a ``ban-user`` event:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
await datasette.track_event(
|
||||
BanUserEvent(user={"id": 1, "username": "cleverbot"})
|
||||
)
|
||||
|
|
|
|||
|
|
@ -228,6 +228,15 @@ If you run ``datasette plugins --all`` it will include default plugins that ship
|
|||
"skip_csrf"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "datasette.events",
|
||||
"static": false,
|
||||
"templates": false,
|
||||
"version": null,
|
||||
"hooks": [
|
||||
"register_events"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "datasette.facets",
|
||||
"static": false,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue