mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Adds sample plugin which responds to triggered events
- Adds some example trigger events for the "Part" model
This commit is contained in:
parent
a604d85f0f
commit
04d25a60b0
@ -60,6 +60,8 @@ import common.models
|
|||||||
|
|
||||||
import part.settings as part_settings
|
import part.settings as part_settings
|
||||||
|
|
||||||
|
from plugin.events import trigger_event
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger("inventree")
|
logger = logging.getLogger("inventree")
|
||||||
|
|
||||||
@ -1980,10 +1982,10 @@ class Part(MPTTModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def attachment_count(self):
|
def attachment_count(self):
|
||||||
""" Count the number of attachments for this part.
|
"""
|
||||||
|
Count the number of attachments for this part.
|
||||||
If the part is a variant of a template part,
|
If the part is a variant of a template part,
|
||||||
include the number of attachments for the template part.
|
include the number of attachments for the template part.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.part_attachments.count()
|
return self.part_attachments.count()
|
||||||
@ -2181,7 +2183,11 @@ def after_save_part(sender, instance: Part, created, **kwargs):
|
|||||||
Function to be executed after a Part is saved
|
Function to be executed after a Part is saved
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not created:
|
trigger_event('part.saved', part_id=instance.pk)
|
||||||
|
|
||||||
|
if created:
|
||||||
|
trigger_event('part.created', part_id=instance.pk)
|
||||||
|
else:
|
||||||
# Check part stock only if we are *updating* the part (not creating it)
|
# Check part stock only if we are *updating* the part (not creating it)
|
||||||
|
|
||||||
# Run this check in the background
|
# Run this check in the background
|
||||||
|
@ -189,10 +189,10 @@ class EventMixin:
|
|||||||
which provide pairs of 'event':'function'
|
which provide pairs of 'event':'function'
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
Events are called by name, and based on the django signal nomenclature,
|
Events are called by name, and based on the django signal nomenclature,
|
||||||
e.g. 'part.pre_save'
|
e.g. 'part.pre_save'
|
||||||
|
|
||||||
Receiving functions must be prototyped to match the 'event' they receive.
|
Receiving functions must be prototyped to match the 'event' they receive.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
@ -8,11 +8,14 @@ from __future__ import unicode_literals
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
from common.models import InvenTreeSetting
|
from common.models import InvenTreeSetting
|
||||||
|
|
||||||
from InvenTree.tasks import offload_task
|
from InvenTree.tasks import offload_task
|
||||||
|
|
||||||
|
from plugin.registry import plugin_registry
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger('inventree')
|
logger = logging.getLogger('inventree')
|
||||||
|
|
||||||
@ -38,12 +41,30 @@ def trigger_event(event, *args, **kwargs):
|
|||||||
def process_event(event, *args, **kwargs):
|
def process_event(event, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Respond to a triggered event.
|
Respond to a triggered event.
|
||||||
|
|
||||||
This function is run by the background worker process.
|
This function is run by the background worker process.
|
||||||
|
|
||||||
|
This function may queue multiple functions to be handled by the background worker.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.info(f"Processing event '{event}'")
|
logger.info(f"Processing event '{event}'")
|
||||||
|
|
||||||
# Determine if there are any plugins which are interested in responding
|
# Determine if there are any plugins which are interested in responding
|
||||||
if settings.PLUGIN_TESTING or InvenTreeSetting.get_setting('ENABLE_PLUGINS_EVENTS'):
|
if settings.PLUGIN_TESTING or InvenTreeSetting.get_setting('ENABLE_PLUGINS_EVENTS'):
|
||||||
pass
|
|
||||||
|
# Run atomically, to ensure that either *all* tasks are registered, or *none*
|
||||||
|
with transaction.atomic():
|
||||||
|
for slug, callbacks in plugin_registry.mixins_events.items():
|
||||||
|
# slug = plugin slug
|
||||||
|
# callbacks = list of (event, function) tuples
|
||||||
|
|
||||||
|
for _event, _func in callbacks:
|
||||||
|
if _event == event:
|
||||||
|
|
||||||
|
logger.info(f"Running task '{_func}' for plugin '{slug}'")
|
||||||
|
|
||||||
|
offload_task(
|
||||||
|
_func,
|
||||||
|
*args,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
@ -63,3 +63,15 @@ class InvenTreePlugin():
|
|||||||
raise error
|
raise error
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
|
def is_active(self):
|
||||||
|
"""
|
||||||
|
Return True if this plugin is currently active
|
||||||
|
"""
|
||||||
|
|
||||||
|
cfg = self.plugin_config()
|
||||||
|
|
||||||
|
if cfg:
|
||||||
|
return cfg.active
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
@ -56,8 +56,10 @@ class PluginsRegistry:
|
|||||||
|
|
||||||
# integration specific
|
# integration specific
|
||||||
self.installed_apps = [] # Holds all added plugin_paths
|
self.installed_apps = [] # Holds all added plugin_paths
|
||||||
|
|
||||||
# mixins
|
# mixins
|
||||||
self.mixins_settings = {}
|
self.mixins_settings = {}
|
||||||
|
self.mixins_events = {}
|
||||||
|
|
||||||
# region public plugin functions
|
# region public plugin functions
|
||||||
def load_plugins(self):
|
def load_plugins(self):
|
||||||
@ -265,6 +267,7 @@ class PluginsRegistry:
|
|||||||
logger.info(f'Found {len(plugins)} active plugins')
|
logger.info(f'Found {len(plugins)} active plugins')
|
||||||
|
|
||||||
self.activate_integration_settings(plugins)
|
self.activate_integration_settings(plugins)
|
||||||
|
self.activate_integration_events(plugins)
|
||||||
self.activate_integration_schedule(plugins)
|
self.activate_integration_schedule(plugins)
|
||||||
self.activate_integration_app(plugins, force_reload=force_reload)
|
self.activate_integration_app(plugins, force_reload=force_reload)
|
||||||
|
|
||||||
@ -275,6 +278,7 @@ class PluginsRegistry:
|
|||||||
|
|
||||||
self.deactivate_integration_app()
|
self.deactivate_integration_app()
|
||||||
self.deactivate_integration_schedule()
|
self.deactivate_integration_schedule()
|
||||||
|
self.deactivate_integration_events()
|
||||||
self.deactivate_integration_settings()
|
self.deactivate_integration_settings()
|
||||||
|
|
||||||
def activate_integration_settings(self, plugins):
|
def activate_integration_settings(self, plugins):
|
||||||
@ -299,6 +303,41 @@ class PluginsRegistry:
|
|||||||
# clear cache
|
# clear cache
|
||||||
self.mixins_settings = {}
|
self.mixins_settings = {}
|
||||||
|
|
||||||
|
def activate_integration_events(self, plugins):
|
||||||
|
"""
|
||||||
|
Activate triggered events mixin for applicable plugins
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.info('Activating plugin events')
|
||||||
|
|
||||||
|
from common.models import InvenTreeSetting
|
||||||
|
|
||||||
|
self.mixins_events = {}
|
||||||
|
|
||||||
|
event_count = 0
|
||||||
|
|
||||||
|
if settings.PLUGIN_TESTING or InvenTreeSetting.get_setting('ENABLE_PLUGINS_EVENTS'):
|
||||||
|
|
||||||
|
for slug, plugin in plugins:
|
||||||
|
|
||||||
|
if plugin.mixin_enabled('events'):
|
||||||
|
config = plugin.plugin_config()
|
||||||
|
|
||||||
|
# Only activate events for plugins which are enabled
|
||||||
|
if config and config.active:
|
||||||
|
self.mixins_events[slug] = plugin.events
|
||||||
|
|
||||||
|
event_count += len(plugin.events)
|
||||||
|
|
||||||
|
if event_count > 0:
|
||||||
|
logger.info(f"Registered callbacks for {event_count} events")
|
||||||
|
|
||||||
|
def deactivate_integration_events(self):
|
||||||
|
"""
|
||||||
|
Deactivate events for all plugins
|
||||||
|
"""
|
||||||
|
self.mixins_events = {}
|
||||||
|
|
||||||
def activate_integration_schedule(self, plugins):
|
def activate_integration_schedule(self, plugins):
|
||||||
|
|
||||||
logger.info('Activating plugin tasks')
|
logger.info('Activating plugin tasks')
|
||||||
|
29
InvenTree/plugin/samples/integration/event_sample.py
Normal file
29
InvenTree/plugin/samples/integration/event_sample.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
"""
|
||||||
|
Sample plugin which responds to events
|
||||||
|
"""
|
||||||
|
|
||||||
|
from plugin import IntegrationPluginBase
|
||||||
|
from plugin.mixins import EventMixin
|
||||||
|
|
||||||
|
|
||||||
|
def on_part_saved(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Simple function which responds to a triggered event
|
||||||
|
"""
|
||||||
|
|
||||||
|
part_id = kwargs['part_id']
|
||||||
|
print(f"func on_part_saved() - part: {part_id}")
|
||||||
|
|
||||||
|
|
||||||
|
class EventPlugin(EventMixin, IntegrationPluginBase):
|
||||||
|
"""
|
||||||
|
A sample plugin which provides supports for triggered events
|
||||||
|
"""
|
||||||
|
|
||||||
|
PLUGIN_NAME = "EventPlugin"
|
||||||
|
PLUGIN_SLUG = "event"
|
||||||
|
PLUGIN_TITLE = "Triggered Events"
|
||||||
|
|
||||||
|
EVENTS = [
|
||||||
|
('part.saved', 'plugin.samples.integration.event_sample.on_part_saved'),
|
||||||
|
]
|
@ -32,7 +32,7 @@ class ScheduledTaskPlugin(ScheduleMixin, IntegrationPluginBase):
|
|||||||
'hello': {
|
'hello': {
|
||||||
'func': 'plugin.samples.integration.scheduled_task.print_hello',
|
'func': 'plugin.samples.integration.scheduled_task.print_hello',
|
||||||
'schedule': 'I',
|
'schedule': 'I',
|
||||||
'minutes': 5,
|
'minutes': 45,
|
||||||
},
|
},
|
||||||
'world': {
|
'world': {
|
||||||
'func': 'plugin.samples.integration.scheduled_task.print_hello',
|
'func': 'plugin.samples.integration.scheduled_task.print_hello',
|
||||||
|
@ -90,7 +90,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td><span class='fas fa-sitemap'></span></td>
|
||||||
<td>{% trans "Installation path" %}</td>
|
<td>{% trans "Installation path" %}</td>
|
||||||
<td>{{ plugin.package_path }}</td>
|
<td>{{ plugin.package_path }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
Loading…
Reference in New Issue
Block a user