mirror of
https://github.com/kjanat/livegraphs-django.git
synced 2026-02-13 22:35:45 +01:00
feat: Add uv Docker, Postgres, and company linking
Introduces uv-based Docker workflow with non-root runtime, cached installs, and uv-run for web and Celery. Updates docker-compose to Postgres + Redis, loads .env, and removes source bind mount for reproducible builds. Switches settings to use Postgres when env is present with SQLite fallback; broadens allowed hosts for containerized development. Adds psycopg2-binary and updates sample env for Redis in Docker. Adds company scoping to external data models and links sessions during ingestion; provides management commands to seed a Jumbo company/users and sync external chat data into the dashboard. Includes .dockerignore, TypeScript config and typings, and minor template/docs tweaks. Requires database migration.
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
"""
|
||||
Management command to set up Jumbo company, users, and link existing data.
|
||||
"""
|
||||
|
||||
from accounts.models import Company, CustomUser
|
||||
from data_integration.models import ChatSession, ExternalDataSource
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Set up Jumbo company, create users, and link existing external data"
|
||||
|
||||
def handle(self, *_args, **_options):
|
||||
self.stdout.write("Setting up Jumbo company and data...")
|
||||
|
||||
# 1. Create Jumbo company
|
||||
jumbo_company, created = Company.objects.get_or_create(
|
||||
name="Jumbo", defaults={"description": "Jumbo Supermarkets - External API Data"}
|
||||
)
|
||||
if created:
|
||||
self.stdout.write(self.style.SUCCESS("✓ Created Jumbo company"))
|
||||
else:
|
||||
self.stdout.write(" Jumbo company already exists")
|
||||
|
||||
# 2. Create admin user for Jumbo
|
||||
admin_created = False
|
||||
if not CustomUser.objects.filter(username="jumbo_admin").exists():
|
||||
CustomUser.objects.create_user( # nosec B106
|
||||
username="jumbo_admin",
|
||||
email="admin@jumbo.nl",
|
||||
password="jumbo123",
|
||||
company=jumbo_company,
|
||||
is_company_admin=True,
|
||||
)
|
||||
self.stdout.write(self.style.SUCCESS("✓ Created Jumbo admin: jumbo_admin / jumbo123"))
|
||||
admin_created = True
|
||||
else:
|
||||
self.stdout.write(" Jumbo admin already exists")
|
||||
|
||||
# 3. Create regular users for Jumbo
|
||||
jumbo_users = [
|
||||
{
|
||||
"username": "jumbo_analyst",
|
||||
"email": "analyst@jumbo.nl",
|
||||
"password": "jumbo123",
|
||||
"is_company_admin": False,
|
||||
},
|
||||
{
|
||||
"username": "jumbo_manager",
|
||||
"email": "manager@jumbo.nl",
|
||||
"password": "jumbo123",
|
||||
"is_company_admin": False,
|
||||
},
|
||||
]
|
||||
|
||||
users_created = 0
|
||||
for user_data in jumbo_users:
|
||||
if not CustomUser.objects.filter(username=user_data["username"]).exists():
|
||||
CustomUser.objects.create_user(
|
||||
username=user_data["username"],
|
||||
email=user_data["email"],
|
||||
password=user_data["password"],
|
||||
company=jumbo_company,
|
||||
is_company_admin=user_data["is_company_admin"],
|
||||
)
|
||||
users_created += 1
|
||||
|
||||
if users_created:
|
||||
self.stdout.write(self.style.SUCCESS(f"✓ Created {users_created} Jumbo users"))
|
||||
else:
|
||||
self.stdout.write(" Jumbo users already exist")
|
||||
|
||||
# 4. Link External Data Source to Jumbo company
|
||||
try:
|
||||
jumbo_ext_source = ExternalDataSource.objects.get(name="Jumbo API")
|
||||
if not jumbo_ext_source.company:
|
||||
jumbo_ext_source.company = jumbo_company
|
||||
jumbo_ext_source.save()
|
||||
self.stdout.write(self.style.SUCCESS("✓ Linked Jumbo API data source to company"))
|
||||
else:
|
||||
self.stdout.write(" Jumbo API data source already linked")
|
||||
except ExternalDataSource.DoesNotExist:
|
||||
self.stdout.write(
|
||||
self.style.WARNING("⚠ Jumbo API external data source not found. Create it in admin first.")
|
||||
)
|
||||
|
||||
# 5. Link existing chat sessions to Jumbo company
|
||||
unlinked_sessions = ChatSession.objects.filter(company__isnull=True)
|
||||
if unlinked_sessions.exists():
|
||||
count = unlinked_sessions.update(company=jumbo_company)
|
||||
self.stdout.write(self.style.SUCCESS(f"✓ Linked {count} existing chat sessions to Jumbo company"))
|
||||
else:
|
||||
self.stdout.write(" All chat sessions already linked to companies")
|
||||
|
||||
# 6. Summary
|
||||
total_sessions = ChatSession.objects.filter(company=jumbo_company).count()
|
||||
total_users = CustomUser.objects.filter(company=jumbo_company).count()
|
||||
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
f"\n✓ Setup complete!"
|
||||
f"\n Company: {jumbo_company.name}"
|
||||
f"\n Users: {total_users} (including {1 if admin_created or CustomUser.objects.filter(username='jumbo_admin').exists() else 0} admin)"
|
||||
f"\n Chat sessions: {total_sessions}"
|
||||
)
|
||||
)
|
||||
self.stdout.write("\nLogin as jumbo_admin/jumbo123 to view the dashboard with Jumbo data.")
|
||||
@@ -0,0 +1,106 @@
|
||||
"""
|
||||
Management command to sync Jumbo API data to dashboard app with proper company linking.
|
||||
"""
|
||||
|
||||
from accounts.models import Company, CustomUser
|
||||
from dashboard.models import ChatSession, DataSource
|
||||
from data_integration.models import ChatSession as ExtChatSession
|
||||
from data_integration.models import ExternalDataSource
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Sync Jumbo API data to dashboard app with company linking"
|
||||
|
||||
def handle(self, *_args, **_options):
|
||||
self.stdout.write("Starting Jumbo data sync to dashboard...")
|
||||
|
||||
# 1. Get or create Jumbo company
|
||||
jumbo_company, created = Company.objects.get_or_create(
|
||||
name="Jumbo", defaults={"description": "Jumbo Supermarkets - External API Data"}
|
||||
)
|
||||
if created:
|
||||
self.stdout.write(self.style.SUCCESS("✓ Created Jumbo company"))
|
||||
else:
|
||||
self.stdout.write(" Jumbo company already exists")
|
||||
|
||||
# 2. Get Jumbo external data source
|
||||
try:
|
||||
jumbo_ext_source = ExternalDataSource.objects.get(name="Jumbo API")
|
||||
except ExternalDataSource.DoesNotExist:
|
||||
self.stdout.write(
|
||||
self.style.ERROR("✗ Jumbo API external data source not found. Please create it in admin first.")
|
||||
)
|
||||
return
|
||||
|
||||
# 3. Get or create DataSource linked to Jumbo company
|
||||
jumbo_datasource, created = DataSource.objects.get_or_create(
|
||||
name="Jumbo API Data",
|
||||
company=jumbo_company,
|
||||
defaults={
|
||||
"description": "Chat sessions from Jumbo external API",
|
||||
"external_source": jumbo_ext_source,
|
||||
},
|
||||
)
|
||||
if created:
|
||||
self.stdout.write(self.style.SUCCESS("✓ Created Jumbo DataSource"))
|
||||
else:
|
||||
self.stdout.write(" Jumbo DataSource already exists")
|
||||
|
||||
# 4. Sync chat sessions from data_integration to dashboard
|
||||
ext_sessions = ExtChatSession.objects.all()
|
||||
synced_count = 0
|
||||
skipped_count = 0
|
||||
|
||||
for ext_session in ext_sessions:
|
||||
# Check if already synced
|
||||
if ChatSession.objects.filter(data_source=jumbo_datasource, session_id=ext_session.session_id).exists():
|
||||
skipped_count += 1
|
||||
continue
|
||||
|
||||
# Create dashboard ChatSession
|
||||
ChatSession.objects.create(
|
||||
data_source=jumbo_datasource,
|
||||
session_id=ext_session.session_id,
|
||||
start_time=ext_session.start_time,
|
||||
end_time=ext_session.end_time,
|
||||
ip_address=ext_session.ip_address,
|
||||
country=ext_session.country or "",
|
||||
language=ext_session.language or "",
|
||||
messages_sent=ext_session.messages_sent or 0,
|
||||
sentiment=ext_session.sentiment or "",
|
||||
escalated=ext_session.escalated or False,
|
||||
forwarded_hr=ext_session.forwarded_hr or False,
|
||||
full_transcript=ext_session.full_transcript_url or "",
|
||||
avg_response_time=ext_session.avg_response_time,
|
||||
tokens=ext_session.tokens or 0,
|
||||
tokens_eur=ext_session.tokens_eur,
|
||||
category=ext_session.category or "",
|
||||
initial_msg=ext_session.initial_msg or "",
|
||||
user_rating=str(ext_session.user_rating) if ext_session.user_rating else "",
|
||||
)
|
||||
synced_count += 1
|
||||
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(f"✓ Synced {synced_count} chat sessions (skipped {skipped_count} existing)")
|
||||
)
|
||||
|
||||
# 5. Create admin user for Jumbo company if needed
|
||||
if not CustomUser.objects.filter(company=jumbo_company, is_company_admin=True).exists():
|
||||
CustomUser.objects.create_user( # nosec B106
|
||||
username="jumbo_admin",
|
||||
email="admin@jumbo.nl",
|
||||
password="jumbo123",
|
||||
company=jumbo_company,
|
||||
is_company_admin=True,
|
||||
)
|
||||
self.stdout.write(self.style.SUCCESS("✓ Created Jumbo admin user: jumbo_admin / jumbo123"))
|
||||
else:
|
||||
self.stdout.write(" Jumbo admin user already exists")
|
||||
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
f"\n✓ Sync complete! Jumbo company now has {ChatSession.objects.filter(data_source__company=jumbo_company).count()} chat sessions"
|
||||
)
|
||||
)
|
||||
self.stdout.write("\nLogin as jumbo_admin to view the dashboard with Jumbo data.")
|
||||
Reference in New Issue
Block a user