cryogenic22 commited on
Commit
2ca820c
·
verified ·
1 Parent(s): e9afb19

Update src/core/services/database_service.py

Browse files
Files changed (1) hide show
  1. src/core/services/database_service.py +136 -107
src/core/services/database_service.py CHANGED
@@ -1,4 +1,4 @@
1
- """Enhanced database service with comprehensive data model and synthetic data generation"""
2
  import sqlite3
3
  from contextlib import contextmanager
4
  from pathlib import Path
@@ -8,15 +8,28 @@ from datetime import datetime, timedelta
8
  import random
9
  from faker import Faker
10
  import uuid
11
-
12
 
13
  class DatabaseService:
14
- def __init__(self, db_path: str = "data/robata.db"):
15
- """Initialize database service"""
 
16
  self.db_path = db_path
17
  self.fake = Faker()
18
  Path(db_path).parent.mkdir(parents=True, exist_ok=True)
19
  self.setup_database()
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  @contextmanager
22
  def get_db(self):
@@ -29,6 +42,7 @@ class DatabaseService:
29
  conn.close()
30
 
31
  def setup_database(self):
 
32
  with self.get_db() as conn:
33
  c = conn.cursor()
34
  c.executescript('''
@@ -64,133 +78,148 @@ class DatabaseService:
64
  FOREIGN KEY (parent_account_id) REFERENCES accounts (id),
65
  FOREIGN KEY (account_owner_id) REFERENCES users (id)
66
  );
67
-
68
- CREATE TABLE IF NOT EXISTS contacts (
69
- id TEXT PRIMARY KEY,
70
- account_id TEXT,
71
- first_name TEXT,
72
- last_name TEXT,
73
- email TEXT,
74
- phone TEXT,
75
- title TEXT,
76
- department TEXT,
77
- reports_to_id TEXT,
78
- influence_level TEXT,
79
- engagement_score REAL,
80
- preferences TEXT,
81
- created_at TEXT,
82
- last_contacted TEXT,
83
- FOREIGN KEY (account_id) REFERENCES accounts (id),
84
- FOREIGN KEY (reports_to_id) REFERENCES contacts (id)
85
- );
86
- CREATE TABLE IF NOT EXISTS interactions (
87
- id TEXT PRIMARY KEY,
88
- type TEXT,
89
- account_id TEXT,
90
- owner_id TEXT,
91
- transcript TEXT,
92
- summary TEXT,
93
- sentiment_score REAL,
94
- metadata TEXT,
95
- created_at TEXT,
96
- FOREIGN KEY (account_id) REFERENCES accounts (id),
97
- FOREIGN KEY (owner_id) REFERENCES users (id)
98
- );
99
  ''')
100
 
101
  def generate_synthetic_data(self):
102
- """Generate comprehensive synthetic data"""
103
  with self.get_db() as conn:
104
  c = conn.cursor()
105
-
106
- # Configuration
107
- NUM_USERS = 50
108
- NUM_ACCOUNTS = 50
109
- NUM_CONTACTS = 300
110
-
111
- # Helper function to generate timestamp within last year
112
- def random_date():
113
- days = random.randint(0, 365)
114
- return (datetime.now() - timedelta(days=days)).isoformat()
115
-
116
  # Generate Users
117
- roles = ['sales_rep'] * 35 + ['regional_lead'] * 10 + ['head_of_sales'] * 5
118
- departments = ['Sales', 'Consulting', 'Technology', 'Operations']
119
- regions = ['North', 'South', 'East', 'West', 'Central']
120
-
121
  users = []
