Finalize SuperOffice production migration and multi-campaign architecture (v1.8)
This commit is contained in:
@@ -93,6 +93,8 @@ class ProvisioningRequest(BaseModel):
|
||||
crm_website: Optional[str] = None
|
||||
job_title: Optional[str] = None
|
||||
crm_industry_name: Optional[str] = None
|
||||
campaign_tag: Optional[str] = None # NEW: e.g. "messe_2026"
|
||||
|
||||
|
||||
class ProvisioningResponse(BaseModel):
|
||||
status: str
|
||||
@@ -102,7 +104,9 @@ class ProvisioningResponse(BaseModel):
|
||||
role_name: Optional[str] = None
|
||||
opener: Optional[str] = None # Primary opener (Infrastructure/Cleaning)
|
||||
opener_secondary: Optional[str] = None # Secondary opener (Service/Logistics)
|
||||
summary: Optional[str] = None # NEW: AI Research Dossier
|
||||
texts: Dict[str, Optional[str]] = {}
|
||||
|
||||
unsubscribe_link: Optional[str] = None
|
||||
|
||||
# Enrichment Data for Write-Back
|
||||
@@ -131,6 +135,7 @@ class MarketingMatrixResponse(BaseModel):
|
||||
id: int
|
||||
industry_id: int
|
||||
persona_id: int
|
||||
campaign_tag: str
|
||||
industry_name: str
|
||||
persona_name: str
|
||||
subject: Optional[str] = None
|
||||
@@ -138,6 +143,7 @@ class MarketingMatrixResponse(BaseModel):
|
||||
social_proof: Optional[str] = None
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
@@ -431,11 +437,22 @@ def provision_superoffice_contact(
|
||||
persona_obj = db.query(Persona).filter(Persona.name == role_name).first()
|
||||
|
||||
if industry_obj and persona_obj:
|
||||
# Try to find a campaign-specific entry first
|
||||
matrix_entry = db.query(MarketingMatrix).filter(
|
||||
MarketingMatrix.industry_id == industry_obj.id,
|
||||
MarketingMatrix.persona_id == persona_obj.id
|
||||
MarketingMatrix.persona_id == persona_obj.id,
|
||||
MarketingMatrix.campaign_tag == req.campaign_tag
|
||||
).first()
|
||||
|
||||
# Fallback to standard if no specific entry is found
|
||||
if not matrix_entry:
|
||||
matrix_entry = db.query(MarketingMatrix).filter(
|
||||
MarketingMatrix.industry_id == industry_obj.id,
|
||||
MarketingMatrix.persona_id == persona_obj.id,
|
||||
MarketingMatrix.campaign_tag == "standard"
|
||||
).first()
|
||||
|
||||
|
||||
if matrix_entry:
|
||||
texts["subject"] = matrix_entry.subject
|
||||
texts["intro"] = matrix_entry.intro
|
||||
@@ -454,8 +471,10 @@ def provision_superoffice_contact(
|
||||
role_name=role_name,
|
||||
opener=company.ai_opener,
|
||||
opener_secondary=company.ai_opener_secondary,
|
||||
summary=company.research_dossier,
|
||||
texts=texts,
|
||||
unsubscribe_link=unsubscribe_link,
|
||||
|
||||
address_city=company.city,
|
||||
address_street=company.street,
|
||||
address_zip=company.zip_code,
|
||||
@@ -666,6 +685,7 @@ def list_job_roles(db: Session = Depends(get_db), username: str = Depends(authen
|
||||
def get_marketing_matrix(
|
||||
industry_id: Optional[int] = Query(None),
|
||||
persona_id: Optional[int] = Query(None),
|
||||
campaign_tag: Optional[str] = Query(None),
|
||||
db: Session = Depends(get_db),
|
||||
username: str = Depends(authenticate_user)
|
||||
):
|
||||
@@ -678,6 +698,8 @@ def get_marketing_matrix(
|
||||
query = query.filter(MarketingMatrix.industry_id == industry_id)
|
||||
if persona_id:
|
||||
query = query.filter(MarketingMatrix.persona_id == persona_id)
|
||||
if campaign_tag:
|
||||
query = query.filter(MarketingMatrix.campaign_tag == campaign_tag)
|
||||
|
||||
entries = query.all()
|
||||
|
||||
@@ -687,6 +709,7 @@ def get_marketing_matrix(
|
||||
id=e.id,
|
||||
industry_id=e.industry_id,
|
||||
persona_id=e.persona_id,
|
||||
campaign_tag=e.campaign_tag,
|
||||
industry_name=e.industry.name if e.industry else "Unknown",
|
||||
persona_name=e.persona.name if e.persona else "Unknown",
|
||||
subject=e.subject,
|
||||
|
||||
@@ -320,6 +320,7 @@ class MarketingMatrix(Base):
|
||||
# The combination keys
|
||||
industry_id = Column(Integer, ForeignKey("industries.id"), nullable=False)
|
||||
persona_id = Column(Integer, ForeignKey("personas.id"), nullable=False)
|
||||
campaign_tag = Column(String, default="standard", index=True) # NEW: Allows multiple variants (e.g. "standard", "messe_2026", "warmup")
|
||||
|
||||
# The Content
|
||||
subject = Column(Text, nullable=True)
|
||||
|
||||
43
company-explorer/backend/scripts/migrate_matrix_campaign.py
Normal file
43
company-explorer/backend/scripts/migrate_matrix_campaign.py
Normal file
@@ -0,0 +1,43 @@
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Pfade so setzen, dass das Backend gefunden wird
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
|
||||
|
||||
from backend.database import SessionLocal, engine
|
||||
from sqlalchemy import text
|
||||
|
||||
def migrate():
|
||||
print("🚀 Starting Migration: Adding 'campaign_tag' to MarketingMatrix...")
|
||||
|
||||
conn = engine.connect()
|
||||
|
||||
try:
|
||||
# 1. Prüfen, ob Spalte schon existiert
|
||||
# SQLite Pragma: table_info(marketing_matrix)
|
||||
result = conn.execute(text("PRAGMA table_info(marketing_matrix)")).fetchall()
|
||||
columns = [row[1] for row in result]
|
||||
|
||||
if "campaign_tag" in columns:
|
||||
print("✅ Column 'campaign_tag' already exists. Skipping.")
|
||||
return
|
||||
|
||||
# 2. Spalte hinzufügen (SQLite supports simple ADD COLUMN)
|
||||
print("Adding column 'campaign_tag' (DEFAULT 'standard')...")
|
||||
conn.execute(text("ALTER TABLE marketing_matrix ADD COLUMN campaign_tag VARCHAR DEFAULT 'standard'"))
|
||||
|
||||
# 3. Index erstellen (Optional, aber gut für Performance)
|
||||
print("Creating index on 'campaign_tag'...")
|
||||
conn.execute(text("CREATE INDEX ix_marketing_matrix_campaign_tag ON marketing_matrix (campaign_tag)"))
|
||||
|
||||
conn.commit()
|
||||
print("✅ Migration successful!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Migration failed: {e}")
|
||||
conn.rollback()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
migrate()
|
||||
Reference in New Issue
Block a user