04/13/2026 Fixed email ingres function

This commit is contained in:
2026-04-13 18:18:15 -04:00
parent 7f025c613f
commit cacc92636a
4 changed files with 257 additions and 59 deletions
+32 -51
View File
@@ -182,39 +182,47 @@ def create_app(config_name=None):
_seed_settings()
_seed_ticket_templates()
# ── Email ingestion background scheduler ────────────────────────────────────
_start_email_ingestion_scheduler(app)
# ── SLA background scheduler ──────────────────────────────────────────────
# APScheduler runs inside the gunicorn worker process (single-worker
# eventlet setup), so no cross-process coordination is needed.
# The scheduler is only started in the main process — not during Flask's
# reloader child process — to prevent duplicate job execution.
_start_sla_scheduler(app)
# ── Background schedulers (SLA + Email Ingestion) ───────────────────────────
# Both jobs share a single BackgroundScheduler instance to avoid duplicate
# thread pools and simplify startup/shutdown. The scheduler is only started
# in the main process — not during Flask's reloader child — to prevent
# duplicate job execution.
_start_background_schedulers(app)
return app
def _start_sla_scheduler(app):
"""Start the APScheduler background job that checks for SLA breaches.
Safe to call on every app startup: the scheduler is idempotent and
jobstore deduplication prevents double-registration on hot-reloads.
Under gunicorn with preload_app=False each worker calls create_app()
once, so there is exactly one scheduler per worker.
def _start_background_schedulers(app):
"""Start a single shared APScheduler instance hosting both the SLA breach
check and the inbound email ingestion job.
A single BackgroundScheduler is used rather than two separate instances to
avoid creating duplicate thread pools under eventlet. Both jobs are
registered here so they share one daemon thread pool.
The scheduler is skipped in Flask's reloader subprocess (identified by the
WERKZEUG_RUN_MAIN env var) to prevent double-registration on hot-reloads.
Under gunicorn with preload_app=False, each worker calls create_app() once,
giving exactly one scheduler per worker.
"""
# Skip inside Flask's reloader subprocess (identified by the env var it sets).
import os as _os
if _os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
# Reloader is active — the child process will start its own scheduler.
return
return # skip reloader child process
try:
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.interval import IntervalTrigger
from app.services.sla_service import check_sla_breaches
from app.services.email_ingestion_service import check_inbound_email
from app.models import SystemSetting
with app.app_context():
email_interval = int(SystemSetting.get('email_ingestion_interval', '5') or '5')
scheduler = BackgroundScheduler(daemon=True)
# SLA breach check — every 30 minutes
scheduler.add_job(
func = check_sla_breaches,
trigger = IntervalTrigger(minutes=30),
@@ -223,49 +231,22 @@ def _start_sla_scheduler(app):
replace_existing = True,
args = [app],
)
scheduler.start()
app.logger.info('[SLA] APScheduler started — SLA breach check every 30 minutes')
except Exception as exc:
app.logger.error(f'[SLA] Failed to start APScheduler: {exc}')
def _start_email_ingestion_scheduler(app):
"""Start the APScheduler job that polls the inbound mailbox for new emails.
The interval is read from SystemSetting at job creation time (default 5 min).
The job is a no-op when email_ingestion_enabled = '0', so it is safe to
always register it — no credentials are required until the admin enables it.
"""
import os as _os
if _os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
return # skip reloader child process
try:
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.interval import IntervalTrigger
from app.services.email_ingestion_service import check_inbound_email
from app.models import SystemSetting
with app.app_context():
interval = int(SystemSetting.get('email_ingestion_interval', '5') or '5')
scheduler = BackgroundScheduler(daemon=True)
# Inbound email ingestion — interval from SystemSetting
scheduler.add_job(
func = check_inbound_email,
trigger = IntervalTrigger(minutes=interval),
trigger = IntervalTrigger(minutes=email_interval),
id = 'email_ingest',
name = 'Inbound Email Ingestion',
replace_existing = True,
args = [app],
)
scheduler.start()
app.logger.info(
f'[EMAIL INGEST] APScheduler started — polling every {interval} minute(s)'
)
app.logger.info('[SCHEDULER] APScheduler started — SLA check every 30 min, '
f'email ingestion every {email_interval} min')
except Exception as exc:
app.logger.error(f'[EMAIL INGEST] Failed to start scheduler: {exc}')
app.logger.error(f'[SCHEDULER] Failed to start APScheduler: {exc}')
def _seed_ticket_templates():