85 lines
3.0 KiB
Python
85 lines
3.0 KiB
Python
# File: app/celery_app.py
|
||
|
||
import os
|
||
import json
|
||
import importlib
|
||
from celery import Celery
|
||
|
||
# 1) Create the Celery object (broker/backend will be set from our Flask config)
|
||
celery = Celery('natureinpots')
|
||
|
||
|
||
def init_celery(app):
|
||
"""
|
||
Wire up Celery to our Flask app, discover all plugin task modules
|
||
and run any tasks_init hooks.
|
||
"""
|
||
# 1. Configure broker & result backend from Flask config
|
||
celery.conf.broker_url = app.config['CELERY_BROKER_URL']
|
||
celery.conf.result_backend = app.config['CELERY_RESULT_BACKEND']
|
||
|
||
# 2. Make all tasks run inside the Flask application context
|
||
TaskBase = celery.Task
|
||
class ContextTask(TaskBase):
|
||
def __call__(self, *args, **kwargs):
|
||
with app.app_context():
|
||
return TaskBase.__call__(self, *args, **kwargs)
|
||
celery.Task = ContextTask
|
||
|
||
# 3. Discover all plugins by looking for plugin.json in plugins/
|
||
plugins_dir = os.path.join(app.root_path, '..', 'plugins')
|
||
task_modules = []
|
||
for plugin_name in sorted(os.listdir(plugins_dir)):
|
||
manifest = os.path.join(plugins_dir, plugin_name, 'plugin.json')
|
||
if not os.path.isfile(manifest):
|
||
continue
|
||
|
||
# Load plugin metadata
|
||
try:
|
||
meta = json.load(open(manifest))
|
||
except Exception:
|
||
app.logger.error(f"[Celery] Failed to load plugin.json for '{plugin_name}'")
|
||
continue
|
||
|
||
# a) Gather task modules (string or dict or list)
|
||
tasks_cfg = meta.get('tasks')
|
||
if isinstance(tasks_cfg, str):
|
||
task_modules.append(tasks_cfg)
|
||
elif isinstance(tasks_cfg, dict) and tasks_cfg.get('module'):
|
||
task_modules.append(tasks_cfg['module'])
|
||
elif isinstance(tasks_cfg, list):
|
||
for entry in tasks_cfg:
|
||
if isinstance(entry, str):
|
||
task_modules.append(entry)
|
||
elif isinstance(entry, dict) and entry.get('module'):
|
||
task_modules.append(entry['module'])
|
||
|
||
# b) Run any tasks_init hooks (e.g. to register schedules)
|
||
for hook in meta.get('tasks_init', []):
|
||
module_name = hook.get('module')
|
||
fn_name = hook.get('callable')
|
||
if not module_name or not fn_name:
|
||
continue
|
||
try:
|
||
m = importlib.import_module(module_name)
|
||
fn = getattr(m, fn_name)
|
||
# pass the Celery instance so they can register schedules, etc.
|
||
fn(celery)
|
||
except Exception as e:
|
||
app.logger.error(f"[Celery] Failed tasks_init for '{plugin_name}': {e}")
|
||
|
||
# 4. Autodiscover all gathered task modules
|
||
if task_modules:
|
||
celery.autodiscover_tasks(task_modules)
|
||
app.logger.info(f"[Celery] Autodiscovered tasks in: {task_modules}")
|
||
|
||
return celery
|
||
|
||
|
||
# 5) Immediately bootstrap Celery with our Flask app so that
|
||
# any `celery -A app.celery_app:celery worker --beat` invocation
|
||
# will pick up your plugins’ tasks and schedules.
|
||
from app import create_app
|
||
_flask_app = create_app()
|
||
init_celery(_flask_app)
|