gb/migrate_gb_rob.py

422 lines
14 KiB
Python

#!/usr/bin/env python3
"""
Migration script: gb.rob -> crm_deals + crm_activities
Execute: python3 migrate_gb_rob.py
"""
import os
import re
import json
from datetime import datetime
from decimal import Decimal
import psycopg2
from psycopg2 import extras
# Database connection
conn = psycopg2.connect(
host="localhost",
database="botserver",
user="gbuser"
)
conn.autocommit = False
cur = conn.cursor()
def parse_history_to_activities(history_text, deal_id, org_id, bot_id, owner_id):
"""Parse history field into crm_activities"""
if not history_text:
return []
activities = []
# Pattern: "DD/MM/YYYY: description" or "DD/MM/YYYY - description"
pattern = r'(\d{2}/\d{2}/\d{4})[;:]?\s*(.+?)(?=\d{2}/\d{2}/\d{4}|$)'
for match in re.finditer(pattern, str(history_text), re.DOTALL):
date_str = match.group(1)
description = match.group(2).strip()
try:
due_date = datetime.strptime(date_str, '%d/%m/%Y')
except:
due_date = None
# Determine activity type from description
activity_type = 'call'
if 'email' in description.lower() or 'mail' in description.lower():
activity_type = 'email'
elif 'wpp' in description.lower() or 'whatsapp' in description.lower():
activity_type = 'whatsapp'
elif 'reunião' in description.lower() or 'reuniao' in description.lower() or 'meeting' in description.lower():
activity_type = 'meeting'
# Determine outcome
outcome = None
desc_lower = description.lower()
if 'enviad' in desc_lower or 'enviado' in desc_lower:
outcome = 'sent'
elif 'recebid' in desc_lower:
outcome = 'received'
elif 'interesse' in desc_lower and 'não' in desc_lower:
outcome = 'not_interested'
elif 'agend' in desc_lower:
outcome = 'scheduled'
activities.append({
'org_id': org_id,
'bot_id': bot_id,
'deal_id': deal_id,
'activity_type': activity_type,
'subject': f"Follow-up: {description[:50]}",
'description': description,
'due_date': due_date,
'outcome': outcome,
'owner_id': owner_id,
'created_at': datetime.now()
})
return activities
def get_stage_id(stage_name):
"""Get stage_id from crm_pipeline_stages"""
cur.execute(
"SELECT id FROM crm_pipeline_stages WHERE LOWER(name) = %s",
(stage_name.lower(),)
)
result = cur.fetchone()
return result[0] if result else None
def get_user_id(username):
"""Get user_id from username"""
if not username:
return None
cur.execute(
"SELECT id FROM users WHERE username = %s",
(str(username).strip(),)
)
result = cur.fetchone()
return result[0] if result else None
def get_segment_id(segment_name, org_id, bot_id):
"""Get or create segment_id"""
if not segment_name:
return None
# Try to find existing
cur.execute(
"SELECT id FROM crm_deal_segments WHERE LOWER(name) = %s",
(str(segment_name).lower(),)
)
result = cur.fetchone()
if result:
return result[0]
# Create new
cur.execute("""
INSERT INTO crm_deal_segments (org_id, bot_id, name)
VALUES (%s, %s, %s)
RETURNING id
""", (org_id, bot_id, str(segment_name).strip()))
return cur.fetchone()[0]
def migrate_rob_to_deals():
"""Main migration function"""
print("🔄 Starting migration...")
# Get default org_id and bot_id
cur.execute("SELECT org_id, id FROM bots LIMIT 1")
bot_row = cur.fetchone()
bot_id = bot_row[1]
cur.execute("SELECT org_id FROM organizations LIMIT 1")
org_id = cur.fetchone()[0]
# Check if gb.rob exists
cur.execute("""
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'gb' AND table_name = 'rob'
""")
if not cur.fetchone():
print("⚠️ Table gb.rob not found!")
print(" Checking if it's in public schema...")
cur.execute("""
SELECT table_name FROM information_schema.tables
WHERE table_name = 'rob'
""")
result = cur.fetchone()
if not result:
print("❌ Table rob not found in any schema")
return
schema = 'public'
print(f"✅ Found table in schema: {schema}")
else:
schema = 'gb'
table_name = f"{schema}.rob" if schema != 'public' else 'rob'
print(f"📋 Reading from table: {table_name}")
# Get column names
cur.execute(f"""
SELECT column_name FROM information_schema.columns
WHERE table_name = 'rob' AND table_schema = '{schema}'
ORDER BY ordinal_position
""")
columns = [row[0] for row in cur.fetchall()]
print(f" Columns: {len(columns)}")
# Fetch all rows
cur.execute(f"SELECT * FROM {table_name}")
rows = cur.fetchall()
print(f" Rows to migrate: {len(rows)}")
# Create column index map
col_map = {col: i for i, col in enumerate(columns)}
migrated = 0
activities_created = 0
for row in rows:
try:
# Extract fields using column map
company = row[col_map.get('company')]
contact = row[col_map.get('contact')]
email = row[col_map.get('email')]
phone = row[col_map.get('phone')]
mobile = row[col_map.get('mobile')]
website = row[col_map.get('website')]
address = row[col_map.get('address')]
city = row[col_map.get('city')]
state = row[col_map.get('state')]
country = row[col_map.get('country')]
notes = row[col_map.get('notes')]
history = row[col_map.get('history')]
action = row[col_map.get('action')]
segment = row[col_map.get('segment')]
segment1 = row[col_map.get('segment1')]
territory = row[col_map.get('territory')]
linkedin = row[col_map.get('linkedin')]
facebook = row[col_map.get('facebook')]
twitter = row[col_map.get('twitter')]
hard_to_find = row[col_map.get('hard_to_find')]
value = row[col_map.get('value')]
chance = row[col_map.get('chance')]
period = row[col_map.get('period')]
assignedto = row[col_map.get('assignedto')]
am = row[col_map.get('am')]
robid = row[col_map.get('robid')]
created = row[col_map.get('created'))
updated_at = row[col_map.get('updated_at'))
# Parse name from contact
first_name = None
last_name = None
if contact:
contact_str = str(contact).strip()
if ' ' in contact_str:
parts = contact_str.split(None, 1)
first_name = parts[0]
last_name = parts[1] if len(parts) > 1 else None
else:
first_name = contact_str
# Map action to stage
action_map = {
'lead': 'new',
'desqualified': 'lost',
'qualified': 'qualified',
'email': 'new',
'call': 'new',
'schedule': 'qualified',
'poc': 'proposal',
'win': 'won',
'recover': 'qualified',
'wait': 'negotiation',
'find': 'new',
'emul_asked': 'new',
'emul': 'new',
'close': 'won',
'recovered': 'qualified'
}
stage_name = action_map.get(str(action).lower().strip(), 'new')
stage_id = get_stage_id(stage_name)
# Get owner_id from assignedto
owner_id = get_user_id(assignedto)
# Get am_id
am_id = get_user_id(am)
# Get segment_id
segment_id = get_segment_id(segment1, org_id, bot_id)
# Parse period (hour or morning/afternoon)
period_val = None
if period:
period_str = str(period).lower().strip()
if period_str.isdigit():
period_val = int(period_str)
elif 'manhã' in period_str or 'manha' in period_str:
period_val = 1
elif 'tarde' in period_str:
period_val = 2
elif 'noite' in period_str:
period_val = 3
# Parse value
deal_value = None
if value:
try:
deal_value = Decimal(str(value))
except:
pass
# Parse chance to probability
probability = 0
if chance:
chance_str = str(chance).lower()
if 'alta' in chance_str:
probability = 80
elif 'média' in chance_str or 'media' in chance_str:
probability = 50
elif 'baixa' in chance_str:
probability = 20
# Parse created timestamp
created_at = datetime.now()
if created:
try:
created_at = datetime.fromtimestamp(int(created))
except:
try:
created_at = datetime.strptime(str(created), '%Y-%m-%d')
except:
pass
# Updated at
updated_at_val = updated_at if updated_at else datetime.now()
# Generate deal_id
deal_id = extras.uuid.uuid4()
# Build custom_fields with remaining columns
custom_fields = {}
remaining_cols = [
'segment', 'segment1', 'segment2', 'networking',
'tech', 'bot', 'template', 'legalname',
'questions', 'features',
'partner', 'partnercontact',
'revenue', 'once', 'recurrence',
'employees', 'poc',
'talk', 'mail',
'mkt7', 'mkt8', 'mkt9',
'video_sent', 'prp_sent', 'emulator',
'CTOFound', 'priority', 'llm_notes',
'detailedsegment', 'assessment',
'address1', 'address2', 'address3', 'state_1', 'cep',
'fax', 'video_1', 'specialistazureappsinfra',
'specialistazuredataai', 'azurecloudacqspecialist',
'cloudsolutionmw', 'cloudacqspecialistmw',
'cloudsolutionbizapps', 'cloudacqspecialistbizapps',
'cloudsolutionarchitectsappsinfra', 'cloudsolutionarchitectsdataai'
]
for col in remaining_cols:
if col in col_map:
val = row[col_map[col]]
if val is not None:
custom_fields[col] = val
# Insert deal
cur.execute("""
INSERT INTO crm_deals (
id, org_id, bot_id,
contact_id, account_id,
owner_id, assignedto_id, am_id,
description, value,
stage_id, probability,
source, segment_id, territory,
linkedin, facebook, twitter,
notes,
hard_to_find, period,
custom_fields,
created_at, updated_at
) VALUES (
%s, %s, %s,
NULL, NULL,
%s, %s, %s,
%s, %s,
%s, %s,
%s, %s, %s,
%s, %s, %s,
%s,
%s, %s,
%s,
%s, %s
)
""", (
deal_id, org_id, bot_id,
None, None,
owner_id, owner_id, am_id,
notes, deal_value,
stage_id, probability,
action, segment_id, territory,
linkedin, facebook, twitter,
notes,
hard_to_find if hard_to_find else False,
period_val,
json.dumps(custom_fields) if custom_fields else '{}',
created_at, updated_at_val
))
# Migrate history to activities
activities = parse_history_to_activities(history, deal_id, org_id, bot_id, owner_id)
for activity in activities:
cur.execute("""
INSERT INTO crm_activities (
id, org_id, bot_id, deal_id,
activity_type, subject, description,
due_date, outcome, owner_id, created_at
) VALUES (
%s, %s, %s, %s,
%s, %s, %s,
%s, %s, %s, %s
)
""", (
extras.uuid.uuid4(),
activity['org_id'], activity['bot_id'], deal_id,
activity['activity_type'], activity['subject'], activity['description'],
activity['due_date'], activity['outcome'], activity['owner_id'],
activity['created_at']
))
activities_created += 1
migrated += 1
if migrated % 10 == 0:
print(f" Migrated {migrated} deals...")
except Exception as e:
print(f" ⚠️ Error on row {migrated}: {e}")
continue
conn.commit()
print(f"✅ Migration complete!")
print(f" Deals migrated: {migrated}")
print(f" Activities created: {activities_created}")
if __name__ == "__main__":
try:
migrate_rob_to_deals()
except Exception as e:
print(f"❌ Migration failed: {e}")
conn.rollback()
finally:
cur.close()
conn.close()