mirror of
https://github.com/thecyberlearn/quantum-ai.git
synced 2026-08-18 07:52:59 +00:00
- Complete Django project structure with apps/ organization - Working homepage with exact Next.js recreation (1039 lines) - User authentication system with wallet balance - Agent system with processors and models - Stripe payment integration setup - All Django migrations and URL routing - Comprehensive .gitignore file - Homepage loads successfully (HTTP 200) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
453 lines
18 KiB
Python
453 lines
18 KiB
Python
# Create your views here.
|
|
from django.shortcuts import render, redirect, get_object_or_404
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.contrib import messages
|
|
from django.http import JsonResponse
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from django.views.decorators.http import require_http_methods
|
|
from django.conf import settings
|
|
from django.contrib.auth import get_user_model
|
|
from django.utils import timezone
|
|
from agents.models import Agent
|
|
from agents.agent_processors import AgentProcessor
|
|
import json
|
|
import os
|
|
|
|
User = get_user_model()
|
|
|
|
def homepage(request):
|
|
"""Enhanced homepage with all features from Next.js version"""
|
|
# Handle contact form submission
|
|
if request.method == 'POST':
|
|
name = request.POST.get('name')
|
|
email = request.POST.get('email')
|
|
company = request.POST.get('company', '')
|
|
message = request.POST.get('message')
|
|
|
|
if name and email and message:
|
|
# Here you can save to database or send email
|
|
# For now, just show success message
|
|
messages.success(request, f'Thank you {name}! Your message has been sent. We will get back to you soon.')
|
|
return redirect('homepage')
|
|
else:
|
|
messages.error(request, 'Please fill in all required fields.')
|
|
|
|
# Get featured agents for preview
|
|
featured_agents = Agent.objects.filter(is_active=True)[:3]
|
|
|
|
context = {
|
|
'featured_agents': featured_agents,
|
|
'total_agents': Agent.objects.filter(is_active=True).count(),
|
|
'total_users': User.objects.count(),
|
|
}
|
|
|
|
return render(request, 'homepage.html', context)
|
|
|
|
def marketplace(request):
|
|
"""Display all available agents with enhanced filtering"""
|
|
category_filter = request.GET.get('category')
|
|
search_query = request.GET.get('search')
|
|
|
|
agents = Agent.objects.filter(is_active=True)
|
|
|
|
if category_filter:
|
|
agents = agents.filter(category=category_filter)
|
|
|
|
if search_query:
|
|
from django.db import models
|
|
agents = agents.filter(
|
|
models.Q(name__icontains=search_query) |
|
|
models.Q(description__icontains=search_query)
|
|
)
|
|
|
|
# Get unique categories for filter dropdown
|
|
categories = Agent.objects.filter(is_active=True).values_list('category', flat=True).distinct()
|
|
|
|
context = {
|
|
'agents': agents.order_by('category', 'name'),
|
|
'categories': categories,
|
|
'current_category': category_filter,
|
|
'search_query': search_query,
|
|
}
|
|
|
|
return render(request, 'marketplace.html', context)
|
|
|
|
def pricing(request):
|
|
"""Enhanced pricing page with payment status handling"""
|
|
packages = [
|
|
{
|
|
'id': 'basic',
|
|
'amount': 10,
|
|
'price': 9.99,
|
|
'label': 'Basic',
|
|
'description': 'Perfect for trying out AI agents',
|
|
'features': ['2-4 agent uses', 'Basic support', 'Email notifications'],
|
|
'icon': '💰',
|
|
'gradient': 'from-blue-500 to-purple-600'
|
|
},
|
|
{
|
|
'id': 'popular',
|
|
'amount': 50,
|
|
'price': 49.99,
|
|
'label': 'Popular',
|
|
'description': 'Most popular choice for regular users',
|
|
'features': ['10-25 agent uses', 'Priority support', 'Advanced analytics', 'Export options'],
|
|
'icon': '⭐',
|
|
'gradient': 'from-purple-500 to-pink-600',
|
|
'popular': True
|
|
},
|
|
{
|
|
'id': 'premium',
|
|
'amount': 100,
|
|
'price': 99.99,
|
|
'label': 'Premium',
|
|
'description': 'For power users and small teams',
|
|
'features': ['50+ agent uses', '24/7 support', 'Custom integrations', 'Team collaboration'],
|
|
'icon': '🚀',
|
|
'gradient': 'from-green-500 to-teal-600'
|
|
},
|
|
{
|
|
'id': 'enterprise',
|
|
'amount': 500,
|
|
'price': 499.99,
|
|
'label': 'Enterprise',
|
|
'description': 'For large teams and businesses',
|
|
'features': ['Unlimited uses', 'Dedicated support', 'Custom development', 'SLA guarantee'],
|
|
'icon': '👑',
|
|
'gradient': 'from-yellow-500 to-red-600'
|
|
},
|
|
]
|
|
|
|
# Handle payment status messages (prevent duplicate messages)
|
|
payment_status = request.GET.get('payment')
|
|
session_id = request.GET.get('session_id')
|
|
|
|
# Create session key to prevent duplicate messages
|
|
if payment_status:
|
|
session_key = f"payment_message_{payment_status}_{session_id or 'cancelled'}"
|
|
if not request.session.get(session_key):
|
|
request.session[session_key] = True
|
|
|
|
if payment_status == 'success':
|
|
messages.success(request, '✅ Payment successful! Your wallet has been topped up.')
|
|
elif payment_status == 'cancelled':
|
|
messages.error(request, '❌ Payment was cancelled. No charges were made.')
|
|
|
|
# FAQ data
|
|
faqs = [
|
|
{
|
|
'question': 'How does the pay-per-use pricing work?',
|
|
'answer': 'You add money to your wallet and pay for each AI agent use. Prices range from 2.00 to 8.00 AED per use.'
|
|
},
|
|
{
|
|
'question': 'Do wallet funds expire?',
|
|
'answer': 'No, your wallet balance never expires. Use it whenever you need AI assistance.'
|
|
},
|
|
{
|
|
'question': 'Can I get a refund?',
|
|
'answer': 'Yes, unused wallet balance can be refunded within 30 days of purchase.'
|
|
},
|
|
{
|
|
'question': 'Is my payment information secure?',
|
|
'answer': 'Absolutely. We use Stripe for secure payment processing and never store your payment details.'
|
|
}
|
|
]
|
|
|
|
context = {
|
|
'packages': packages,
|
|
'faqs': faqs,
|
|
}
|
|
|
|
return render(request, 'pricing.html', context)
|
|
|
|
def debug_page(request):
|
|
"""Debug page for development environment checking"""
|
|
if not settings.DEBUG:
|
|
context = {'debug_mode': False}
|
|
return render(request, 'debug.html', context)
|
|
|
|
# Environment status check
|
|
env_status = {
|
|
'DATABASE_URL': bool(os.getenv('DATABASE_URL')),
|
|
'STRIPE_SECRET_KEY': bool(settings.STRIPE_SECRET_KEY),
|
|
'N8N_WEBHOOK_DATA_ANALYZER': bool(settings.N8N_WEBHOOK_DATA_ANALYZER),
|
|
'N8N_WEBHOOK_FIVE_WHYS': bool(settings.N8N_WEBHOOK_FIVE_WHYS),
|
|
'OPENWEATHER_API_KEY': bool(settings.OPENWEATHER_API_KEY),
|
|
'DEBUG': settings.DEBUG,
|
|
'ALLOWED_HOSTS': settings.ALLOWED_HOSTS,
|
|
}
|
|
|
|
# Database connection test
|
|
try:
|
|
user_count = User.objects.count()
|
|
agent_count = Agent.objects.count()
|
|
db_status = {'status': 'Connected', 'color': 'green'}
|
|
except Exception as e:
|
|
user_count = 0
|
|
agent_count = 0
|
|
db_status = {'status': f'Error: {str(e)}', 'color': 'red'}
|
|
|
|
context = {
|
|
'debug_mode': True,
|
|
'env_status': json.dumps(env_status, indent=2),
|
|
'db_status': db_status,
|
|
'user_count': user_count,
|
|
'agent_count': agent_count,
|
|
}
|
|
|
|
return render(request, 'debug.html', context)
|
|
|
|
def reset_password(request):
|
|
"""Password reset functionality"""
|
|
if request.method == 'POST':
|
|
password = request.POST.get('password')
|
|
confirm_password = request.POST.get('confirm_password')
|
|
|
|
if not password or not confirm_password:
|
|
context = {'error': 'Both password fields are required'}
|
|
return render(request, 'reset_password.html', context)
|
|
|
|
if password != confirm_password:
|
|
context = {'error': 'Passwords do not match'}
|
|
return render(request, 'reset_password.html', context)
|
|
|
|
if len(password) < 8:
|
|
context = {'error': 'Password must be at least 8 characters long'}
|
|
return render(request, 'reset_password.html', context)
|
|
|
|
# In a real implementation, you would:
|
|
# 1. Verify the reset token from the URL
|
|
# 2. Update the user's password
|
|
# 3. Redirect to login with success message
|
|
|
|
messages.success(request, 'Password updated successfully! Please log in with your new password.')
|
|
return redirect('homepage')
|
|
|
|
# Check if we have a valid reset token (simplified version)
|
|
token = request.GET.get('token')
|
|
if not token:
|
|
context = {'error': 'Invalid or expired reset link. Please request a new password reset.'}
|
|
return render(request, 'reset_password.html', context)
|
|
|
|
return render(request, 'reset_password.html')
|
|
|
|
@login_required
|
|
def agent_detail(request, slug):
|
|
"""Enhanced agent detail page with wallet balance checking"""
|
|
agent = get_object_or_404(Agent, slug=slug, is_active=True)
|
|
|
|
# Calculate wallet status
|
|
user_balance = request.user.wallet_balance
|
|
has_sufficient_balance = user_balance >= agent.price
|
|
|
|
# Calculate usage count
|
|
if has_sufficient_balance:
|
|
possible_uses = int(user_balance / agent.price)
|
|
else:
|
|
possible_uses = 0
|
|
|
|
context = {
|
|
'agent': agent,
|
|
'user_balance': user_balance,
|
|
'has_sufficient_balance': has_sufficient_balance,
|
|
'possible_uses': possible_uses,
|
|
'balance_after_use': user_balance - agent.price if has_sufficient_balance else user_balance,
|
|
}
|
|
|
|
return render(request, 'agent_detail.html', context)
|
|
|
|
@login_required
|
|
@require_http_methods(["POST"])
|
|
def process_agent(request, slug):
|
|
"""Enhanced agent processing with comprehensive error handling"""
|
|
agent = get_object_or_404(Agent, slug=slug, is_active=True)
|
|
|
|
# Check wallet balance
|
|
if not request.user.has_sufficient_balance(agent.price):
|
|
return JsonResponse({
|
|
'success': False,
|
|
'error': f'Insufficient balance. Required: {agent.price_display}, Available: {request.user.wallet_balance:.2f} AED'
|
|
}, status=400)
|
|
|
|
try:
|
|
# Process based on agent type
|
|
processor = AgentProcessor(agent.slug)
|
|
|
|
if agent.slug == 'data-analyzer':
|
|
file_obj = request.FILES.get('file')
|
|
if not file_obj:
|
|
return JsonResponse({'success': False, 'error': 'File is required'}, status=400)
|
|
|
|
# Validate file size (10MB limit)
|
|
if file_obj.size > 10 * 1024 * 1024:
|
|
return JsonResponse({'success': False, 'error': 'File size must be less than 10MB'}, status=400)
|
|
|
|
# Validate file type
|
|
allowed_extensions = ['.csv', '.xlsx', '.xls', '.json']
|
|
file_extension = os.path.splitext(file_obj.name)[1].lower()
|
|
if file_extension not in allowed_extensions:
|
|
return JsonResponse({'success': False, 'error': 'Invalid file type. Allowed: CSV, Excel, JSON'}, status=400)
|
|
|
|
result = processor.process_agent(file_obj=file_obj, user_id=str(request.user.id))
|
|
|
|
elif agent.slug == 'five-whys':
|
|
problem = request.POST.get('problem')
|
|
if not problem or len(problem.strip()) < 10:
|
|
return JsonResponse({'success': False, 'error': 'Problem description must be at least 10 characters'}, status=400)
|
|
result = processor.process_agent(problem_description=problem, user_id=str(request.user.id))
|
|
|
|
elif agent.slug == 'weather-reporter':
|
|
location = request.POST.get('location')
|
|
if not location or len(location.strip()) < 2:
|
|
return JsonResponse({'success': False, 'error': 'Location must be at least 2 characters'}, status=400)
|
|
result = processor.process_agent(location=location)
|
|
|
|
elif agent.slug == 'job-posting-generator':
|
|
required_fields = ['title', 'company', 'description', 'requirements']
|
|
job_details = {}
|
|
|
|
for field in required_fields:
|
|
value = request.POST.get(field, '').strip()
|
|
if not value:
|
|
return JsonResponse({'success': False, 'error': f'{field.title()} is required'}, status=400)
|
|
if len(value) < 5:
|
|
return JsonResponse({'success': False, 'error': f'{field.title()} must be at least 5 characters'}, status=400)
|
|
job_details[field] = value
|
|
|
|
result = processor.process_agent(job_details=job_details, user_id=str(request.user.id))
|
|
|
|
elif agent.slug == 'social-ads-generator':
|
|
required_fields = ['product', 'platform', 'target_audience', 'tone']
|
|
ad_requirements = {}
|
|
|
|
for field in required_fields:
|
|
value = request.POST.get(field, '').strip()
|
|
if not value:
|
|
return JsonResponse({'success': False, 'error': f'{field.replace("_", " ").title()} is required'}, status=400)
|
|
ad_requirements[field] = value
|
|
|
|
# Validate platform
|
|
valid_platforms = ['facebook', 'instagram', 'twitter', 'linkedin']
|
|
if ad_requirements['platform'] not in valid_platforms:
|
|
return JsonResponse({'success': False, 'error': 'Invalid platform selected'}, status=400)
|
|
|
|
# Validate tone
|
|
valid_tones = ['professional', 'casual', 'humorous', 'urgent']
|
|
if ad_requirements['tone'] not in valid_tones:
|
|
return JsonResponse({'success': False, 'error': 'Invalid tone selected'}, status=400)
|
|
|
|
result = processor.process_agent(ad_requirements=ad_requirements, user_id=str(request.user.id))
|
|
|
|
elif agent.slug == 'faq-generator':
|
|
content_source = request.POST.get('content_source', '').strip()
|
|
if not content_source:
|
|
return JsonResponse({'success': False, 'error': 'Content source is required'}, status=400)
|
|
if len(content_source) < 50:
|
|
return JsonResponse({'success': False, 'error': 'Content source must be at least 50 characters'}, status=400)
|
|
|
|
result = processor.process_agent(content_source=content_source, user_id=str(request.user.id))
|
|
|
|
else:
|
|
return JsonResponse({'success': False, 'error': 'Agent not supported'}, status=400)
|
|
|
|
# Deduct balance on successful processing
|
|
if request.user.deduct_balance(
|
|
agent.price,
|
|
f"Used {agent.name}",
|
|
agent.slug
|
|
):
|
|
return JsonResponse({
|
|
'success': True,
|
|
'result': result,
|
|
'new_balance': float(request.user.wallet_balance),
|
|
'agent_used': agent.name,
|
|
'cost': float(agent.price)
|
|
})
|
|
else:
|
|
return JsonResponse({
|
|
'success': False,
|
|
'error': 'Failed to process payment. Please try again.'
|
|
}, status=400)
|
|
|
|
except Exception as e:
|
|
# Log the error in production
|
|
if not settings.DEBUG:
|
|
import logging
|
|
logger = logging.getLogger(__name__)
|
|
logger.error(f"Agent processing error: {str(e)}", exc_info=True)
|
|
|
|
return JsonResponse({
|
|
'success': False,
|
|
'error': 'An error occurred while processing your request. Please try again later.'
|
|
}, status=500)
|
|
|
|
@login_required
|
|
def profile(request):
|
|
"""Enhanced user profile with transaction history and wallet management"""
|
|
# Get recent transactions
|
|
transactions = request.user.wallet_transactions.all()[:50] # Last 50 transactions
|
|
|
|
# Calculate usage statistics
|
|
total_spent = sum(abs(t.amount) for t in transactions if t.type == 'agent_usage')
|
|
total_topped_up = sum(t.amount for t in transactions if t.type == 'top_up')
|
|
total_agents_used = transactions.filter(type='agent_usage').count()
|
|
|
|
# Get most used agents
|
|
from django.db.models import Count
|
|
popular_agents = (transactions.filter(type='agent_usage')
|
|
.values('agent_slug')
|
|
.annotate(count=Count('agent_slug'))
|
|
.order_by('-count')[:5])
|
|
|
|
# Wallet status
|
|
balance = request.user.wallet_balance
|
|
if balance < 5:
|
|
wallet_status = {'status': 'low', 'color': 'red', 'message': 'Low balance - Add money to continue using agents'}
|
|
elif balance < 20:
|
|
wallet_status = {'status': 'medium', 'color': 'orange', 'message': 'Consider adding more funds'}
|
|
else:
|
|
wallet_status = {'status': 'high', 'color': 'green', 'message': 'Good balance'}
|
|
|
|
context = {
|
|
'transactions': transactions,
|
|
'total_spent': total_spent,
|
|
'total_topped_up': total_topped_up,
|
|
'total_agents_used': total_agents_used,
|
|
'popular_agents': popular_agents,
|
|
'wallet_status': wallet_status,
|
|
}
|
|
|
|
return render(request, 'profile.html', context)
|
|
|
|
# API endpoints for AJAX functionality
|
|
@login_required
|
|
@require_http_methods(["GET"])
|
|
def check_wallet_balance(request):
|
|
"""API endpoint to check current wallet balance"""
|
|
return JsonResponse({
|
|
'balance': float(request.user.wallet_balance),
|
|
'formatted_balance': f"{request.user.wallet_balance:.2f} AED"
|
|
})
|
|
|
|
@login_required
|
|
@require_http_methods(["POST"])
|
|
def chat_message(request, slug):
|
|
"""Handle chat messages for interactive agents like 5 Whys"""
|
|
if slug != 'five-whys':
|
|
return JsonResponse({'success': False, 'error': 'Chat not available for this agent'}, status=400)
|
|
|
|
message = request.POST.get('message', '').strip()
|
|
if not message:
|
|
return JsonResponse({'success': False, 'error': 'Message is required'}, status=400)
|
|
|
|
# Here you would integrate with your 5 Whys processing logic
|
|
# For now, return a simple response
|
|
response_message = f"Thank you for: {message}. Let me ask you the next Why question..."
|
|
|
|
return JsonResponse({
|
|
'success': True,
|
|
'response': response_message,
|
|
'timestamp': timezone.now().isoformat()
|
|
})
|
|
|