Cleanup: Add migration scripts to gitignore and remove from tracking
- Add debug_usage.py, fix_database.py, migrate_usage_summaries.py, simple_migrate.py, validate_implementation.py to .gitignore - Add CAMERA_SELFIE_IMPLEMENTATION.md to .gitignore - Remove these files from git tracking (keep locally)
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -261,3 +261,13 @@ docs/__pycache__/
|
|||||||
backend/.onboarding_progress*.json
|
backend/.onboarding_progress*.json
|
||||||
backend/researchtools_text/projects/Draft__AI_advanc_c2f90698.json
|
backend/researchtools_text/projects/Draft__AI_advanc_c2f90698.json
|
||||||
backend/researchtools_text/projects/Draft__AI_adv_388d4491.json
|
backend/researchtools_text/projects/Draft__AI_adv_388d4491.json
|
||||||
|
|
||||||
|
# Migration and debug scripts
|
||||||
|
debug_usage.py
|
||||||
|
fix_database.py
|
||||||
|
migrate_usage_summaries.py
|
||||||
|
simple_migrate.py
|
||||||
|
validate_implementation.py
|
||||||
|
|
||||||
|
# Camera selfie implementation (not needed)
|
||||||
|
CAMERA_SELFIE_IMPLEMENTATION.md
|
||||||
|
|||||||
@@ -1,137 +0,0 @@
|
|||||||
# Camera Selfie Feature - Implementation Complete
|
|
||||||
|
|
||||||
## ✅ **Feature Successfully Implemented**
|
|
||||||
|
|
||||||
The camera selfie feature has been successfully added to the Podcast Maker's avatar upload section.
|
|
||||||
|
|
||||||
## 🚀 **What Was Built**
|
|
||||||
|
|
||||||
### 1. **CameraSelfie Component** (`CameraSelfie.tsx`)
|
|
||||||
- **Full camera functionality** using MediaDevices API
|
|
||||||
- **Live video preview** with mirror effect for natural selfie experience
|
|
||||||
- **Camera controls**: Capture, flip camera, close
|
|
||||||
- **Face positioning guide** overlay for better framing
|
|
||||||
- **Comprehensive error handling** for permissions and device limitations
|
|
||||||
- **Mobile support** with front/back camera switching
|
|
||||||
- **Responsive design** for desktop and mobile
|
|
||||||
|
|
||||||
### 2. **AvatarSelector Integration**
|
|
||||||
- **New "Take Selfie" tab** added before "Upload Your Photo"
|
|
||||||
- **Seamless integration** with existing avatar flow
|
|
||||||
- **Consistent UI/UX** matching current design patterns
|
|
||||||
- **Updated help text** to include camera option
|
|
||||||
|
|
||||||
### 3. **CreateModal Integration**
|
|
||||||
- **Camera state management** with React hooks
|
|
||||||
- **Image processing**: DataURL → File conversion
|
|
||||||
- **Upload integration**: Reuses existing upload logic
|
|
||||||
- **Error handling** for camera capture failures
|
|
||||||
|
|
||||||
## 🎯 **Key Features**
|
|
||||||
|
|
||||||
### **Camera Experience**
|
|
||||||
- **One-click camera access** from avatar selector
|
|
||||||
- **Live preview** with natural mirror effect
|
|
||||||
- **Face guide overlay** to help users position themselves
|
|
||||||
- **Camera flip** for mobile devices (front/back)
|
|
||||||
- **Instant capture** with visual feedback
|
|
||||||
|
|
||||||
### **Technical Features**
|
|
||||||
- **MediaDevices API** for camera access
|
|
||||||
- **Canvas-based image capture** with proper formatting
|
|
||||||
- **File conversion** to maintain compatibility with existing upload flow
|
|
||||||
- **Permission handling** with user-friendly error messages
|
|
||||||
- **Resource cleanup** to prevent camera leaks
|
|
||||||
|
|
||||||
### **User Experience**
|
|
||||||
- **Intuitive tab placement** before file upload
|
|
||||||
- **Clear visual indicators** and instructions
|
|
||||||
- **Graceful fallback** to file upload if camera unavailable
|
|
||||||
- **Consistent styling** with existing UI components
|
|
||||||
|
|
||||||
## 📱 **Browser Compatibility**
|
|
||||||
|
|
||||||
### **Supported**
|
|
||||||
- ✅ Modern browsers with MediaDevices API support
|
|
||||||
- ✅ Chrome 60+, Firefox 55+, Safari 11+, Edge 79+
|
|
||||||
- ✅ Mobile browsers with camera access
|
|
||||||
|
|
||||||
### **Fallback Handling**
|
|
||||||
- ❌ Camera not available → Shows message with file upload suggestion
|
|
||||||
- ❌ Permission denied → Clear instructions to enable camera
|
|
||||||
- ❌ Camera in use → User-friendly error message
|
|
||||||
|
|
||||||
## 🔧 **How It Works**
|
|
||||||
|
|
||||||
### **User Flow**
|
|
||||||
1. User clicks "Take Selfie" tab in avatar selector
|
|
||||||
2. Camera dialog opens with live preview
|
|
||||||
3. User positions face using guide overlay
|
|
||||||
4. User clicks capture button (or uses controls)
|
|
||||||
5. Image is processed and uploaded automatically
|
|
||||||
6. User can use "Make Presentable" feature like uploaded photos
|
|
||||||
|
|
||||||
### **Technical Flow**
|
|
||||||
1. `setCameraSelfieOpen(true)` opens camera dialog
|
|
||||||
2. `CameraSelfie` component requests camera access
|
|
||||||
3. Live video stream displayed with mirror effect
|
|
||||||
4. User captures photo → canvas conversion
|
|
||||||
5. DataURL passed to `handleCameraSelfie`
|
|
||||||
6. DataURL → File conversion and upload
|
|
||||||
7. Integration with existing avatar preview system
|
|
||||||
|
|
||||||
## 🎨 **UI Components**
|
|
||||||
|
|
||||||
### **Camera Dialog**
|
|
||||||
- **Modal dialog** with full-screen camera view
|
|
||||||
- **Control overlay** at bottom with capture, flip, close buttons
|
|
||||||
- **Face guide** overlay in center
|
|
||||||
- **Loading states** and error messages
|
|
||||||
|
|
||||||
### **Tab Integration**
|
|
||||||
- **New tab** with camera icon
|
|
||||||
- **Consistent styling** with existing tabs
|
|
||||||
- **Hover effects** and visual feedback
|
|
||||||
- **Help text** updates
|
|
||||||
|
|
||||||
## 🔍 **Files Modified/Created**
|
|
||||||
|
|
||||||
### **New Files**
|
|
||||||
- `frontend/src/components/PodcastMaker/CameraSelfie.tsx` - Full camera component
|
|
||||||
|
|
||||||
### **Modified Files**
|
|
||||||
- `frontend/src/components/PodcastMaker/CreateStep/AvatarSelector.tsx` - Added camera tab and integration
|
|
||||||
- `frontend/src/components/PodcastMaker/CreateModal.tsx` - Added camera state and handlers
|
|
||||||
|
|
||||||
## 🧪 **Testing Instructions**
|
|
||||||
|
|
||||||
### **Manual Testing**
|
|
||||||
1. Start frontend development server
|
|
||||||
2. Navigate to Podcast Maker
|
|
||||||
3. Click "Create New Podcast"
|
|
||||||
4. Select "Take Selfie" tab in avatar section
|
|
||||||
5. Grant camera permissions when prompted
|
|
||||||
6. Test camera preview and capture functionality
|
|
||||||
7. Verify "Make Presentable" works with captured photo
|
|
||||||
8. Test error scenarios (deny permission, no camera)
|
|
||||||
|
|
||||||
### **Test Scenarios**
|
|
||||||
- ✅ Camera permission granted
|
|
||||||
- ✅ Camera permission denied
|
|
||||||
- ✅ No camera available
|
|
||||||
- ✅ Camera already in use
|
|
||||||
- ✅ Mobile camera switching
|
|
||||||
- ✅ Image capture and upload
|
|
||||||
- ✅ Integration with "Make Presentable"
|
|
||||||
- ✅ Avatar removal and re-capture
|
|
||||||
|
|
||||||
## 🎉 **Ready for Production**
|
|
||||||
|
|
||||||
The camera selfie feature is now fully implemented and ready for user testing. It provides a modern, intuitive way for users to capture their podcast presenter photos directly from their device camera, with full integration into the existing avatar upload and enhancement workflow.
|
|
||||||
|
|
||||||
**Key Benefits:**
|
|
||||||
- 📸 **Faster than file upload** - No need to find and select photos
|
|
||||||
- 🎯 **Better framing** - Face guide helps users position themselves correctly
|
|
||||||
- 📱 **Mobile optimized** - Native camera experience on phones
|
|
||||||
- 🔄 **Seamless integration** - Works with existing "Make Presentable" feature
|
|
||||||
- 🛡️ **Robust error handling** - Graceful fallbacks and clear instructions
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
from sqlalchemy import create_engine, text
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
|
|
||||||
# Add backend to path
|
|
||||||
sys.path.append(os.path.join(os.getcwd(), 'backend'))
|
|
||||||
|
|
||||||
from services.database import Base
|
|
||||||
from models.subscription_models import APIUsageLog, UserSubscription
|
|
||||||
from services.subscription import UsageTrackingService, PricingService
|
|
||||||
|
|
||||||
# Setup DB connection
|
|
||||||
# dynamic path resolution as per codebase
|
|
||||||
DB_PATH = os.path.join(os.getcwd(), 'backend', 'data', 'alwrity.db')
|
|
||||||
# Note: The codebase might use user-specific DBs now.
|
|
||||||
# Let's check how get_db works or if we need to look at a specific user db.
|
|
||||||
# user_memories says: Database path updated to `workspace/workspace_{user_id}/db/alwrity.db` to support user isolation.
|
|
||||||
|
|
||||||
USER_ID = "user_33Gz1FPI86VDXhRY8QN4ragRFGN"
|
|
||||||
WORKSPACE_DB_PATH = os.path.join(os.getcwd(), 'workspace', f'workspace_{USER_ID}', 'db', 'alwrity.db')
|
|
||||||
|
|
||||||
print(f"Checking specific user DB at: {WORKSPACE_DB_PATH}")
|
|
||||||
|
|
||||||
if os.path.exists(WORKSPACE_DB_PATH):
|
|
||||||
db_url = f"sqlite:///{WORKSPACE_DB_PATH}"
|
|
||||||
else:
|
|
||||||
print(f"User DB not found at {WORKSPACE_DB_PATH}, falling back to main DB for check (legacy/shared mode)")
|
|
||||||
db_url = f"sqlite:///backend/data/alwrity.db"
|
|
||||||
|
|
||||||
engine = create_engine(db_url)
|
|
||||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
||||||
db = SessionLocal()
|
|
||||||
|
|
||||||
try:
|
|
||||||
print(f"\n--- Checking Usage for User: {USER_ID} ---")
|
|
||||||
|
|
||||||
# Check API Usage Logs
|
|
||||||
logs_count = db.query(APIUsageLog).filter(APIUsageLog.user_id == USER_ID).count()
|
|
||||||
print(f"Total API Usage Logs: {logs_count}")
|
|
||||||
|
|
||||||
if logs_count > 0:
|
|
||||||
last_log = db.query(APIUsageLog).filter(APIUsageLog.user_id == USER_ID).order_by(APIUsageLog.created_at.desc()).first()
|
|
||||||
print(f"Last Activity: {last_log.created_at} - {last_log.endpoint} ({last_log.provider})")
|
|
||||||
|
|
||||||
# Check Subscription
|
|
||||||
sub = db.query(UserSubscription).filter(UserSubscription.user_id == USER_ID).first()
|
|
||||||
if sub:
|
|
||||||
print(f"Subscription: {sub.plan_type} (Status: {sub.status})")
|
|
||||||
else:
|
|
||||||
print("No subscription record found.")
|
|
||||||
|
|
||||||
# Run Service Logic
|
|
||||||
print("\n--- Running UsageTrackingService.get_user_usage_stats ---")
|
|
||||||
usage_service = UsageTrackingService(db)
|
|
||||||
stats = usage_service.get_user_usage_stats(USER_ID)
|
|
||||||
print("Stats returned:")
|
|
||||||
print(stats)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error: {e}")
|
|
||||||
finally:
|
|
||||||
db.close()
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
"""
|
|
||||||
Quick fix for missing wavespeed columns in usage_summaries table
|
|
||||||
Run this script to fix the database schema issue
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sqlite3
|
|
||||||
import os
|
|
||||||
|
|
||||||
def fix_database():
|
|
||||||
# Find database file
|
|
||||||
db_path = None
|
|
||||||
for path in ["backend/database.db", "database.db"]:
|
|
||||||
if os.path.exists(path):
|
|
||||||
db_path = path
|
|
||||||
break
|
|
||||||
|
|
||||||
if not db_path:
|
|
||||||
print("❌ Database not found!")
|
|
||||||
print("Please make sure you're running this from the project root directory")
|
|
||||||
return
|
|
||||||
|
|
||||||
print(f"📁 Using database: {db_path}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
conn = sqlite3.connect(db_path)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Check if table exists
|
|
||||||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='usage_summaries'")
|
|
||||||
if not cursor.fetchone():
|
|
||||||
print("❌ Table 'usage_summaries' not found!")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get current columns
|
|
||||||
cursor.execute("PRAGMA table_info(usage_summaries)")
|
|
||||||
columns = [row[1] for row in cursor.fetchall()]
|
|
||||||
|
|
||||||
# Columns that need to be added
|
|
||||||
missing_columns = []
|
|
||||||
required_columns = [
|
|
||||||
'wavespeed_calls', 'tavily_calls', 'serper_calls', 'metaphor_calls',
|
|
||||||
'firecrawl_calls', 'stability_calls', 'exa_calls', 'video_calls',
|
|
||||||
'image_edit_calls', 'audio_calls', 'wavespeed_tokens', 'wavespeed_cost',
|
|
||||||
'tavily_cost', 'serper_cost', 'metaphor_cost', 'firecrawl_cost',
|
|
||||||
'stability_cost', 'exa_cost', 'video_cost', 'image_edit_cost', 'audio_cost'
|
|
||||||
]
|
|
||||||
|
|
||||||
for col in required_columns:
|
|
||||||
if col not in columns:
|
|
||||||
missing_columns.append(col)
|
|
||||||
|
|
||||||
if missing_columns:
|
|
||||||
print(f"➕ Adding {len(missing_columns)} missing columns...")
|
|
||||||
for col in missing_columns:
|
|
||||||
if col.endswith('_calls') or col.endswith('_tokens'):
|
|
||||||
cursor.execute(f"ALTER TABLE usage_summaries ADD COLUMN {col} INTEGER DEFAULT 0")
|
|
||||||
else: # cost columns
|
|
||||||
cursor.execute(f"ALTER TABLE usage_summaries ADD COLUMN {col} FLOAT DEFAULT 0.0")
|
|
||||||
print(f" ✅ Added {col}")
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
print("🎉 Database schema updated successfully!")
|
|
||||||
else:
|
|
||||||
print("✅ All columns already exist!")
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Error: {e}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("🔧 Fixing database schema for usage_summaries...")
|
|
||||||
fix_database()
|
|
||||||
print("✅ Done!")
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Migration script to add missing wavespeed columns to usage_summaries table
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sqlite3
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
def get_db_path():
|
|
||||||
"""Find the database file"""
|
|
||||||
# Look for common database locations
|
|
||||||
possible_paths = [
|
|
||||||
"backend/database.db",
|
|
||||||
"backend/data/database.db",
|
|
||||||
"database.db",
|
|
||||||
"data/database.db"
|
|
||||||
]
|
|
||||||
|
|
||||||
for path in possible_paths:
|
|
||||||
if os.path.exists(path):
|
|
||||||
return path
|
|
||||||
|
|
||||||
# If not found, check if there's a .db file in backend directory
|
|
||||||
backend_dir = Path("backend")
|
|
||||||
if backend_dir.exists():
|
|
||||||
for db_file in backend_dir.glob("*.db"):
|
|
||||||
return str(db_file)
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def migrate_usage_summaries():
|
|
||||||
"""Add missing wavespeed columns to usage_summaries table"""
|
|
||||||
|
|
||||||
db_path = get_db_path()
|
|
||||||
if not db_path:
|
|
||||||
print("❌ Database file not found!")
|
|
||||||
print("Looked in:")
|
|
||||||
for path in ["backend/database.db", "backend/data/database.db", "database.db", "data/database.db"]:
|
|
||||||
print(f" - {path}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
print(f"📁 Using database: {db_path}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
conn = sqlite3.connect(db_path)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Check if table exists
|
|
||||||
cursor.execute("""
|
|
||||||
SELECT name FROM sqlite_master
|
|
||||||
WHERE type='table' AND name='usage_summaries'
|
|
||||||
""")
|
|
||||||
|
|
||||||
if not cursor.fetchone():
|
|
||||||
print("❌ Table 'usage_summaries' does not exist!")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Get current columns
|
|
||||||
cursor.execute("PRAGMA table_info(usage_summaries)")
|
|
||||||
columns = [row[1] for row in cursor.fetchall()]
|
|
||||||
print(f"📋 Current columns: {columns}")
|
|
||||||
|
|
||||||
# Columns to add
|
|
||||||
columns_to_add = [
|
|
||||||
("wavespeed_calls", "INTEGER DEFAULT 0"),
|
|
||||||
("tavily_calls", "INTEGER DEFAULT 0"),
|
|
||||||
("serper_calls", "INTEGER DEFAULT 0"),
|
|
||||||
("metaphor_calls", "INTEGER DEFAULT 0"),
|
|
||||||
("firecrawl_calls", "INTEGER DEFAULT 0"),
|
|
||||||
("stability_calls", "INTEGER DEFAULT 0"),
|
|
||||||
("exa_calls", "INTEGER DEFAULT 0"),
|
|
||||||
("video_calls", "INTEGER DEFAULT 0"),
|
|
||||||
("image_edit_calls", "INTEGER DEFAULT 0"),
|
|
||||||
("audio_calls", "INTEGER DEFAULT 0"),
|
|
||||||
("wavespeed_tokens", "INTEGER DEFAULT 0"),
|
|
||||||
("wavespeed_cost", "FLOAT DEFAULT 0.0"),
|
|
||||||
("tavily_cost", "FLOAT DEFAULT 0.0"),
|
|
||||||
("serper_cost", "FLOAT DEFAULT 0.0"),
|
|
||||||
("metaphor_cost", "FLOAT DEFAULT 0.0"),
|
|
||||||
("firecrawl_cost", "FLOAT DEFAULT 0.0"),
|
|
||||||
("stability_cost", "FLOAT DEFAULT 0.0"),
|
|
||||||
("exa_cost", "FLOAT DEFAULT 0.0"),
|
|
||||||
("video_cost", "FLOAT DEFAULT 0.0"),
|
|
||||||
("image_edit_cost", "FLOAT DEFAULT 0.0"),
|
|
||||||
("audio_cost", "FLOAT DEFAULT 0.0")
|
|
||||||
]
|
|
||||||
|
|
||||||
# Add missing columns
|
|
||||||
added_columns = []
|
|
||||||
for column_name, column_def in columns_to_add:
|
|
||||||
if column_name not in columns:
|
|
||||||
print(f"➕ Adding column: {column_name}")
|
|
||||||
cursor.execute(f"ALTER TABLE usage_summaries ADD COLUMN {column_name} {column_def}")
|
|
||||||
added_columns.append(column_name)
|
|
||||||
else:
|
|
||||||
print(f"✅ Column already exists: {column_name}")
|
|
||||||
|
|
||||||
if added_columns:
|
|
||||||
conn.commit()
|
|
||||||
print(f"🎉 Successfully added {len(added_columns)} columns:")
|
|
||||||
for col in added_columns:
|
|
||||||
print(f" - {col}")
|
|
||||||
else:
|
|
||||||
print("✅ All columns already exist!")
|
|
||||||
|
|
||||||
# Verify the changes
|
|
||||||
cursor.execute("PRAGMA table_info(usage_summaries)")
|
|
||||||
new_columns = [row[1] for row in cursor.fetchall()]
|
|
||||||
print(f"📋 Updated columns: {new_columns}")
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
return True
|
|
||||||
|
|
||||||
except sqlite3.Error as e:
|
|
||||||
print(f"❌ Database error: {e}")
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Unexpected error: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("🚀 Starting usage_summaries migration...")
|
|
||||||
|
|
||||||
if migrate_usage_summaries():
|
|
||||||
print("✅ Migration completed successfully!")
|
|
||||||
sys.exit(0)
|
|
||||||
else:
|
|
||||||
print("❌ Migration failed!")
|
|
||||||
sys.exit(1)
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
import sqlite3
|
|
||||||
import os
|
|
||||||
|
|
||||||
# Find database
|
|
||||||
db_paths = ["backend/database.db", "backend/data/database.db", "database.db", "data/database.db"]
|
|
||||||
db_path = None
|
|
||||||
|
|
||||||
for path in db_paths:
|
|
||||||
if os.path.exists(path):
|
|
||||||
db_path = path
|
|
||||||
break
|
|
||||||
|
|
||||||
if not db_path:
|
|
||||||
print("Database not found!")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
print(f"Using database: {db_path}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
conn = sqlite3.connect(db_path)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Check table
|
|
||||||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='usage_summaries'")
|
|
||||||
if not cursor.fetchone():
|
|
||||||
print("Table usage_summaries not found!")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
# Get columns
|
|
||||||
cursor.execute("PRAGMA table_info(usage_summaries)")
|
|
||||||
columns = [row[1] for row in cursor.fetchall()]
|
|
||||||
print(f"Columns: {columns}")
|
|
||||||
|
|
||||||
# Check for wavespeed_calls
|
|
||||||
if "wavespeed_calls" in columns:
|
|
||||||
print("✅ wavespeed_calls column exists")
|
|
||||||
else:
|
|
||||||
print("❌ wavespeed_calls column missing")
|
|
||||||
|
|
||||||
# Add the column
|
|
||||||
print("Adding wavespeed_calls column...")
|
|
||||||
cursor.execute("ALTER TABLE usage_summaries ADD COLUMN wavespeed_calls INTEGER DEFAULT 0")
|
|
||||||
conn.commit()
|
|
||||||
print("✅ wavespeed_calls column added")
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
print("Migration completed!")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error: {e}")
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Validation script for the enhanced topic feature implementation.
|
|
||||||
Checks that all files and components are properly implemented.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
|
|
||||||
def check_file_exists(filepath, description):
|
|
||||||
"""Check if a file exists."""
|
|
||||||
if os.path.exists(filepath):
|
|
||||||
print(f"✅ {description}: {filepath}")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print(f"❌ {description}: {filepath} (NOT FOUND)")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def check_file_content(filepath, search_strings, description):
|
|
||||||
"""Check if file contains required content."""
|
|
||||||
if not os.path.exists(filepath):
|
|
||||||
print(f"❌ {description}: File not found")
|
|
||||||
return False
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(filepath, 'r', encoding='utf-8') as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
missing = []
|
|
||||||
for search in search_strings:
|
|
||||||
if search not in content:
|
|
||||||
missing.append(search)
|
|
||||||
|
|
||||||
if missing:
|
|
||||||
print(f"❌ {description}: Missing content: {missing}")
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
print(f"✅ {description}: All required content found")
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ {description}: Error reading file: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Validate the complete implementation."""
|
|
||||||
print("🔍 Validating Enhanced Topic Feature Implementation")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
backend_root = "c:\\Users\\diksha rawat\\Desktop\\ALwrity_github\\windsurf\\ALwrity\\backend"
|
|
||||||
frontend_root = "c:\\Users\\diksha rawat\\Desktop\\ALwrity_github\\windsurf\\ALwrity\\frontend\\src\\components\\PodcastMaker"
|
|
||||||
|
|
||||||
checks_passed = 0
|
|
||||||
total_checks = 0
|
|
||||||
|
|
||||||
# Backend Checks
|
|
||||||
print("\n📋 BACKEND VALIDATION")
|
|
||||||
print("-" * 30)
|
|
||||||
|
|
||||||
# Check models.py
|
|
||||||
total_checks += 1
|
|
||||||
if check_file_content(
|
|
||||||
f"{backend_root}\\api\\podcast\\models.py",
|
|
||||||
["enhanced_ideas: List[str]", "rationales: List[str]"],
|
|
||||||
"Backend Response Model"
|
|
||||||
):
|
|
||||||
checks_passed += 1
|
|
||||||
|
|
||||||
# Check analysis.py handler
|
|
||||||
total_checks += 1
|
|
||||||
if check_file_content(
|
|
||||||
f"{backend_root}\\api\\podcast\\handlers\\analysis.py",
|
|
||||||
["Professional & Expert-led angle", "Storytelling & Human interest angle", "Trendy & Contemporary angle"],
|
|
||||||
"Backend Enhancement Prompt"
|
|
||||||
):
|
|
||||||
checks_passed += 1
|
|
||||||
|
|
||||||
# Check response handling
|
|
||||||
total_checks += 1
|
|
||||||
if check_file_content(
|
|
||||||
f"{backend_root}\\api\\podcast\\handlers\\analysis.py",
|
|
||||||
["enhanced_ideas[:3]", "rationales[:3]"],
|
|
||||||
"Backend Response Handling"
|
|
||||||
):
|
|
||||||
checks_passed += 1
|
|
||||||
|
|
||||||
# Frontend Checks
|
|
||||||
print("\n📋 FRONTEND VALIDATION")
|
|
||||||
print("-" * 30)
|
|
||||||
|
|
||||||
# Check modal component
|
|
||||||
total_checks += 1
|
|
||||||
if check_file_exists(
|
|
||||||
f"{frontend_root}\\EnhancedTopicChoicesModal.tsx",
|
|
||||||
"Enhanced Topic Choices Modal Component"
|
|
||||||
):
|
|
||||||
checks_passed += 1
|
|
||||||
|
|
||||||
# Check modal content
|
|
||||||
total_checks += 1
|
|
||||||
if check_file_content(
|
|
||||||
f"{frontend_root}\\EnhancedTopicChoicesModal.tsx",
|
|
||||||
["CHOICE_LABELS", "handleChoiceEdit", "handleSelectChoice"],
|
|
||||||
"Modal Component Logic"
|
|
||||||
):
|
|
||||||
checks_passed += 1
|
|
||||||
|
|
||||||
# Check CreateModal state
|
|
||||||
total_checks += 1
|
|
||||||
if check_file_content(
|
|
||||||
f"{frontend_root}\\CreateModal.tsx",
|
|
||||||
["enhancedChoices", "enhancedRationales", "choicesModalOpen", "editedChoices"],
|
|
||||||
"CreateModal State Management"
|
|
||||||
):
|
|
||||||
checks_passed += 1
|
|
||||||
|
|
||||||
# Check CreateModal handlers
|
|
||||||
total_checks += 1
|
|
||||||
if check_file_content(
|
|
||||||
f"{frontend_root}\\CreateModal.tsx",
|
|
||||||
["handleChoiceSelection", "result.enhanced_ideas", "setChoicesModalOpen(true)"],
|
|
||||||
"CreateModal Event Handlers"
|
|
||||||
):
|
|
||||||
checks_passed += 1
|
|
||||||
|
|
||||||
# Check API service update
|
|
||||||
total_checks += 1
|
|
||||||
if check_file_content(
|
|
||||||
f"{frontend_root}\\..\\..\\services\\podcastApi.ts",
|
|
||||||
["enhanced_ideas: string[]", "rationales: string[]"],
|
|
||||||
"Frontend API Service Update"
|
|
||||||
):
|
|
||||||
checks_passed += 1
|
|
||||||
|
|
||||||
# Check modal import and usage
|
|
||||||
total_checks += 1
|
|
||||||
if check_file_content(
|
|
||||||
f"{frontend_root}\\CreateModal.tsx",
|
|
||||||
["import { EnhancedTopicChoicesModal }", "<EnhancedTopicChoicesModal"],
|
|
||||||
"Modal Integration"
|
|
||||||
):
|
|
||||||
checks_passed += 1
|
|
||||||
|
|
||||||
# Summary
|
|
||||||
print("\n📊 VALIDATION SUMMARY")
|
|
||||||
print("=" * 30)
|
|
||||||
print(f"Checks Passed: {checks_passed}/{total_checks}")
|
|
||||||
print(f"Success Rate: {(checks_passed/total_checks)*100:.1f}%")
|
|
||||||
|
|
||||||
if checks_passed == total_checks:
|
|
||||||
print("\n🎉 ALL CHECKS PASSED! Implementation is complete.")
|
|
||||||
print("\n📝 FEATURE SUMMARY:")
|
|
||||||
print("✅ Backend returns 3 enhanced ideas with rationales")
|
|
||||||
print("✅ Frontend displays choices in editable modal")
|
|
||||||
print("✅ Users can select and edit choices")
|
|
||||||
print("✅ AI gradient styling applied consistently")
|
|
||||||
print("✅ Error handling and fallbacks implemented")
|
|
||||||
print("\n🚀 Ready for testing!")
|
|
||||||
else:
|
|
||||||
print(f"\n⚠️ {total_checks - checks_passed} checks failed. Please review implementation.")
|
|
||||||
|
|
||||||
return checks_passed == total_checks
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
success = main()
|
|
||||||
sys.exit(0 if success else 1)
|
|
||||||
Reference in New Issue
Block a user