Spaces:
Runtime error
Runtime error
| """ | |
| SQLAlchemy Database Models for Enterprise CX AI Agent | |
| """ | |
| from sqlalchemy import ( | |
| Column, Integer, String, Text, Float, Boolean, DateTime, ForeignKey, | |
| UniqueConstraint, Index, Date | |
| ) | |
| from sqlalchemy.ext.declarative import declarative_base | |
| from sqlalchemy.orm import relationship | |
| from datetime import datetime | |
| import json | |
| Base = declarative_base() | |
| class Company(Base): | |
| __tablename__ = 'companies' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| name = Column(String, nullable=False) | |
| domain = Column(String, unique=True) | |
| industry = Column(String) | |
| size = Column(String) | |
| revenue = Column(String) | |
| location = Column(String) | |
| description = Column(Text) | |
| pain_points = Column(Text) # JSON | |
| website = Column(String) | |
| linkedin_url = Column(String) | |
| summary = Column(Text) # AI-generated comprehensive summary | |
| created_at = Column(DateTime, default=datetime.utcnow) | |
| updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) | |
| # Relationships | |
| contacts = relationship("Contact", back_populates="company") | |
| def to_dict(self): | |
| return { | |
| 'id': self.id, | |
| 'name': self.name, | |
| 'domain': self.domain, | |
| 'industry': self.industry, | |
| 'size': self.size, | |
| 'revenue': self.revenue, | |
| 'location': self.location, | |
| 'description': self.description, | |
| 'pain_points': json.loads(self.pain_points) if self.pain_points else [], | |
| 'website': self.website, | |
| 'linkedin_url': self.linkedin_url, | |
| 'summary': self.summary, | |
| 'created_at': self.created_at.isoformat() if self.created_at else None, | |
| } | |
| class ClientProfile(Base): | |
| """Stores CLIENT company profiles for email personalization""" | |
| __tablename__ = 'client_profiles' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| name = Column(String, nullable=False, unique=True) | |
| website = Column(String) | |
| domain = Column(String) | |
| description = Column(Text) | |
| industry = Column(String) | |
| # What they offer | |
| offerings = Column(Text) # JSON list of offerings/products/services | |
| value_propositions = Column(Text) # JSON list of value props/benefits | |
| target_customers = Column(Text) # JSON list of who they serve | |
| use_cases = Column(Text) # JSON list of use cases | |
| differentiators = Column(Text) # JSON list of what makes them unique | |
| summary = Column(Text) # AI-generated comprehensive summary for personalization | |
| # Metadata | |
| created_at = Column(DateTime, default=datetime.utcnow) | |
| updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) | |
| last_researched_at = Column(DateTime, default=datetime.utcnow) | |
| def to_dict(self): | |
| return { | |
| 'id': self.id, | |
| 'name': self.name, | |
| 'website': self.website, | |
| 'domain': self.domain, | |
| 'description': self.description, | |
| 'industry': self.industry, | |
| 'offerings': json.loads(self.offerings) if self.offerings else [], | |
| 'value_propositions': json.loads(self.value_propositions) if self.value_propositions else [], | |
| 'target_customers': json.loads(self.target_customers) if self.target_customers else [], | |
| 'use_cases': json.loads(self.use_cases) if self.use_cases else [], | |
| 'differentiators': json.loads(self.differentiators) if self.differentiators else [], | |
| 'summary': self.summary, | |
| 'created_at': self.created_at.isoformat() if self.created_at else None, | |
| 'updated_at': self.updated_at.isoformat() if self.updated_at else None, | |
| 'last_researched_at': self.last_researched_at.isoformat() if self.last_researched_at else None, | |
| } | |
| class Contact(Base): | |
| __tablename__ = 'contacts' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| company_id = Column(Integer, ForeignKey('companies.id', ondelete='SET NULL')) | |
| first_name = Column(String) | |
| last_name = Column(String) | |
| email = Column(String, unique=True, nullable=False) | |
| phone = Column(String) | |
| job_title = Column(String) | |
| department = Column(String) | |
| seniority_level = Column(String) | |
| linkedin_url = Column(String) | |
| twitter_url = Column(String) | |
| location = Column(String) | |
| timezone = Column(String) | |
| # Scoring | |
| fit_score = Column(Float, default=0.0) | |
| engagement_score = Column(Float, default=0.0) | |
| intent_score = Column(Float, default=0.0) | |
| overall_score = Column(Float, default=0.0) | |
| # Status & Lifecycle | |
| status = Column(String, default='new') | |
| lifecycle_stage = Column(String, default='lead') | |
| # Tracking | |
| source = Column(String) | |
| first_contacted_at = Column(DateTime) | |
| last_contacted_at = Column(DateTime) | |
| last_activity_at = Column(DateTime) | |
| # Metadata | |
| tags = Column(Text) # JSON | |
| notes = Column(Text) | |
| custom_fields = Column(Text) # JSON | |
| is_suppressed = Column(Boolean, default=False) | |
| suppression_reason = Column(String) | |
| created_at = Column(DateTime, default=datetime.utcnow) | |
| updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) | |
| # Relationships | |
| company = relationship("Company", back_populates="contacts") | |
| campaign_associations = relationship("CampaignContact", back_populates="contact") | |
| email_activities = relationship("EmailActivity", back_populates="contact") | |
| meetings = relationship("Meeting", back_populates="contact") | |
| activities = relationship("Activity", back_populates="contact") | |
| def full_name(self): | |
| return f"{self.first_name or ''} {self.last_name or ''}".strip() | |
| def to_dict(self): | |
| return { | |
| 'id': self.id, | |
| 'company_id': self.company_id, | |
| 'company_name': self.company.name if self.company else None, | |
| 'first_name': self.first_name, | |
| 'last_name': self.last_name, | |
| 'full_name': self.full_name, | |
| 'email': self.email, | |
| 'phone': self.phone, | |
| 'job_title': self.job_title, | |
| 'department': self.department, | |
| 'seniority_level': self.seniority_level, | |
| 'linkedin_url': self.linkedin_url, | |
| 'fit_score': self.fit_score, | |
| 'engagement_score': self.engagement_score, | |
| 'overall_score': self.overall_score, | |
| 'status': self.status, | |
| 'lifecycle_stage': self.lifecycle_stage, | |
| 'source': self.source, | |
| 'tags': json.loads(self.tags) if self.tags else [], | |
| 'created_at': self.created_at.isoformat() if self.created_at else None, | |
| 'last_activity_at': self.last_activity_at.isoformat() if self.last_activity_at else None, | |
| } | |
| class Campaign(Base): | |
| __tablename__ = 'campaigns' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| name = Column(String, nullable=False) | |
| description = Column(Text) | |
| status = Column(String, default='draft') | |
| # Targeting | |
| target_industries = Column(Text) # JSON | |
| target_company_sizes = Column(Text) # JSON | |
| target_locations = Column(Text) # JSON | |
| target_job_titles = Column(Text) # JSON | |
| # Configuration | |
| sequence_id = Column(Integer, ForeignKey('sequences.id', ondelete='SET NULL')) | |
| goal_contacts = Column(Integer) | |
| goal_response_rate = Column(Float) | |
| goal_meetings = Column(Integer) | |
| # Tracking | |
| contacts_discovered = Column(Integer, default=0) | |
| contacts_enriched = Column(Integer, default=0) | |
| contacts_scored = Column(Integer, default=0) | |
| contacts_contacted = Column(Integer, default=0) | |
| contacts_responded = Column(Integer, default=0) | |
| meetings_booked = Column(Integer, default=0) | |
| # Dates | |
| started_at = Column(DateTime) | |
| completed_at = Column(DateTime) | |
| created_at = Column(DateTime, default=datetime.utcnow) | |
| updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) | |
| created_by = Column(String) | |
| # Relationships | |
| sequence = relationship("Sequence", back_populates="campaigns") | |
| contact_associations = relationship("CampaignContact", back_populates="campaign") | |
| email_activities = relationship("EmailActivity", back_populates="campaign") | |
| meetings = relationship("Meeting", back_populates="campaign") | |
| activities = relationship("Activity", back_populates="campaign") | |
| def to_dict(self): | |
| return { | |
| 'id': self.id, | |
| 'name': self.name, | |
| 'description': self.description, | |
| 'status': self.status, | |
| 'sequence_id': self.sequence_id, | |
| 'sequence_name': self.sequence.name if self.sequence else None, | |
| 'goal_contacts': self.goal_contacts, | |
| 'goal_response_rate': self.goal_response_rate, | |
| 'goal_meetings': self.goal_meetings, | |
| 'contacts_discovered': self.contacts_discovered, | |
| 'contacts_enriched': self.contacts_enriched, | |
| 'contacts_contacted': self.contacts_contacted, | |
| 'contacts_responded': self.contacts_responded, | |
| 'meetings_booked': self.meetings_booked, | |
| 'started_at': self.started_at.isoformat() if self.started_at else None, | |
| 'created_at': self.created_at.isoformat() if self.created_at else None, | |
| } | |
| class CampaignContact(Base): | |
| __tablename__ = 'campaign_contacts' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| campaign_id = Column(Integer, ForeignKey('campaigns.id', ondelete='CASCADE'), nullable=False) | |
| contact_id = Column(Integer, ForeignKey('contacts.id', ondelete='CASCADE'), nullable=False) | |
| stage = Column(String, default='discovery') | |
| stage_updated_at = Column(DateTime, default=datetime.utcnow) | |
| added_at = Column(DateTime, default=datetime.utcnow) | |
| notes = Column(Text) | |
| # Relationships | |
| campaign = relationship("Campaign", back_populates="contact_associations") | |
| contact = relationship("Contact", back_populates="campaign_associations") | |
| __table_args__ = ( | |
| UniqueConstraint('campaign_id', 'contact_id', name='uq_campaign_contact'), | |
| ) | |
| class Sequence(Base): | |
| __tablename__ = 'sequences' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| name = Column(String, nullable=False) | |
| description = Column(Text) | |
| category = Column(String, default='outbound') | |
| is_active = Column(Boolean, default=True) | |
| is_template = Column(Boolean, default=False) | |
| created_at = Column(DateTime, default=datetime.utcnow) | |
| updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) | |
| created_by = Column(String) | |
| # Relationships | |
| emails = relationship("SequenceEmail", back_populates="sequence", order_by="SequenceEmail.step_number") | |
| campaigns = relationship("Campaign", back_populates="sequence") | |
| def to_dict(self): | |
| return { | |
| 'id': self.id, | |
| 'name': self.name, | |
| 'description': self.description, | |
| 'category': self.category, | |
| 'is_active': self.is_active, | |
| 'email_count': len(self.emails) if self.emails else 0, | |
| 'created_at': self.created_at.isoformat() if self.created_at else None, | |
| } | |
| class SequenceEmail(Base): | |
| __tablename__ = 'sequence_emails' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| sequence_id = Column(Integer, ForeignKey('sequences.id', ondelete='CASCADE'), nullable=False) | |
| step_number = Column(Integer, nullable=False) | |
| wait_days = Column(Integer, default=0) | |
| subject = Column(String, nullable=False) | |
| body = Column(Text, nullable=False) | |
| send_time_preference = Column(String) | |
| created_at = Column(DateTime, default=datetime.utcnow) | |
| # Relationships | |
| sequence = relationship("Sequence", back_populates="emails") | |
| email_activities = relationship("EmailActivity", back_populates="sequence_email") | |
| __table_args__ = ( | |
| UniqueConstraint('sequence_id', 'step_number', name='uq_sequence_step'), | |
| ) | |
| def to_dict(self): | |
| return { | |
| 'id': self.id, | |
| 'sequence_id': self.sequence_id, | |
| 'step_number': self.step_number, | |
| 'wait_days': self.wait_days, | |
| 'subject': self.subject, | |
| 'body': self.body, | |
| 'send_time_preference': self.send_time_preference, | |
| } | |
| class EmailActivity(Base): | |
| __tablename__ = 'email_activities' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| contact_id = Column(Integer, ForeignKey('contacts.id', ondelete='CASCADE'), nullable=False) | |
| campaign_id = Column(Integer, ForeignKey('campaigns.id', ondelete='SET NULL')) | |
| sequence_email_id = Column(Integer, ForeignKey('sequence_emails.id', ondelete='SET NULL')) | |
| type = Column(String, nullable=False) # sent, opened, clicked, replied, etc. | |
| subject = Column(String) | |
| preview = Column(Text) | |
| link_url = Column(String) | |
| meta_data = Column(Text) # JSON | |
| occurred_at = Column(DateTime, default=datetime.utcnow) | |
| # Relationships | |
| contact = relationship("Contact", back_populates="email_activities") | |
| campaign = relationship("Campaign", back_populates="email_activities") | |
| sequence_email = relationship("SequenceEmail", back_populates="email_activities") | |
| class Meeting(Base): | |
| __tablename__ = 'meetings' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| contact_id = Column(Integer, ForeignKey('contacts.id', ondelete='CASCADE'), nullable=False) | |
| campaign_id = Column(Integer, ForeignKey('campaigns.id', ondelete='SET NULL')) | |
| title = Column(String, nullable=False) | |
| description = Column(Text) | |
| scheduled_at = Column(DateTime, nullable=False) | |
| duration_minutes = Column(Integer, default=30) | |
| meeting_url = Column(String) | |
| location = Column(String) | |
| status = Column(String, default='scheduled') | |
| outcome = Column(String) | |
| notes = Column(Text) | |
| created_at = Column(DateTime, default=datetime.utcnow) | |
| updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) | |
| # Relationships | |
| contact = relationship("Contact", back_populates="meetings") | |
| campaign = relationship("Campaign", back_populates="meetings") | |
| activities = relationship("Activity", back_populates="meeting") | |
| def to_dict(self): | |
| return { | |
| 'id': self.id, | |
| 'contact_id': self.contact_id, | |
| 'contact_name': self.contact.full_name if self.contact else None, | |
| 'campaign_id': self.campaign_id, | |
| 'title': self.title, | |
| 'scheduled_at': self.scheduled_at.isoformat() if self.scheduled_at else None, | |
| 'duration_minutes': self.duration_minutes, | |
| 'meeting_url': self.meeting_url, | |
| 'status': self.status, | |
| 'outcome': self.outcome, | |
| } | |
| class Activity(Base): | |
| __tablename__ = 'activities' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| contact_id = Column(Integer, ForeignKey('contacts.id', ondelete='CASCADE')) | |
| campaign_id = Column(Integer, ForeignKey('campaigns.id', ondelete='SET NULL')) | |
| meeting_id = Column(Integer, ForeignKey('meetings.id', ondelete='SET NULL')) | |
| type = Column(String, nullable=False) | |
| description = Column(Text) | |
| meta_data = Column(Text) # JSON | |
| performed_by = Column(String) | |
| occurred_at = Column(DateTime, default=datetime.utcnow) | |
| # Relationships | |
| contact = relationship("Contact", back_populates="activities") | |
| campaign = relationship("Campaign", back_populates="activities") | |
| meeting = relationship("Meeting", back_populates="activities") | |
| class ABTest(Base): | |
| __tablename__ = 'ab_tests' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| campaign_id = Column(Integer, ForeignKey('campaigns.id', ondelete='CASCADE'), nullable=False) | |
| sequence_id = Column(Integer, ForeignKey('sequences.id', ondelete='CASCADE'), nullable=False) | |
| name = Column(String, nullable=False) | |
| description = Column(Text) | |
| test_type = Column(String, nullable=False) | |
| variant_a = Column(Text, nullable=False) # JSON | |
| variant_b = Column(Text, nullable=False) # JSON | |
| winner = Column(String) | |
| status = Column(String, default='running') | |
| started_at = Column(DateTime, default=datetime.utcnow) | |
| completed_at = Column(DateTime) | |
| # Relationships | |
| results = relationship("ABTestResult", back_populates="ab_test") | |
| class ABTestResult(Base): | |
| __tablename__ = 'ab_test_results' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| ab_test_id = Column(Integer, ForeignKey('ab_tests.id', ondelete='CASCADE'), nullable=False) | |
| variant = Column(String, nullable=False) | |
| emails_sent = Column(Integer, default=0) | |
| emails_delivered = Column(Integer, default=0) | |
| emails_opened = Column(Integer, default=0) | |
| emails_clicked = Column(Integer, default=0) | |
| emails_replied = Column(Integer, default=0) | |
| meetings_booked = Column(Integer, default=0) | |
| # Relationships | |
| ab_test = relationship("ABTest", back_populates="results") | |
| __table_args__ = ( | |
| UniqueConstraint('ab_test_id', 'variant', name='uq_ab_test_variant'), | |
| ) | |
| class Template(Base): | |
| __tablename__ = 'templates' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| name = Column(String, nullable=False) | |
| category = Column(String) | |
| subject = Column(String, nullable=False) | |
| body = Column(Text, nullable=False) | |
| variables = Column(Text) # JSON | |
| is_active = Column(Boolean, default=True) | |
| usage_count = Column(Integer, default=0) | |
| created_at = Column(DateTime, default=datetime.utcnow) | |
| updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) | |
| class AnalyticsSnapshot(Base): | |
| __tablename__ = 'analytics_snapshots' | |
| id = Column(Integer, primary_key=True, autoincrement=True) | |
| campaign_id = Column(Integer, ForeignKey('campaigns.id', ondelete='CASCADE')) | |
| date = Column(Date, nullable=False) | |
| hour = Column(Integer) | |
| # Metrics | |
| contacts_discovered = Column(Integer, default=0) | |
| contacts_enriched = Column(Integer, default=0) | |
| emails_sent = Column(Integer, default=0) | |
| emails_opened = Column(Integer, default=0) | |
| emails_clicked = Column(Integer, default=0) | |
| emails_replied = Column(Integer, default=0) | |
| meetings_booked = Column(Integer, default=0) | |
| # Rates | |
| open_rate = Column(Float, default=0.0) | |
| click_rate = Column(Float, default=0.0) | |
| response_rate = Column(Float, default=0.0) | |
| meeting_rate = Column(Float, default=0.0) | |
| created_at = Column(DateTime, default=datetime.utcnow) | |
| __table_args__ = ( | |
| UniqueConstraint('campaign_id', 'date', 'hour', name='uq_analytics_snapshot'), | |
| ) | |
| class Setting(Base): | |
| __tablename__ = 'settings' | |
| key = Column(String, primary_key=True) | |
| value = Column(String, nullable=False) | |
| description = Column(Text) | |
| updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) | |