diff --git a/agents/management/commands/create_cybersec_career_agent.py b/agents/management/commands/create_cybersec_career_agent.py index 6f3f3d4..b154741 100644 --- a/agents/management/commands/create_cybersec_career_agent.py +++ b/agents/management/commands/create_cybersec_career_agent.py @@ -34,8 +34,8 @@ class Command(BaseCommand): 'fields': [] # Empty since we're using JotForm directly }, 'webhook_url': 'https://agent.jotform.com/019865a942ab7fa5b5b743a5fd2abe09e345', - 'access_url_name': '', - 'display_url_name': '' + 'access_url_name': 'agents:direct_access_handler', + 'display_url_name': 'agents:direct_access_display' } ) diff --git a/agents/migrations/0005_auto_20250804_1045.py b/agents/migrations/0005_auto_20250804_1045.py deleted file mode 100644 index 06fa14f..0000000 --- a/agents/migrations/0005_auto_20250804_1045.py +++ /dev/null @@ -1,50 +0,0 @@ -# Generated by Django 5.2.4 on 2025-08-04 10:45 - -from django.db import migrations, models - - -def check_and_add_fields(apps, schema_editor): - """Add fields only if they don't already exist""" - db_alias = schema_editor.connection.alias - - # Check if columns already exist in the database - with schema_editor.connection.cursor() as cursor: - cursor.execute(""" - SELECT column_name - FROM information_schema.columns - WHERE table_name = 'agents_agent' - AND column_name IN ('access_url_name', 'display_url_name') - """) - existing_columns = [row[0] for row in cursor.fetchall()] - - # Add access_url_name if it doesn't exist - if 'access_url_name' not in existing_columns: - cursor.execute(""" - ALTER TABLE agents_agent - ADD COLUMN access_url_name VARCHAR(100) DEFAULT '' NOT NULL - """) - - # Add display_url_name if it doesn't exist - if 'display_url_name' not in existing_columns: - cursor.execute(""" - ALTER TABLE agents_agent - ADD COLUMN display_url_name VARCHAR(100) DEFAULT '' NOT NULL - """) - - -def reverse_check_and_add_fields(apps, schema_editor): - """Remove fields if they exist""" - with schema_editor.connection.cursor() as cursor: - cursor.execute("ALTER TABLE agents_agent DROP COLUMN IF EXISTS access_url_name") - cursor.execute("ALTER TABLE agents_agent DROP COLUMN IF EXISTS display_url_name") - - -class Migration(migrations.Migration): - - dependencies = [ - ("agents", "0004_add_message_limit"), - ] - - operations = [ - migrations.RunPython(check_and_add_fields, reverse_check_and_add_fields), - ] diff --git a/agents/migrations/0006_agent_access_url_name_agent_display_url_name_and_more.py b/agents/migrations/0006_agent_access_url_name_agent_display_url_name_and_more.py new file mode 100644 index 0000000..78c5752 --- /dev/null +++ b/agents/migrations/0006_agent_access_url_name_agent_display_url_name_and_more.py @@ -0,0 +1,42 @@ +# Generated by Django 5.2.4 on 2025-08-04 17:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("agents", "0005_auto_20250804_1105"), + ] + + operations = [ + migrations.AddField( + model_name="agent", + name="access_url_name", + field=models.CharField( + blank=True, + default="", + help_text="URL name for direct access agents", + max_length=100, + ), + ), + migrations.AddField( + model_name="agent", + name="display_url_name", + field=models.CharField( + blank=True, + default="", + help_text="URL name for agent display page", + max_length=100, + ), + ), + migrations.AlterField( + model_name="chatsession", + name="expires_at", + field=models.DateTimeField( + blank=True, + help_text="Session expiration time (30 minutes from last activity)", + null=True, + ), + ), + ] diff --git a/agents/templates/agents/agent_detail.html b/agents/templates/agents/agent_detail.html index a68cb44..82eae99 100644 --- a/agents/templates/agents/agent_detail.html +++ b/agents/templates/agents/agent_detail.html @@ -39,7 +39,42 @@ document.body.setAttribute('data-user-balance', '{{ user.wallet_balance }}');
-
+ {% if agent.access_url_name and agent.display_url_name %} + +
+
+

