Files
TapTrack-Hub/backend/app/tasks/onboarding.py
kevin-asprec 9ef8f1a421 feat(phases-10-15): complete TapTrack Hub v1.0
Phase 10 — Support Ticket System:
- tickets.py router: SLA status (on_track/at_risk/breached/responded), email
  notifications on create+reply via background threads, school_name in list,
  priority filter, bulk-close endpoint
- tasks/tickets.py: escalate_stale Celery task (48h→high, 72h no reply→urgent)
- worker.py: escalate_stale scheduled every hour
- templates/email/ticket_notification.html: HTML ticket notification email
- TicketsPage.vue: status tabs, SLA badge, priority badge, school name column,
  checkbox bulk-close, pagination
- TicketDetailPage.vue: inline priority/status/assignee selectors, SLA timer,
  internal note lock icon, closed-ticket guard

Phase 11 — Monthly Report Generation:
- models/report.py: MonthlyReport + SchoolMonthlyStats ORM models
- tasks/reports.py: send_monthly_reports enhanced with SMS stats, attendance
  data, invoice summary, stores MonthlyReport record per school per month

Phase 12 — On-Prem Monthly Report Pull:
- tasks/reports.py: pull_monthly_stats task — httpx GET to each school's
  hub_base_url, upserts SchoolMonthlyStats; runs 1st at 5am
- worker.py: pull_monthly_stats scheduled 1st at 5am

Phase 13 — Feature Flags + Suspension:
- models/school.py: hub_base_url, feature_overrides (JSON), onboarding_completed_at
- routers/schools.py: PUT /{id}/feature-overrides endpoint
- routers/sync.py: _tier_features() merges school.feature_overrides into poll config

Phase 14 — Onboarding Wizard + Welcome Email:
- tasks/onboarding.py: send_welcome_email Celery task with license key
- routers/schools.py: auto-trigger welcome email on POST /schools,
  POST /{id}/activate (status→active + onboarding_completed_at),
  POST /{id}/resend-welcome

Phase 15 — UX Polish + Ops Tools:
- routers/search.py: GET /api/search?q= (schools + invoices + tickets, 5 each)
- routers/audit.py: GET /api/audit-logs (paginated, filterable)
- AppLayout.vue: global search bar with debounced dropdown, result navigation
- AuditLogsPage.vue: new page with filter + pagination
- AppSidebar.vue: Audit Logs nav item added
- router/index.ts: /audit-logs route
- api.ts: globalSearch, getAuditLogs, activateSchool, resendWelcomeEmail,
  updateFeatureOverrides, bulkCloseTickets

Deployment:
- docker-compose.yml: x-backend-env anchor (DRY), PDF_DIR env var,
  seed service (one-shot python seed.py on first boot)
- migrations/003_phases11_15.py: monthly_reports, school_monthly_stats tables
  + schools hub_base_url/feature_overrides/onboarding_completed_at columns
2026-03-16 14:28:52 +08:00

57 lines
2.2 KiB
Python

"""Celery task: welcome email on school onboarding."""
import logging
from app.worker import celery_app
logger = logging.getLogger(__name__)
@celery_app.task(name="onboarding.send_welcome_email")
def send_welcome_email(school_id: str):
"""Send welcome email with license key to new school."""
import os
from sqlalchemy import create_engine, select
from sqlalchemy.orm import sessionmaker
from app.models.school import School
from app.models.license import License
from app.services.email import send_email
db_url = os.getenv("DATABASE_URL", "postgresql://postgres:postgres@db:5432/taptrack_hub")
engine = create_engine(db_url.replace("postgresql+asyncpg://", "postgresql://"), pool_pre_ping=True)
db = sessionmaker(bind=engine)()
try:
school = db.get(School, school_id)
if not school:
return
lic = db.execute(select(License).where(License.school_id == school_id)).scalar_one_or_none()
email_to = school.billing_email or school.contact_email
if not email_to:
logger.warning("No email for school %s — skipping welcome", school.name)
return
hub_url = os.getenv("HUB_BASE_URL", "http://localhost:8090")
license_key = lic.key if lic else "Contact support to get your license key"
plain = (
f"Welcome to TapTrack Hub!\n\n"
f"Your school '{school.name}' has been registered.\n\n"
f"License Key: {license_key}\n\n"
f"Setup instructions:\n"
f"1. Log in to TapTrack Hub: {hub_url}/login\n"
f"2. Enter your license key in TapTrack's Hub settings\n"
f"3. Restart the TapTrack service\n\n"
f"Support: support@taptrack.io\n\nTapTrack Hub Team"
)
send_email(
to=email_to,
subject=f"Welcome to TapTrack Hub — {school.name}",
body=plain,
email_type="welcome",
school_id=school.id,
)
logger.info("Welcome email sent to %s for school %s", email_to, school.name)
except Exception as e:
logger.error("send_welcome_email error for %s: %s", school_id, e)
finally:
db.close()
engine.dispose()