Finalize SuperOffice production migration and multi-campaign architecture (v1.8)

This commit is contained in:
2026-02-27 15:09:52 +00:00
parent 89fe284554
commit 41e5696c57
18 changed files with 460 additions and 311 deletions

View File

@@ -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,

View File

@@ -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)

View 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()