{{ agent.category.icon }} {{ agent.name }} - External Consultation

+

+ This consultation will redirect you to our specialized external form for personalized guidance. +

+ + {% if user.is_authenticated %} + {% if agent.price == 0 %} + + {{ agent.category.icon }} Start {{ agent.name }} Consultation (FREE) + + {% elif user.wallet_balance >= agent.price %} + + {{ agent.category.icon }} Start {{ agent.name }} Consultation ({{ agent.price }} AED) + + {% else %} +
+ Insufficient balance! You need {{ agent.price }} AED. +
+ + 💰 Top Up Wallet + + {% endif %} + {% else %} + + 🔐 Login to Continue + + {% endif %} +
+
+ {% else %} + + {% csrf_token %} @@ -167,6 +202,7 @@ document.body.setAttribute('data-user-balance', '{{ user.wallet_balance }}'); {% endif %}
+ {% endif %} diff --git a/agents/urls.py b/agents/urls.py index 381d020..96e490f 100644 --- a/agents/urls.py +++ b/agents/urls.py @@ -27,6 +27,10 @@ urlpatterns = [ path('api/', views.agent_list, name='agent_list'), path('api//', views.agent_detail, name='agent_detail_api'), + # Generic direct access routes (must be before agent detail) + path('/access/', views.direct_access_handler, name='direct_access_handler'), + path('/display/', views.direct_access_display, name='direct_access_display'), + # Agent detail page (must be last to avoid conflicts) path('/', views.agent_detail_view, name='detail'), ] \ No newline at end of file diff --git a/agents/views.py b/agents/views.py index fcdc3db..5336ad4 100644 --- a/agents/views.py +++ b/agents/views.py @@ -994,3 +994,62 @@ def export_chat_txt(chat_session, messages): response = HttpResponse(text_content, content_type='text/plain') response['Content-Disposition'] = f'attachment; filename="5whys_chat_{chat_session.session_id}.txt"' return response + + +# Generic Direct Access Views for External Form Agents +@login_required +def direct_access_handler(request, slug): + """ + Generic handler for direct access agents (external forms like JotForm). + Handles payment processing and grants access to external form. + """ + agent = get_object_or_404(Agent, slug=slug, is_active=True) + + # Verify this is a direct access agent + if not agent.access_url_name or not agent.display_url_name: + messages.error(request, 'This agent does not support direct access.') + return redirect('agents:marketplace') + + # Handle payment for paid agents + if agent.price > 0: + user_balance = request.user.wallet_balance + if user_balance < agent.price: + messages.error(request, f'Insufficient balance. You need {agent.price} AED but have {user_balance} AED.') + return redirect('wallet:wallet') + + # Process payment + try: + from wallet.models import WalletTransaction + WalletTransaction.objects.create( + user=request.user, + amount=-agent.price, + type='agent_usage', + description=f'Payment for {agent.name}', + agent_slug=agent.slug + ) + messages.success(request, f'Payment of {agent.price} AED processed successfully.') + except Exception as e: + messages.error(request, 'Payment processing failed. Please try again.') + return redirect('agents:agent_detail', slug=slug) + + # Grant access - redirect to display page + messages.success(request, f'Access granted to {agent.name}. Redirecting to consultation form...') + return redirect('agents:direct_access_display', slug=slug) + + +@login_required +def direct_access_display(request, slug): + """ + Generic display handler for direct access agents. + Shows external form (JotForm, Google Forms, etc.) in iframe or redirects directly. + """ + agent = get_object_or_404(Agent, slug=slug, is_active=True) + + # Verify this is a direct access agent + if not agent.access_url_name or not agent.display_url_name: + messages.error(request, 'This agent does not support direct access.') + return redirect('agents:marketplace') + + # For now, redirect directly to external form + # Future: Can render iframe template or custom display logic + return redirect(agent.webhook_url)