122
- for _ in range(NUM_USERS):
123
- role = random.choice(roles)
124
- dept = random.choice(departments)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  users.append({
126
- 'id': str(uuid.uuid4()),
127
  'email': self.fake.company_email(),
128
  'name': self.fake.name(),
129
- 'role': role,
130
- 'department': dept,
131
- 'title': f"Senior {dept} {role.title()}",
132
- 'region': random.choice(regions),
133
  'quota': random.uniform(500000, 2000000),
134
- 'created_at': random_date(),
135
- 'last_login': random_date()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
- c.executemany('''INSERT INTO users VALUES (
139
- :id, :email, :name, :role, :department, :title, :region,
140
- :quota, :created_at, :last_login
141
- )''', users)
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
  def get_user_by_email(self, email: str) -> Optional[Dict]:
144
  """Get user by email address"""
145
  with self.get_db() as conn:
146
- # Print all users for debugging
147
- all_users = conn.execute("SELECT email FROM users").fetchall()
148
- print(f"Available users: {[u[0] for u in all_users]}")
149
-
150
  cursor = conn.execute(
151
  "SELECT * FROM users WHERE email = ?",
152
  (email,)
153
  )
154
  row = cursor.fetchone()
155
- if row:
156
- return dict(row)
157
- print(f"No user found for email: {email}")
158
- return None
159
-
160
  def get_user_accounts(self, user_id: str) -> List[Dict]:
161
  """Get accounts associated with user"""
162
  with self.get_db() as conn:
163
  cursor = conn.execute("""
164
- SELECT DISTINCT a.*
165
- FROM accounts a
166
- WHERE a.account_owner_id = ?
167
- ORDER BY a.name
168
  """, (user_id,))
169
- return [dict(row) for row in cursor.fetchall()]
170
-
171
- def get_account_metrics(self, account_id: str) -> Dict:
172
- """Get metrics for a specific account"""
173
- with self.get_db() as conn:
174
- contacts_count = conn.execute(
175
- "SELECT COUNT(*) FROM contacts WHERE account_id = ?",
176
- (account_id,)
177
- ).fetchone()[0]
178
-
179
- return {
180
- 'contact_count': contacts_count,
181
- 'interaction_count': 0, # Default for now
182
- 'avg_sentiment': 0 # Default for now
183
- }
184
-
185
- def get_recent_interactions(self, user_id: str, limit: int = 10) -> List[Dict]:
186
- """Get recent interactions for a user"""
187
- with self.get_db() as conn:
188
- cursor = conn.execute("""
189
- SELECT i.*, a.name as account_name
190
- FROM interactions i
191
- JOIN accounts a ON i.account_id = a.id
192
- WHERE i.owner_id = ?
193
- ORDER BY i.created_at DESC
194
- LIMIT ?
195
- """, (user_id, limit))
196
- return [dict(row) for row in cursor.fetchall()]
 
1
+ """Enhanced database service with persistent storage and account management"""
2
  import sqlite3
3
  from contextlib import contextmanager
4
  from pathlib import Path
 
8
  import random
9
  from faker import Faker
10
  import uuid
11
+ import os
12
 
13
  class DatabaseService:
14
+ def __init__(self, db_path: str = "/data/robata.db"):
15
+ """Initialize database service with persistent storage"""
16
+ # Use /data directory for persistence in Hugging Face Spaces
17
  self.db_path = db_path
18
  self.fake = Faker()
19
  Path(db_path).parent.mkdir(parents=True, exist_ok=True)
20
  self.setup_database()
21
+
22
+ # Only generate data if database is empty
23
+ if not self._has_data():
24
+ self.generate_synthetic_data()
25
+
26
+ def _has_data(self) -> bool:
27
+ """Check if database has existing data"""
28
+ with self.get_db() as conn:
29
+ cursor = conn.cursor()
30
+ cursor.execute("SELECT COUNT(*) FROM users")
31
+ user_count = cursor.fetchone()[0]
32
+ return user_count > 0
33
 
34
  @contextmanager
35
  def get_db(self):
 
42
  conn.close()
43
 
44
  def setup_database(self):
45
+ """Set up database schema"""
46
  with self.get_db() as conn:
47
  c = conn.cursor()
48
  c.executescript('''
 
78
  FOREIGN KEY (parent_account_id) REFERENCES accounts (id),
79
  FOREIGN KEY (account_owner_id) REFERENCES users (id)
80
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  ''')
82
 
83
  def generate_synthetic_data(self):
84
+ """Generate synthetic users and accounts with proper linkage"""
85
  with self.get_db() as conn:
86
  c = conn.cursor()
87
+
 
 
 
 
 
 
 
 
 
 
88
  # Generate Users
 
 
 
 
89
  users = []
90
+ user_ids = [] # Keep track of user IDs for account assignment
91
+
92
+ # Predefined test user
93
+ test_user_id = str(uuid.uuid4())
94
+ users.append({
95
+ 'id': test_user_id,
96
+ 'email': 'test@example.com', # Default test login
97
+ 'name': 'Test User',
98
+ 'role': 'sales_rep',
99
+ 'department': 'Sales',
100
+ 'title': 'Senior Sales Representative',
101
+ 'region': 'North',
102
+ 'quota': 1000000.0,
103
+ 'created_at': datetime.now().isoformat(),
104
+ 'last_login': datetime.now().isoformat()
105
+ })
106
+ user_ids.append(test_user_id)
107
+
108
+ # Generate additional users
109
+ for _ in range(10):
110
+ user_id = str(uuid.uuid4())
111
+ user_ids.append(user_id)
112
  users.append({
113
+ 'id': user_id,
114
  'email': self.fake.company_email(),
115
  'name': self.fake.name(),
116
+ 'role': random.choice(['sales_rep', 'regional_lead', 'head_of_sales']),
117
+ 'department': random.choice(['Sales', 'Consulting', 'Technology']),
118
+ 'title': 'Senior Sales Representative',
119
+ 'region': random.choice(['North', 'South', 'East', 'West']),
120
  'quota': random.uniform(500000, 2000000),
121
+ 'created_at': datetime.now().isoformat(),
122
+ 'last_login': datetime.now().isoformat()
123
+ })
124
+
125
+ # Insert users
126
+ c.executemany('''
127
+ INSERT OR REPLACE INTO users VALUES (
128
+ :id, :email, :name, :role, :department, :title, :region,
129
+ :quota, :created_at, :last_login
130
+ )
131
+ ''', users)
132
+
133
+ # Generate Accounts
134
+ accounts = []
135
+ industries = ['Technology', 'Healthcare', 'Financial Services', 'Manufacturing', 'Retail']
136
+
137
+ # Ensure test user has accounts
138
+ for _ in range(3):
139
+ accounts.append({
140
+ 'id': str(uuid.uuid4()),
141
+ 'name': self.fake.company(),
142
+ 'parent_account_id': None,
143
+ 'industry': random.choice(industries),
144
+ 'status': 'active',
145
+ 'website': self.fake.url(),
146
+ 'annual_revenue': random.uniform(1000000, 100000000),
147
+ 'employee_count': random.randint(50, 10000),
148
+ 'technology_stack': json.dumps(['Python', 'React', 'AWS']),
149
+ 'region': 'North',
150
+ 'address': self.fake.address(),
151
+ 'account_owner_id': test_user_id, # Assign to test user
152
+ 'engagement_score': random.uniform(0, 100),
153
+ 'created_at': datetime.now().isoformat(),
154
+ 'last_activity_at': datetime.now().isoformat()
155
  })
156
+
157
+ # Generate additional accounts
158
+ for user_id in user_ids:
159
+ for _ in range(random.randint(2, 5)):
160
+ accounts.append({
161
+ 'id': str(uuid.uuid4()),
162
+ 'name': self.fake.company(),
163
+ 'parent_account_id': None,
164
+ 'industry': random.choice(industries),
165
+ 'status': 'active',
166
+ 'website': self.fake.url(),
167
+ 'annual_revenue': random.uniform(1000000, 100000000),
168
+ 'employee_count': random.randint(50, 10000),
169
+ 'technology_stack': json.dumps(['Python', 'React', 'AWS']),
170
+ 'region': random.choice(['North', 'South', 'East', 'West']),
171
+ 'address': self.fake.address(),
172
+ 'account_owner_id': user_id,
173
+ 'engagement_score': random.uniform(0, 100),
174
+ 'created_at': datetime.now().isoformat(),
175
+ 'last_activity_at': datetime.now().isoformat()
176
+ })
177
+
178
+ # Insert accounts
179
+ c.executemany('''
180
+ INSERT OR REPLACE INTO accounts VALUES (
181
+ :id, :name, :parent_account_id, :industry, :status, :website,
182
+ :annual_revenue, :employee_count, :technology_stack, :region,
183
+ :address, :account_owner_id, :engagement_score, :created_at,
184
+ :last_activity_at
185
+ )
186
+ ''', accounts)
187
+
188
+ conn.commit()
189
 
190
+ def add_account(self, account_data: Dict[str, Any]) -> str:
191
+ """Add a new account to the database"""
192
+ account_id = str(uuid.uuid4())
193
+ account_data['id'] = account_id
194
+ account_data['created_at'] = datetime.now().isoformat()
195
+ account_data['last_activity_at'] = datetime.now().isoformat()
196
+
197
+ with self.get_db() as conn:
198
+ c = conn.cursor()
199
+ placeholders = ', '.join(['?' for _ in account_data])
200
+ columns = ', '.join(account_data.keys())
201
+ sql = f'INSERT INTO accounts ({columns}) VALUES ({placeholders})'
202
+ c.execute(sql, list(account_data.values()))
203
+ conn.commit()
204
+
205
+ return account_id
206
 
207
  def get_user_by_email(self, email: str) -> Optional[Dict]:
208
  """Get user by email address"""
209
  with self.get_db() as conn:
 
 
 
 
210
  cursor = conn.execute(
211
  "SELECT * FROM users WHERE email = ?",
212
  (email,)
213
  )
214
  row = cursor.fetchone()
215
+ return dict(row) if row else None
216
+
 
 
 
217
  def get_user_accounts(self, user_id: str) -> List[Dict]:
218
  """Get accounts associated with user"""
219
  with self.get_db() as conn:
220
  cursor = conn.execute("""
221
+ SELECT * FROM accounts
222
+ WHERE account_owner_id = ?
223
+ ORDER BY name
 
224
  """, (user_id,))
225
+ return [dict(row) for row in cursor.fetchall()]