Files
orion/app/modules/prospecting/models/campaign.py
Samir Boulahtit 6d6eba75bf
Some checks failed
CI / pytest (push) Failing after 48m31s
CI / docs (push) Has been skipped
CI / deploy (push) Has been skipped
CI / ruff (push) Successful in 11s
CI / validate (push) Successful in 23s
CI / dependency-scanning (push) Successful in 28s
feat(prospecting): add complete prospecting module for lead discovery and scoring
Migrates scanning pipeline from marketing-.lu-domains app into Orion module.
Supports digital (domain scan) and offline (manual capture) lead channels
with enrichment, scoring, campaign management, and interaction tracking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 00:59:47 +01:00

84 lines
2.4 KiB
Python

# app/modules/prospecting/models/campaign.py
"""
Campaign templates and send tracking.
Templates are tailored by lead type (no_website, bad_website, etc.)
with support for multiple languages and delivery channels.
"""
import enum
from sqlalchemy import (
Boolean,
Column,
DateTime,
Enum,
ForeignKey,
Integer,
String,
Text,
)
from app.core.database import Base
from models.database.base import TimestampMixin
class LeadType(str, enum.Enum):
NO_WEBSITE = "no_website"
BAD_WEBSITE = "bad_website"
GMAIL_ONLY = "gmail_only"
SECURITY_ISSUES = "security_issues"
PERFORMANCE_ISSUES = "performance_issues"
OUTDATED_CMS = "outdated_cms"
GENERAL = "general"
class CampaignChannel(str, enum.Enum):
EMAIL = "email"
LETTER = "letter"
PHONE_SCRIPT = "phone_script"
class CampaignSendStatus(str, enum.Enum):
DRAFT = "draft"
SENT = "sent"
DELIVERED = "delivered"
OPENED = "opened"
BOUNCED = "bounced"
REPLIED = "replied"
class CampaignTemplate(Base, TimestampMixin):
"""A reusable marketing campaign template."""
__tablename__ = "campaign_templates"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(255), nullable=False)
lead_type = Column(Enum(LeadType), nullable=False)
channel = Column(Enum(CampaignChannel), nullable=False, default=CampaignChannel.EMAIL)
language = Column(String(5), nullable=False, default="fr")
subject_template = Column(String(500), nullable=True)
body_template = Column(Text, nullable=False)
is_active = Column(Boolean, nullable=False, default=True)
class CampaignSend(Base, TimestampMixin):
"""A record of a campaign sent to a specific prospect."""
__tablename__ = "campaign_sends"
id = Column(Integer, primary_key=True, index=True)
template_id = Column(Integer, ForeignKey("campaign_templates.id", ondelete="SET NULL"), nullable=True)
prospect_id = Column(Integer, ForeignKey("prospects.id", ondelete="CASCADE"), nullable=False, index=True)
channel = Column(Enum(CampaignChannel), nullable=False)
rendered_subject = Column(String(500), nullable=True)
rendered_body = Column(Text, nullable=True)
status = Column(Enum(CampaignSendStatus), nullable=False, default=CampaignSendStatus.DRAFT)
sent_at = Column(DateTime, nullable=True)
sent_by_user_id = Column(Integer, nullable=True)