feat(prospecting): add complete prospecting module for lead discovery and scoring
Some checks failed
Some checks failed
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>
This commit is contained in:
83
app/modules/prospecting/models/campaign.py
Normal file
83
app/modules/prospecting/models/campaign.py
Normal file
@@ -0,0 +1,83 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user