From 2612019a615fd24dabeab66da877ad97c3787a34 Mon Sep 17 00:00:00 2001 From: thecyberlearn Date: Sat, 6 Sep 2025 08:52:12 +0530 Subject: [PATCH] Remove non-essential files and reduce project bloat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove 29 unused files including: - Dokploy and Railway deployment configs - Multiple unused Dockerfiles - Debug/development scripts - Documentation and test files - Generated static files and logs Keep only files essential for CapRover deployment. ๐Ÿค– Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .env.dokploy.example | 24 - .env.production.template | 86 --- .railway.env.example | 43 -- DEPLOYMENT_GUIDE.md | 172 ----- Dockerfile.dokploy | 32 - Dockerfile.dokploy.debug | 57 -- Dockerfile.simple-test | 18 - Dockerfile.test | 39 - WARP.md | 212 ----- agents/brand_presence_analyzer.py | 210 ----- agents/brand_presence_analyzer_pro.py | 1023 ------------------------- build.sh | 12 - debug_middleware.py | 33 - debug_settings.py | 32 - deploy-dokploy.sh | 54 -- docs/AGENT_CREATION.md | 46 -- docs/AGENT_REQUEST_TEMPLATE.md | 152 ---- dokploy.debug.json | 15 - dokploy.json | 25 - production_https_settings.py | 39 - run_dev.sh | 39 - scripts/auto_update_docs.py | 319 -------- scripts/setup_branch_protection.sh | 143 ---- scripts/setup_git_hooks.sh | 120 --- scripts/update_docs_manual.sh | 25 - start-dokploy.sh | 36 - start.sh | 13 - tests/check_agents.py | 30 - tests/simple_test.py | 85 -- tests/test_homepage.py | 74 -- 30 files changed, 3208 deletions(-) delete mode 100644 .env.dokploy.example delete mode 100644 .env.production.template delete mode 100644 .railway.env.example delete mode 100644 DEPLOYMENT_GUIDE.md delete mode 100644 Dockerfile.dokploy delete mode 100644 Dockerfile.dokploy.debug delete mode 100644 Dockerfile.simple-test delete mode 100644 Dockerfile.test delete mode 100644 WARP.md delete mode 100644 agents/brand_presence_analyzer.py delete mode 100644 agents/brand_presence_analyzer_pro.py delete mode 100755 build.sh delete mode 100644 debug_middleware.py delete mode 100644 debug_settings.py delete mode 100755 deploy-dokploy.sh delete mode 100644 docs/AGENT_CREATION.md delete mode 100644 docs/AGENT_REQUEST_TEMPLATE.md delete mode 100644 dokploy.debug.json delete mode 100644 dokploy.json delete mode 100644 production_https_settings.py delete mode 100755 run_dev.sh delete mode 100755 scripts/auto_update_docs.py delete mode 100755 scripts/setup_branch_protection.sh delete mode 100755 scripts/setup_git_hooks.sh delete mode 100755 scripts/update_docs_manual.sh delete mode 100755 start-dokploy.sh delete mode 100755 start.sh delete mode 100644 tests/check_agents.py delete mode 100644 tests/simple_test.py delete mode 100644 tests/test_homepage.py diff --git a/.env.dokploy.example b/.env.dokploy.example deleted file mode 100644 index 20d20bd..0000000 --- a/.env.dokploy.example +++ /dev/null @@ -1,24 +0,0 @@ -# Required Environment Variables for Dokploy Deployment - -# Django Core -SECRET_KEY=your-secret-key-here-generate-a-new-one -DEBUG=false -ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com - -# Database (PostgreSQL recommended for production) -DATABASE_URL=postgres://username:password@hostname:5432/database_name - -# Optional - Email Configuration -EMAIL_HOST_USER=your-email@gmail.com -EMAIL_HOST_PASSWORD=your-app-password - -# Optional - Stripe Payments -STRIPE_PUBLISHABLE_KEY=pk_test_your_stripe_publishable_key -STRIPE_SECRET_KEY=sk_test_your_stripe_secret_key -STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret - -# Optional - Static Files (for custom S3/CDN) -# AWS_ACCESS_KEY_ID=your-aws-access-key -# AWS_SECRET_ACCESS_KEY=your-aws-secret-key -# AWS_STORAGE_BUCKET_NAME=your-bucket-name -# AWS_S3_REGION_NAME=us-east-1 \ No newline at end of file diff --git a/.env.production.template b/.env.production.template deleted file mode 100644 index ef25d32..0000000 --- a/.env.production.template +++ /dev/null @@ -1,86 +0,0 @@ -# ๐Ÿ” Production Environment Variables Template -# Copy this file and replace placeholder values with your actual production values -# NEVER commit this file with real values to version control - -# ======================================== -# ๐Ÿ”’ CORE SECURITY SETTINGS -# ======================================== -SECRET_KEY=django-insecure-REPLACE-WITH-50-RANDOM-CHARACTERS-FOR-PRODUCTION -DEBUG=False -ALLOWED_HOSTS=your-project-name.railway.app,quantumtaskai.com,www.quantumtaskai.com -CSRF_TRUSTED_ORIGINS=https://your-project-name.railway.app,https://quantumtaskai.com,https://www.quantumtaskai.com - -# ======================================== -# ๐Ÿ“ง EMAIL CONFIGURATION -# ======================================== -EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend -EMAIL_HOST=smtp.gmail.com -EMAIL_PORT=587 -EMAIL_USE_TLS=True -EMAIL_HOST_USER=your-email@gmail.com -EMAIL_HOST_PASSWORD=your-16-character-app-password -DEFAULT_FROM_EMAIL=Quantum Tasks AI - -# ======================================== -# ๐Ÿ’ณ STRIPE PAYMENT CONFIGURATION -# ======================================== -STRIPE_SECRET_KEY=sk_live_your_stripe_secret_key_here -STRIPE_WEBHOOK_SECRET=whsec_your_webhook_endpoint_secret - -# ======================================== -# ๐Ÿค– N8N AI AGENT WEBHOOKS (External Server) -# ======================================== -# IMPORTANT: These URLs point to your SEPARATE N8N instance -# Replace with your actual N8N webhook URLs - -# Option A: N8N Cloud -N8N_WEBHOOK_DATA_ANALYZER=https://yourworkspace.app.n8n.cloud/webhook/data-analyzer -N8N_WEBHOOK_FIVE_WHYS=https://yourworkspace.app.n8n.cloud/webhook/five-whys -N8N_WEBHOOK_JOB_POSTING=https://yourworkspace.app.n8n.cloud/webhook/job-posting -N8N_WEBHOOK_SOCIAL_ADS=https://yourworkspace.app.n8n.cloud/webhook/social-ads - -# Option B: Self-hosted N8N (comment out Option A if using this) -# N8N_WEBHOOK_DATA_ANALYZER=https://your-n8n-server.com/webhook/data-analyzer -# N8N_WEBHOOK_FIVE_WHYS=https://your-n8n-server.com/webhook/five-whys -# N8N_WEBHOOK_JOB_POSTING=https://your-n8n-server.com/webhook/job-posting -# N8N_WEBHOOK_SOCIAL_ADS=https://your-n8n-server.com/webhook/social-ads - -# ======================================== -# ๐ŸŒค๏ธ EXTERNAL API KEYS -# ======================================== -OPENWEATHER_API_KEY=your_openweather_api_key_here - -# ======================================== -# โšก PERFORMANCE & CACHING (Optional) -# ======================================== -# Redis URL - Automatically set by Railway Redis service -# REDIS_URL=redis://default:password@host:port - -# ======================================== -# ๐Ÿ” MONITORING & DEBUGGING -# ======================================== -# Optional: Set to your admin email for notifications -ADMIN_EMAIL=abhay@quantumtaskai.com - -# ======================================== -# ๐Ÿ“Š ANALYTICS (Optional) -# ======================================== -# Add analytics service keys if needed -# GOOGLE_ANALYTICS_ID=your_ga_id_here - -# ======================================== -# NOTES FOR SETUP -# ======================================== -# 1. Generate SECRET_KEY using: python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())" -# 2. EMAIL_HOST_PASSWORD should be Gmail App Password (16 characters), not regular password -# 3. Use LIVE Stripe keys for production (sk_live_... and whsec_...) -# 4. N8N webhooks must be on external server accessible via HTTPS -# 5. Test all variables before deploying to production - -# ======================================== -# RAILWAY AUTOMATIC VARIABLES -# ======================================== -# These are automatically set by Railway - DO NOT SET MANUALLY: -# - DATABASE_URL (PostgreSQL connection string) -# - PORT (Application port) -# - RAILWAY_* (Railway-specific variables) \ No newline at end of file diff --git a/.railway.env.example b/.railway.env.example deleted file mode 100644 index b160b0b..0000000 --- a/.railway.env.example +++ /dev/null @@ -1,43 +0,0 @@ -# Railway Environment Variables Template -# Copy this to Railway dashboard for environment-specific deployments - -# Django Settings -DEBUG=False -SECRET_KEY=your-production-secret-key-here -ALLOWED_HOSTS=your-domain.railway.app,www.quantumtaskai.com - -# Database -DATABASE_URL=postgresql://user:password@host:port/database - -# Stripe Configuration -STRIPE_PUBLISHABLE_KEY=pk_live_your_publishable_key -STRIPE_SECRET_KEY=sk_live_your_secret_key -STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret - -# Email Configuration -EMAIL_HOST=smtp.gmail.com -EMAIL_PORT=587 -EMAIL_USE_TLS=True -EMAIL_HOST_USER=your-email@gmail.com -EMAIL_HOST_PASSWORD=your-app-password - -# Admin Configuration -DJANGO_SUPERUSER_USERNAME=admin -DJANGO_SUPERUSER_EMAIL=admin@quantumtaskai.com -DJANGO_SUPERUSER_PASSWORD=your-secure-admin-password - -# N8N Webhook URLs (Production) -N8N_WEBHOOK_DATA_ANALYZER=https://your-n8n-instance.com/webhook/data-analyzer -N8N_WEBHOOK_SOCIAL_ADS=https://your-n8n-instance.com/webhook/social-ads -N8N_WEBHOOK_JOB_POSTING=https://your-n8n-instance.com/webhook/job-posting -N8N_WEBHOOK_FIVE_WHYS=https://your-n8n-instance.com/webhook/five-whys - -# Security Settings -SECURE_SSL_REDIRECT=True -SECURE_HSTS_SECONDS=31536000 -SECURE_HSTS_INCLUDE_SUBDOMAINS=True -SECURE_FRAME_DENY=True - -# Deployment Control -DEPLOYMENT_ENVIRONMENT=production # production, staging, development -BRANCH_NAME=main # Track which branch is deployed \ No newline at end of file diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md deleted file mode 100644 index 590701f..0000000 --- a/DEPLOYMENT_GUIDE.md +++ /dev/null @@ -1,172 +0,0 @@ -# CapRover Deployment Guide - Quantum Tasks AI - -## Overview -Step-by-step guide for deploying the Quantum Tasks AI Django application on CapRover. - -## Prerequisites -- CapRover installed and running on your VPS -- GitHub repository with the project -- Basic understanding of Django and CapRover - ---- - -## Part 1: Project Setup - -### Required Files -Your repository contains these CapRover-ready files: -``` -quantumtaskai-caprover/ -โ”œโ”€โ”€ captain-definition # CapRover configuration -โ”œโ”€โ”€ Dockerfile.captain # Production Docker setup -โ”œโ”€โ”€ requirements.txt # Essential Python dependencies -โ”œโ”€โ”€ .dockerignore # Docker build optimization -โ””โ”€โ”€ netcop_hub/settings.py # Django settings with production support -``` - -### Key Features -- **Database**: Supports SQLite (dev), PostgreSQL (production) -- **Static Files**: WhiteNoise for production serving -- **Environment Variables**: Production-ready configuration - ---- - -## Part 2: CapRover Deployment - -### Step 1: Create New App -1. **Open CapRover Dashboard** -2. **Apps โ†’ Create New App** -3. **App Name**: `quantumtaskai` (or your preferred name) -4. **Click "Create New App"** - -### Step 2: Configure GitHub Deployment -1. **Go to**: App โ†’ Deployment Tab -2. **Method**: Deploy from GitHub -3. **Repository**: `https://github.com/thecyberlearn/quantumtaskai-caprover.git` -4. **Branch**: `main` -5. **Authentication**: Use GitHub Personal Access Token - -### Step 3: Environment Variables -**Go to**: App Configs โ†’ Environment Variables โ†’ Bulk Edit - -**Essential Variables:** -```env -SECRET_KEY=your-unique-secret-key-here -DEBUG=false -ALLOWED_HOSTS=yourapp.yourdomain.com -DATABASE_URL=postgres://user:password@host:5432/database -``` - -**Optional Variables:** -```env -# Email Configuration (for notifications) -EMAIL_HOST_USER=your-email@gmail.com -EMAIL_HOST_PASSWORD=your-app-password - -# Stripe Configuration (for payments) -STRIPE_SECRET_KEY=sk_test_your_stripe_key -STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret -``` - -### Step 4: Deploy Application -1. **Deployment Tab** โ†’ **Deploy Now** -2. **Monitor build logs** for successful completion -3. **Check App URL** after deployment completes - ---- - -## Part 3: Database Setup (Optional) - -If you need PostgreSQL database: - -### Option A: CapRover PostgreSQL -1. **One-Click Apps** โ†’ **PostgreSQL** -2. **Create database instance** -3. **Get connection details** from app configs -4. **Add DATABASE_URL** to your app environment variables - -### Option B: External Database -1. **Use Railway, Neon, or other PostgreSQL provider** -2. **Get connection string** -3. **Add to environment variables** - ---- - -## Part 4: Custom Domain (Optional) - -1. **App Settings** โ†’ **HTTP Settings** -2. **Add your domain**: `yourdomain.com` -3. **Enable HTTPS**: Force HTTPS redirect -4. **Update DNS**: Point your domain to CapRover server IP - ---- - -## Part 5: Post-Deployment - -### Create Admin User -1. **App โ†’ Web Terminal** -2. **Run commands**: -```bash -python manage.py migrate -python manage.py createsuperuser -``` - -### Verify Deployment -1. **Visit your app URL** -2. **Check admin panel**: `/admin/` -3. **Test agent marketplace**: `/agents/` - ---- - -## Troubleshooting - -### Build Failures -- **Check logs** in Deployment tab -- **Verify environment variables** are set -- **Ensure SECRET_KEY** is properly set - -### Runtime Issues -- **Check App Logs** in CapRover dashboard -- **Verify DATABASE_URL** format -- **Check ALLOWED_HOSTS** includes your domain - -### Database Issues -- **Run migrations**: `python manage.py migrate` -- **Check database connectivity** -- **Verify PostgreSQL is running** (if using) - ---- - -## Environment Variable Reference - -| Variable | Required | Description | -|----------|----------|-------------| -| `SECRET_KEY` | Yes | Django secret key | -| `DEBUG` | Yes | Set to `false` for production | -| `ALLOWED_HOSTS` | Yes | Your domain name | -| `DATABASE_URL` | Optional | PostgreSQL connection string | -| `EMAIL_HOST_USER` | Optional | SMTP email username | -| `EMAIL_HOST_PASSWORD` | Optional | SMTP email password | -| `STRIPE_SECRET_KEY` | Optional | Stripe API key | - ---- - -## Success Checklist - -- [ ] App builds successfully in CapRover -- [ ] Environment variables configured -- [ ] Database migrations completed -- [ ] Admin user created -- [ ] App accessible via URL -- [ ] Static files loading correctly -- [ ] Agent marketplace functional - -Your Quantum Tasks AI application should now be live and ready to use! - ---- - -## Support - -For issues with: -- **CapRover deployment**: Check CapRover documentation -- **Django configuration**: Review `netcop_hub/settings.py` -- **Agent system**: See `agents/` directory structure \ No newline at end of file diff --git a/Dockerfile.dokploy b/Dockerfile.dokploy deleted file mode 100644 index 3d4c0c9..0000000 --- a/Dockerfile.dokploy +++ /dev/null @@ -1,32 +0,0 @@ -FROM python:3.11-slim - -WORKDIR /app - -# Install system dependencies -RUN apt-get update && apt-get install -y \ - gcc \ - libpq-dev \ - && rm -rf /var/lib/apt/lists/* - -# Copy and install Python dependencies -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt - -# Copy application code -COPY . . - -# Set environment variables -ENV PYTHONUNBUFFERED=1 \ - SECRET_KEY="build-time-dummy-key-change-in-production" - -# Collect static files -RUN python manage.py collectstatic --noinput --settings=production_https_settings - -# Make startup script executable -RUN chmod +x start-dokploy.sh - -# Expose port 3000 for Dokploy -EXPOSE 3000 - -# Use startup script -CMD ["./start-dokploy.sh"] diff --git a/Dockerfile.dokploy.debug b/Dockerfile.dokploy.debug deleted file mode 100644 index 11e24d0..0000000 --- a/Dockerfile.dokploy.debug +++ /dev/null @@ -1,57 +0,0 @@ -FROM python:3.11-slim - -WORKDIR /app - -# Install system dependencies -RUN apt-get update && apt-get install -y \ - gcc \ - libpq-dev \ - curl \ - && rm -rf /var/lib/apt/lists/* - -# Copy and install Python dependencies -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt - -# Copy application code -COPY . . - -# Set environment variables -ENV PYTHONUNBUFFERED=1 \ - DEBUG=true \ - SECRET_KEY="build-time-dummy-key-change-in-production" \ - ALLOWED_HOSTS="*" - -# Collect static files -RUN python manage.py collectstatic --noinput --settings=debug_settings || true - -# Make startup script executable -RUN chmod +x start-dokploy.sh - -# Expose port 3000 for Dokploy -EXPOSE 3000 - -# Debug: Print Django info -RUN python manage.py --version -RUN python manage.py check --settings=debug_settings || true - -# Start with debug info -CMD echo "๐Ÿ› Debug Mode - Django $(python manage.py --version)" && \ - echo "๐Ÿ” Environment:" && \ - echo " - DEBUG: $DEBUG" && \ - echo " - ALLOWED_HOSTS: $ALLOWED_HOSTS" && \ - echo " - SECRET_KEY: $(echo $SECRET_KEY | cut -c1-10)..." && \ - echo "๐ŸŒ Testing Django config..." && \ - python manage.py check --settings=debug_settings && \ - echo "โœ… Django config OK" && \ - echo "๐Ÿงช Testing URL patterns..." && \ - python manage.py show_urls --settings=debug_settings || echo "show_urls not available" && \ - echo "๐Ÿš€ Starting server with detailed logging..." && \ - DJANGO_SETTINGS_MODULE=debug_settings gunicorn netcop_hub.wsgi:application - --bind 0.0.0.0:3000 \ - --workers 1 \ - --timeout 120 \ - --log-level debug \ - --access-logfile - \ - --error-logfile - \ - --capture-output diff --git a/Dockerfile.simple-test b/Dockerfile.simple-test deleted file mode 100644 index 290ac99..0000000 --- a/Dockerfile.simple-test +++ /dev/null @@ -1,18 +0,0 @@ -FROM python:3.11-slim - -WORKDIR /app - -# Create a simple test file -RUN echo "

๐Ÿš€ Container is Running!

Port 3000 is accessible

Time: $(date)

" > index.html - -# Expose port 3000 -EXPOSE 3000 - -# Start simple HTTP server -CMD echo "๐Ÿงช Simple HTTP Server Test" && \ - echo "๐Ÿ“‹ Container Details:" && \ - echo " - Time: $(date)" && \ - echo " - Port: 3000" && \ - echo " - Files: $(ls -la)" && \ - echo "๐ŸŒ Starting HTTP server on port 3000..." && \ - python -m http.server 3000 diff --git a/Dockerfile.test b/Dockerfile.test deleted file mode 100644 index 1506f0b..0000000 --- a/Dockerfile.test +++ /dev/null @@ -1,39 +0,0 @@ -FROM python:3.11-slim - -WORKDIR /app - -# Install system dependencies -RUN apt-get update && apt-get install -y \ - gcc \ - libpq-dev \ - curl \ - && rm -rf /var/lib/apt/lists/* - -# Copy and install Python dependencies -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt - -# Copy application code -COPY . . - -# Set minimal environment variables -ENV PYTHONUNBUFFERED=1 \ - DEBUG=true \ - SECRET_KEY="test-secret-key-123456789" \ - ALLOWED_HOSTS="*" - -# Expose port 3000 -EXPOSE 3000 - -# Very simple test - just run a basic HTTP server -CMD echo "๐Ÿงช TEST MODE - Container is running!" && \ - echo "๐Ÿ“‹ Environment Check:" && \ - echo " - Python: $(python --version)" && \ - echo " - Django: $(python -c 'import django; print(django.get_version())')" && \ - echo " - Working directory: $(pwd)" && \ - echo " - Files: $(ls -la | head -10)" && \ - echo "" && \ - echo "๐ŸŒ Testing Django..." && \ - python manage.py check --settings=netcop_hub.settings && \ - echo "โœ… Django OK - Starting simple HTTP server on port 3000" && \ - python -m http.server 3000 diff --git a/WARP.md b/WARP.md deleted file mode 100644 index 807b450..0000000 --- a/WARP.md +++ /dev/null @@ -1,212 +0,0 @@ -# WARP.md - -This file provides guidance to WARP (warp.dev) when working with code in this repository. - -## Project Overview - -**Quantum Tasks AI** is a Django-based AI agent marketplace that allows users to browse, execute, and interact with various AI-powered tools and services. The application is designed for deployment on container platforms like CapRover and Dokploy. - -### Core Architecture - -- **Framework**: Django 5.2.4 with Django REST Framework -- **Database**: SQLite (development) / PostgreSQL (production) -- **Static Files**: WhiteNoise for production serving -- **Agent System**: File-based JSON configuration system -- **Authentication**: Custom User model with email verification -- **Payments**: Stripe integration for agent execution fees -- **Deployment**: Containerized with Docker, optimized for CapRover/Dokploy - -### Application Structure - -``` -โ”œโ”€โ”€ agents/ # AI agent marketplace and execution system -โ”œโ”€โ”€ authentication/ # User management and authentication -โ”œโ”€โ”€ core/ # Homepage, health checks, and utilities -โ”œโ”€โ”€ wallet/ # Stripe payment integration -โ”œโ”€โ”€ netcop_hub/ # Django project settings and configuration -โ”œโ”€โ”€ static/ # Static assets (CSS, JS, images) -โ”œโ”€โ”€ templates/ # Django HTML templates -โ””โ”€โ”€ agents/configs/ # File-based agent configurations - โ”œโ”€โ”€ agents/ # Individual agent JSON files - โ””โ”€โ”€ categories/ # Agent categories configuration -``` - -## Development Commands - -### Local Development Setup - -```bash -# Install dependencies -pip install -r requirements.txt - -# Database setup -python manage.py migrate -python manage.py createsuperuser - -# Development server -python manage.py runserver -# OR use the development script -./run_dev.sh -``` - -### Testing and Quality - -```bash -# Run Django checks -python manage.py check - -# Test database connection -python manage.py check --database default - -# Clear agent cache (useful during development) -python manage.py shell -c "from agents.services import AgentFileService; AgentFileService.clear_cache()" -``` - -### Static Files and Assets - -```bash -# Collect static files for production -python manage.py collectstatic --noinput - -# Clear cache table -python manage.py createcachetable -``` - -## Agent System Architecture - -### File-Based Configuration -The application uses a file-based agent system instead of database models for agent configurations: - -- **Agent configs**: `agents/configs/agents/*.json` -- **Categories**: `agents/configs/categories/categories.json` -- **Service class**: `AgentFileService` handles loading and caching - -### Agent Configuration Format -```json -{ - "slug": "agent-identifier", - "name": "Human Readable Name", - "description": "Detailed description", - "category": "category-slug", - "price": 0.0, - "agent_type": "form", - "system_type": "webhook", - "webhook_url": "https://external-service.com/webhook", - "form_schema": { - "fields": [ - {"name": "input", "type": "text", "label": "Input Field"} - ] - } -} -``` - -### Agent Execution Flow -1. User selects agent from marketplace (`/agents/`) -2. Fills out dynamic form based on `form_schema` -3. Payment processed via Stripe (if price > 0) -4. Request sent to agent's `webhook_url` -5. Response stored in `AgentExecution` model -6. Results displayed to user - -## Deployment Configurations - -### CapRover Deployment -- **Docker file**: `Dockerfile.captain` -- **Configuration**: `captain-definition` -- **Port**: 80 -- **Startup**: Direct gunicorn execution - -### Dokploy Deployment -- **Docker file**: `Dockerfile.dokploy` -- **Configuration**: `dokploy.json` -- **Port**: 3000 -- **Startup**: `start-dokploy.sh` script with migrations - -### Environment Variables - -**Required for Production:** -```env -SECRET_KEY=your-secret-key-here -DEBUG=false -ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com -``` - -**Optional:** -```env -DATABASE_URL=postgres://user:pass@host:5432/db -EMAIL_HOST_USER=your-email@gmail.com -EMAIL_HOST_PASSWORD=your-app-password -STRIPE_SECRET_KEY=sk_live_your_stripe_key -``` - -**Platform-Specific:** -```env -# CapRover auto-detection -CAPROVER_GIT_COMMIT_SHA=auto-set-by-caprover - -# Dokploy auto-detection -DOKPLOY_PROJECT_NAME=your-project-name -``` - -## Key Application Features - -### Authentication System -- Custom User model with email verification -- Password reset functionality -- User wallet balance tracking -- Session management with security headers - -### Payment Integration -- Stripe checkout for agent executions -- Wallet balance system -- Transaction logging -- Webhook handling for payment confirmations - -### Agent Marketplace -- Category-based organization -- Search and filtering capabilities -- Dynamic form generation based on agent schemas -- Execution history tracking - -### Security Features -- CSRF protection with trusted origins -- Rate limiting on critical endpoints -- Security headers (CSP, XSS protection) -- Input validation and sanitization -- Error handling with custom error pages - -## Troubleshooting Common Issues - -### Dokploy 404 Errors -If getting 404 errors on Dokploy: -1. Ensure `ALLOWED_HOSTS` includes the Dokploy domain -2. Check that port 3000 is correctly configured -3. Verify health check endpoint `/health/` is accessible -4. Use debug configuration temporarily: `Dockerfile.dokploy.debug` - -### Database Issues -- SQLite is used for development (no setup required) -- PostgreSQL for production (requires `DATABASE_URL`) -- Run migrations after deployment: `python manage.py migrate` - -### Static Files Problems -- Ensure `python manage.py collectstatic` runs during build -- WhiteNoise handles static file serving in production -- Check `STATIC_ROOT` and `STATICFILES_DIRS` configuration - -### Agent Loading Issues -- Agent configs are cached for performance -- Clear cache during development: `AgentFileService.clear_cache()` -- Check JSON syntax in agent configuration files -- Ensure required fields are present in agent schemas - -## Important File Locations - -- **Main settings**: `netcop_hub/settings.py` -- **URL configuration**: `netcop_hub/urls.py` -- **Agent service**: `agents/services.py` -- **Health check**: `core/views.py` (health_check_view) -- **Error handlers**: `core/error_views.py` -- **Production startup**: `start-dokploy.sh` - -This Django application is optimized for containerized deployment with focus on AI agent marketplace functionality, file-based configuration management, and production-ready security features. diff --git a/agents/brand_presence_analyzer.py b/agents/brand_presence_analyzer.py deleted file mode 100644 index 840c5be..0000000 --- a/agents/brand_presence_analyzer.py +++ /dev/null @@ -1,210 +0,0 @@ -""" -Brand Digital Presence Analyzer - -Python implementation for analyzing brand presence across 14 major digital platforms -using Groq for fast and cost-effective AI analysis. -""" - -import json -import logging -from groq import Groq -from datetime import datetime -from typing import Dict, Any, Optional -from django.conf import settings - -logger = logging.getLogger(__name__) - -class BrandPresenceAnalyzer: - """ - Analyzes brand digital presence across major platforms using Groq AI. - """ - - def __init__(self): - """Initialize the analyzer with Groq configuration.""" - self.client = None - if settings.GROQ_API_KEY: - self.client = Groq(api_key=settings.GROQ_API_KEY) - else: - logger.warning("Groq API key not configured") - - def analyze_brand_presence(self, brand_name: str, website_url: str) -> Dict[str, Any]: - """ - Analyze brand presence across 14 digital platforms. - - Args: - brand_name: The brand name to search for - website_url: The brand's official website URL - - Returns: - Dictionary containing analysis results in structured format - """ - if not self.client: - return self._create_error_response("Groq API key not configured") - - if not brand_name or not website_url: - return self._create_error_response("Brand name and website URL are required") - - try: - logger.info(f"Starting brand presence analysis for: {brand_name}") - - # Create the analysis prompt - prompt = self._create_analysis_prompt(brand_name, website_url) - - # Call Groq API - response = self.client.chat.completions.create( - model="llama-3.1-8b-instant", # Fast and current model - messages=[ - { - "role": "system", - "content": "You are a digital marketing analyst. Return only valid JSON with no additional text or formatting." - }, - { - "role": "user", - "content": prompt - } - ], - temperature=0.3, - max_tokens=2500 - ) - - # Extract and parse the response - ai_response = response.choices[0].message.content.strip() - logger.info(f"Received AI response for {brand_name}") - - # Parse JSON response - try: - result = json.loads(ai_response) - return self._format_success_response(result, brand_name, website_url) - except json.JSONDecodeError as e: - logger.error(f"Failed to parse AI response as JSON: {e}") - logger.error(f"Raw response: {ai_response}") - return self._create_error_response("Invalid response format from AI") - - except Exception as e: - logger.error(f"Error during brand presence analysis: {e}") - return self._create_error_response(f"Analysis failed: {str(e)}") - - def _create_analysis_prompt(self, brand_name: str, website_url: str) -> str: - """Create the detailed analysis prompt for the AI.""" - return f""" -You are a digital marketing analyst specializing in brand presence research. Analyze the given brand's presence across 14 major digital platforms. - -BRAND INFORMATION: -Brand Name: {brand_name} -Website: {website_url} - -PLATFORMS TO ANALYZE: -1. Google Business -2. LinkedIn (Company Pages) -3. YouTube -4. TikTok -5. Instagram -6. Pinterest -7. X (Twitter) -8. Facebook (Business Pages) -9. Medium -10. Tumblr -11. Threads -12. Quora -13. Reddit -14. Blue Sky - -SEARCH METHODOLOGY: -- Search for exact brand name matches -- Try variations (official, verified, brand + industry terms) -- Cross-reference with the provided website URL -- Look for verification badges and official indicators -- Assess account activity and authenticity - -RETURN ONLY THIS JSON FORMAT (no additional text): - -{{ - "platforms": [ - {{ - "name": "Google Business", - "found": true, - "verified": true, - "profile_url": "https://example.com/profile", - "confidence": "high", - "notes": "Verified business listing with reviews" - }}, - {{ - "name": "LinkedIn", - "found": false, - "verified": null, - "profile_url": null, - "confidence": null, - "notes": "No official company page found" - }} - ], - "summary": {{ - "total_platforms_checked": 14, - "platforms_found": 8, - "platforms_missing": 6, - "completion_percentage": 57 - }}, - "recommendations": [ - {{ - "platform": "LinkedIn", - "priority": "high", - "reason": "Essential for B2B networking and credibility" - }} - ] -}} - -IMPORTANT: -- Return ONLY valid JSON -- Set confidence as "high", "medium", or "low" -- Use null for missing data -- Include brief, helpful notes for each platform -- Focus on official business accounts, not personal profiles -- If unsure, mark confidence as "low" and explain in notes -- Ensure all 14 platforms are included in the platforms array -""" - - def _format_success_response(self, ai_result: Dict, brand_name: str, website_url: str) -> Dict[str, Any]: - """Format the successful analysis response.""" - return { - "status": "success", - "brand_analysis": { - "brand_name": brand_name, - "website": website_url, - "analysis_date": datetime.now().isoformat(), - "processing_time": "AI-powered analysis" - }, - "data": ai_result, - "meta": { - "analyzer_version": "1.0", - "platforms_supported": 14, - "analysis_method": "AI-powered research" - } - } - - def _create_error_response(self, error_message: str) -> Dict[str, Any]: - """Create standardized error response.""" - return { - "status": "error", - "error": { - "message": error_message, - "timestamp": datetime.now().isoformat(), - "code": "ANALYSIS_FAILED" - } - } - -# Global analyzer instance - commented out to avoid import-time initialization -# analyzer = BrandPresenceAnalyzer() - -def analyze_brand_presence(brand_name: str, website_url: str) -> Dict[str, Any]: - """ - Convenience function for analyzing brand presence. - - Args: - brand_name: The brand name to analyze - website_url: The brand's website URL - - Returns: - Analysis results dictionary - """ - # Create analyzer instance when needed to avoid import-time initialization - analyzer = BrandPresenceAnalyzer() - return analyzer.analyze_brand_presence(brand_name, website_url) \ No newline at end of file diff --git a/agents/brand_presence_analyzer_pro.py b/agents/brand_presence_analyzer_pro.py deleted file mode 100644 index 03a4698..0000000 --- a/agents/brand_presence_analyzer_pro.py +++ /dev/null @@ -1,1023 +0,0 @@ -""" -Brand Digital Presence Analyzer Pro - -Enhanced Python implementation for analyzing brand presence across 14 major digital platforms -using SERP API for real-time search and OpenAI GPT-4 for superior analysis. -""" - -import json -import logging -import requests -import openai -import time -import re -from datetime import datetime -from typing import Dict, Any, Optional, List -from django.conf import settings -from django.template.loader import render_to_string - -logger = logging.getLogger(__name__) - -class BrandPresenceAnalyzerPro: - """ - Enhanced brand presence analyzer with real-time SERP API search and GPT-4 analysis. - """ - - PLATFORMS = [ - "Google Business", - "LinkedIn", - "YouTube", - "TikTok", - "Instagram", - "Pinterest", - "X (Twitter)", - "Facebook", - "Medium", - "Tumblr", - "Threads", - "Quora", - "Reddit", - "Blue Sky" - ] - - def __init__(self): - """Initialize the analyzer with OpenAI and SERP API configuration.""" - self.openai_configured = False - self.serp_api_key = None - - # Initialize OpenAI (legacy v0.28.1) - if hasattr(settings, 'OPENAI_API_KEY') and settings.OPENAI_API_KEY: - try: - openai.api_key = settings.OPENAI_API_KEY - self.openai_configured = True - except Exception as e: - logger.warning(f"Failed to configure OpenAI: {e}") - self.openai_configured = False - else: - logger.warning("OpenAI API key not configured") - - # Initialize SERP API (SerpAPI preferred, ValueSERP fallback) - if hasattr(settings, 'SERPAPI_API_KEY') and settings.SERPAPI_API_KEY: - self.serp_api_key = settings.SERPAPI_API_KEY - self.serp_provider = 'serpapi' - elif hasattr(settings, 'VALUESERP_API_KEY') and settings.VALUESERP_API_KEY: - self.serp_api_key = settings.VALUESERP_API_KEY - self.serp_provider = 'valueserp' - else: - self.serp_api_key = None - self.serp_provider = None - logger.warning("SERP API key not configured (tried SerpAPI and ValueSERP)") - - def analyze_brand_presence(self, brand_name: str, website_url: str, include_competitor_analysis: bool = False) -> Dict[str, Any]: - """ - Analyze brand presence across 14 digital platforms with real-time search. - - Args: - brand_name: The brand name to search for - website_url: The brand's official website URL - include_competitor_analysis: Whether to include competitor analysis - - Returns: - Dictionary containing comprehensive analysis results - """ - if not self.openai_configured: - return self._create_error_response("OpenAI API key not configured") - - if not self.serp_api_key: - return self._create_error_response("SERP API key not configured") - - if not brand_name or not website_url: - return self._create_error_response("Brand name and website URL are required") - - try: - logger.info(f"Starting enhanced brand presence analysis for: {brand_name}") - - # Step 1: Perform real-time SERP searches for each platform - platform_search_results = self._search_platforms(brand_name, website_url) - - # Step 2: Check if we have any valid search results - has_valid_results = any( - len(data.get('results', [])) > 0 and not data.get('error') - for data in platform_search_results.values() - ) - - if has_valid_results: - # Step 2a: Analyze SERP results with GPT-4 - analysis_result = self._analyze_with_gpt4(brand_name, website_url, platform_search_results) - else: - # Step 2b: Use GPT-4 knowledge-based analysis as fallback - logger.warning(f"No valid SERP results for {brand_name}, using knowledge-based analysis") - analysis_result = self._analyze_with_gpt4_knowledge(brand_name, website_url) - - # Step 3: Add competitor analysis if requested - competitor_data = {} - if include_competitor_analysis: - competitor_data = self._analyze_competitors(brand_name) - - # Step 4: Generate insights and recommendations - insights = self._generate_insights(analysis_result, competitor_data) - - return self._format_success_response( - analysis_result, - brand_name, - website_url, - competitor_data, - insights - ) - - except Exception as e: - logger.error(f"Error during enhanced brand presence analysis: {e}") - return self._create_error_response(f"Analysis failed: {str(e)}") - - def _search_platforms(self, brand_name: str, website_url: str) -> Dict[str, Any]: - """ - Perform real-time SERP searches for brand presence on each platform. - """ - search_results = {} - base_domain = website_url.replace('https://', '').replace('http://', '').replace('www.', '').split('/')[0] - - for platform in self.PLATFORMS: - try: - # Construct search query for each platform - search_queries = self._get_platform_search_queries(brand_name, platform, base_domain) - - platform_results = [] - for i, query in enumerate(search_queries[:1]): # Limit to 1 search per platform for performance - result = self._perform_serp_search(query) - if result: - platform_results.append(result) - # If we found results, no need to search more for this platform - if result.get('results_count', 0) > 0: - break - - # Add small delay between searches to avoid rate limiting - if i < len(search_queries) - 1: - time.sleep(0.2) - - search_results[platform] = { - 'queries_performed': len(platform_results), - 'results': platform_results, - 'timestamp': datetime.now().isoformat() - } - - except Exception as e: - logger.error(f"Error searching for {platform}: {e}") - search_results[platform] = { - 'queries_performed': 0, - 'results': [], - 'error': str(e) - } - - return search_results - - def _get_platform_search_queries(self, brand_name: str, platform: str, base_domain: str) -> List[str]: - """ - Generate targeted search queries for each platform. - """ - platform_domains = { - "Google Business": ["business.google.com", "google.com/maps"], - "LinkedIn": ["linkedin.com/company", "linkedin.com/in"], - "YouTube": ["youtube.com"], - "TikTok": ["tiktok.com"], - "Instagram": ["instagram.com"], - "Pinterest": ["pinterest.com"], - "X (Twitter)": ["x.com", "twitter.com"], - "Facebook": ["facebook.com"], - "Medium": ["medium.com"], - "Tumblr": ["tumblr.com"], - "Threads": ["threads.net"], - "Quora": ["quora.com"], - "Reddit": ["reddit.com"], - "Blue Sky": ["bsky.app", "blueskyweb.xyz"] - } - - domains = platform_domains.get(platform, [platform.lower().replace(' ', '').replace('(', '').replace(')', '') + '.com']) - - queries = [] - for domain in domains: - queries.extend([ - f'site:{domain} "{brand_name}"', - f'site:{domain} {brand_name} {base_domain}' - ]) - - return queries - - def _perform_serp_search(self, query: str) -> Optional[Dict[str, Any]]: - """ - Perform a single SERP search using configured SERP provider. - """ - try: - if self.serp_provider == 'serpapi': - return self._perform_serpapi_search(query) - elif self.serp_provider == 'valueserp': - return self._perform_valueserp_search(query) - else: - logger.error("No SERP provider configured") - return None - - except Exception as e: - logger.error(f"SERP search failed for query '{query}': {e}") - return None - - def _perform_serpapi_search(self, query: str) -> Optional[Dict[str, Any]]: - """ - Perform search using SerpAPI. - """ - try: - url = "https://serpapi.com/search" - params = { - 'api_key': self.serp_api_key, - 'q': query, - 'location': 'United States', - 'hl': 'en', - 'gl': 'us', - 'num': 10, - 'engine': 'google' - } - - response = requests.get(url, params=params, timeout=10) - response.raise_for_status() - - data = response.json() - - # Handle SerpAPI error responses - if 'error' in data: - logger.error(f"SerpAPI error: {data.get('error')}") - return None - - organic_results = data.get('organic_results', []) - return { - 'query': query, - 'results_count': len(organic_results), - 'organic_results': organic_results[:5], # Top 5 results - 'search_information': data.get('search_information', {}), - 'search_metadata': data.get('search_metadata', {}), - 'timestamp': datetime.now().isoformat(), - 'provider': 'serpapi' - } - - except requests.RequestException as e: - logger.error(f"SerpAPI request failed for query '{query}': {e}") - return None - except Exception as e: - logger.error(f"SerpAPI error for query '{query}': {e}") - return None - - def _perform_valueserp_search(self, query: str) -> Optional[Dict[str, Any]]: - """ - Perform search using ValueSERP (fallback). - """ - try: - url = "https://api.valueserp.com/search" - params = { - 'api_key': self.serp_api_key, - 'q': query, - 'location': 'United States', - 'google_domain': 'google.com', - 'gl': 'us', - 'hl': 'en', - 'num': 10, - 'output': 'json' - } - - response = requests.get(url, params=params, timeout=10) - response.raise_for_status() - - data = response.json() - return { - 'query': query, - 'results_count': len(data.get('organic_results', [])), - 'organic_results': data.get('organic_results', [])[:5], # Top 5 results - 'search_information': data.get('search_information', {}), - 'timestamp': datetime.now().isoformat(), - 'provider': 'valueserp' - } - - except Exception as e: - logger.error(f"ValueSERP search failed for query '{query}': {e}") - return None - - def _analyze_with_gpt4(self, brand_name: str, website_url: str, search_results: Dict[str, Any]) -> Dict[str, Any]: - """ - Analyze SERP search results using GPT-4 for intelligent brand presence detection. - """ - try: - # Create comprehensive prompt with search results - prompt = self._create_gpt4_analysis_prompt(brand_name, website_url, search_results) - - response = openai.ChatCompletion.create( - model="gpt-4o", - messages=[ - { - "role": "system", - "content": "You are an expert digital marketing analyst specializing in brand presence research. Analyze the provided SERP search results to determine brand presence across platforms. Return only valid JSON with no additional text." - }, - { - "role": "user", - "content": prompt - } - ], - temperature=0.2, - max_tokens=3000 - ) - - ai_response = response.choices[0].message.content.strip() - logger.info(f"Received GPT-4 analysis for {brand_name}") - - # Parse JSON response (handle markdown code blocks) - try: - # Clean up response - remove markdown code blocks if present - clean_response = ai_response - if ai_response.startswith('```json'): - clean_response = ai_response.replace('```json', '').replace('```', '').strip() - elif ai_response.startswith('```'): - clean_response = ai_response.replace('```', '').strip() - - result = json.loads(clean_response) - return result - except json.JSONDecodeError as e: - logger.error(f"Failed to parse GPT-4 response as JSON: {e}") - logger.error(f"Raw response: {ai_response}") - return self._create_fallback_analysis(search_results) - - except Exception as e: - logger.error(f"GPT-4 analysis failed: {e}") - return self._create_fallback_analysis(search_results) - - def _analyze_with_gpt4_knowledge(self, brand_name: str, website_url: str) -> Dict[str, Any]: - """ - Use GPT-4's knowledge to analyze brand presence when SERP API is unavailable. - """ - try: - prompt = f""" -You are a digital marketing analyst. Analyze the brand "{brand_name}" (website: {website_url}) for its likely presence across 14 major digital platforms based on your knowledge. - -Consider: -- Brand size and industry -- Typical platform usage patterns -- Official social media strategy -- Business model (B2B vs B2C) - -PLATFORMS TO ANALYZE: -{', '.join(self.PLATFORMS)} - -RETURN ONLY THIS JSON FORMAT (no additional text): - -{{ - "platforms": [ - {{ - "name": "Platform Name", - "found": true/false, - "verified": true/false/null, - "profile_url": "likely_url_or_null", - "confidence": "high/medium/low", - "search_ranking": null, - "notes": "knowledge-based assessment", - "activity_level": "high/medium/low/unknown", - "last_updated": null - }} - ], - "summary": {{ - "total_platforms_checked": 14, - "platforms_found": 0, - "platforms_missing": 0, - "completion_percentage": 0, - "verification_rate": 0, - "average_search_ranking": 0.0 - }} -}} - -IMPORTANT: -- Base assessment on your knowledge of this brand -- Mark confidence as "medium" for knowledge-based analysis -- Provide realistic likelihood of presence -- Include likely URLs where appropriate -- Calculate accurate summary statistics -""" - - response = openai.ChatCompletion.create( - model="gpt-4o", - messages=[ - { - "role": "system", - "content": "You are an expert digital marketing analyst. Use your knowledge to assess brand presence across platforms when search data is unavailable. Return only valid JSON." - }, - { - "role": "user", - "content": prompt - } - ], - temperature=0.2, - max_tokens=2500 - ) - - ai_response = response.choices[0].message.content.strip() - logger.info(f"Received GPT-4 knowledge-based analysis for {brand_name}") - - # Parse JSON response (handle markdown code blocks) - try: - # Clean up response - remove markdown code blocks if present - clean_response = ai_response - if ai_response.startswith('```json'): - clean_response = ai_response.replace('```json', '').replace('```', '').strip() - elif ai_response.startswith('```'): - clean_response = ai_response.replace('```', '').strip() - - result = json.loads(clean_response) - return result - except json.JSONDecodeError as e: - logger.error(f"Failed to parse GPT-4 knowledge response as JSON: {e}") - logger.error(f"Raw response: {ai_response}") - return self._create_knowledge_fallback_analysis(brand_name) - - except Exception as e: - logger.error(f"GPT-4 knowledge analysis failed: {e}") - return self._create_knowledge_fallback_analysis(brand_name) - - def _create_gpt4_analysis_prompt(self, brand_name: str, website_url: str, search_results: Dict[str, Any]) -> str: - """ - Create detailed analysis prompt for GPT-4 with SERP search results. - """ - search_summary = "" - for platform, data in search_results.items(): - if data.get('results'): - search_summary += f"\n{platform}:\n" - for result in data['results']: - for organic in result.get('organic_results', []): - title = organic.get('title', 'N/A') - link = organic.get('link', 'N/A') - position = organic.get('position', 'N/A') - snippet = organic.get('snippet', '') - search_summary += f" - Title: {title}\n URL: {link}\n Position: {position}\n Snippet: {snippet[:200]}...\n" - else: - search_summary += f"\n{platform}: No results found\n" - - return f""" -Analyze the brand presence for "{brand_name}" (Website: {website_url}) across 14 digital platforms using the following SERP search results: - -SEARCH RESULTS: -{search_summary} - -ANALYSIS REQUIREMENTS: -1. Determine if official brand profiles exist on each platform -2. Verify authenticity using website URL cross-reference -3. Extract follower/subscriber counts from search result snippets -4. Identify verification badges (verified, checkmark, blue tick) from titles/snippets -5. Assess engagement indicators (likes, comments, posts) from snippets -6. Extract actual profile URLs where found -7. Note search ranking positions -8. Evaluate activity level and recent posting dates -9. Estimate account age from available information -10. Calculate profile completeness based on available data - -PLATFORMS TO ANALYZE: -{', '.join(self.PLATFORMS)} - -RETURN ONLY THIS JSON FORMAT (no additional text): - -{{ - "platforms": [ - {{ - "name": "Platform Name", - "found": true/false, - "verified": true/false/null, - "profile_url": "actual_url_or_null", - "confidence": "high/medium/low", - "search_ranking": 1-10_or_null, - "followers_count": number_or_null, - "subscribers_count": number_or_null, - "engagement_level": "high/medium/low/unknown", - "posts_count": number_or_null, - "verification_badge": "verified/blue_tick/checkmark/none", - "account_age_estimate": "X_years_or_unknown", - "last_activity": "recent/days_ago/weeks_ago/unknown", - "profile_completeness": 0-100_percentage, - "notes": "detailed findings including metrics found", - "activity_level": "high/medium/low/unknown" - }} - ], - "summary": {{ - "total_platforms_checked": 14, - "platforms_found": 0, - "platforms_missing": 0, - "completion_percentage": 0, - "verification_rate": 0, - "average_search_ranking": 0.0, - "total_followers": 0, - "average_engagement": "medium", - "verified_accounts": 0 - }} -}} - -IMPORTANT: -- Only mark as "found" if you have strong evidence of official brand presence -- Use actual URLs from search results -- Extract follower/subscriber counts from snippets (e.g., "1.2M followers", "500K subscribers") -- Look for verification indicators in titles/snippets ("โœ“", "verified", "official") -- Assess engagement from snippet text ("10K likes", "active posts", "daily updates") -- Estimate account age from dates or "since 20XX" in snippets -- Rate profile completeness based on available information richness -- Set confidence based on verification indicators and URL authenticity -- Include search ranking position where profile appears in top 10 -- Provide specific, actionable notes including extracted metrics -- Calculate accurate summary statistics including total follower counts -- Ensure all 14 platforms are included in the platforms array -- Use null for metrics that cannot be determined from search results -""" - - def _analyze_competitors(self, brand_name: str) -> Dict[str, Any]: - """ - Analyze top 3 competitors for comparison insights. - """ - try: - # Search for competitors in the same industry - competitor_query = f'"{brand_name}" competitors top companies industry' - competitor_search = self._perform_serp_search(competitor_query) - - if not competitor_search: - return {"enabled": True, "competitors_found": [], "error": "Could not identify competitors"} - - # Extract competitor names using GPT-4 - competitors = self._extract_competitors_with_gpt4(brand_name, competitor_search) - - return { - "enabled": True, - "competitors_found": competitors[:3], # Top 3 competitors - "analysis_date": datetime.now().isoformat() - } - - except Exception as e: - logger.error(f"Competitor analysis failed: {e}") - return {"enabled": True, "competitors_found": [], "error": str(e)} - - def _extract_competitors_with_gpt4(self, brand_name: str, search_result: Dict[str, Any]) -> List[Dict[str, Any]]: - """ - Use GPT-4 to extract competitor information from search results. - """ - try: - organic_results = search_result.get('organic_results', []) - search_text = "\n".join([f"{r.get('title', '')} - {r.get('snippet', '')}" for r in organic_results[:5]]) - - prompt = f""" -Based on the search results below, identify the top 3 main competitors of "{brand_name}". - -Search Results: -{search_text} - -Return only JSON format: -{{ - "competitors": [ - {{ - "name": "Competitor Name", - "platforms_present": 12, - "verification_rate": 85, - "digital_presence_score": "A-" - }} - ] -}} -""" - - response = openai.ChatCompletion.create( - model="gpt-4o", - messages=[ - {"role": "system", "content": "Extract competitor information from search results. Return only valid JSON."}, - {"role": "user", "content": prompt} - ], - temperature=0.2, - max_tokens=1000 - ) - - result = json.loads(response.choices[0].message.content.strip()) - return result.get('competitors', []) - - except Exception as e: - logger.error(f"Competitor extraction failed: {e}") - return [] - - def _generate_insights(self, analysis_result: Dict[str, Any], competitor_data: Dict[str, Any]) -> Dict[str, Any]: - """ - Generate actionable insights and recommendations. - """ - platforms = analysis_result.get('platforms', []) - found_platforms = [p for p in platforms if p.get('found')] - missing_platforms = [p for p in platforms if not p.get('found')] - - # Calculate enhanced digital presence score with follower weighting - completion_rate = len(found_platforms) / len(platforms) * 100 if platforms else 0 - verification_rate = len([p for p in found_platforms if p.get('verified')]) / len(found_platforms) * 100 if found_platforms else 0 - - # Calculate follower-weighted score - total_followers = 0 - weighted_platforms = 0 - high_engagement_count = 0 - - for platform in found_platforms: - # Extract follower count - followers = platform.get('followers_count') or platform.get('subscribers_count') or 0 - if followers: - total_followers += followers - # Weight platforms with followers more heavily - if followers >= 1000000: weighted_platforms += 3 # 1M+ followers - elif followers >= 100000: weighted_platforms += 2 # 100K+ followers - elif followers >= 10000: weighted_platforms += 1.5 # 10K+ followers - else: weighted_platforms += 1 - else: - weighted_platforms += 1 # Default weight for platforms without follower data - - # Count high engagement platforms - if platform.get('engagement_level') == 'high': - high_engagement_count += 1 - - # Enhanced scoring algorithm - base_score = completion_rate - follower_bonus = min(20, (weighted_platforms - len(found_platforms)) * 5) # Up to 20% bonus - engagement_bonus = (high_engagement_count / len(found_platforms) * 10) if found_platforms else 0 # Up to 10% bonus - verification_bonus = verification_rate * 0.1 # Up to 10% bonus - - final_score = min(100, base_score + follower_bonus + engagement_bonus + verification_bonus) - - # Determine grade based on enhanced score - if final_score >= 90: grade = "A+" - elif final_score >= 85: grade = "A" - elif final_score >= 80: grade = "A-" - elif final_score >= 75: grade = "B+" - elif final_score >= 70: grade = "B" - elif final_score >= 65: grade = "B-" - elif final_score >= 60: grade = "C+" - elif final_score >= 55: grade = "C" - elif final_score >= 50: grade = "C-" - elif final_score >= 40: grade = "D+" - elif final_score >= 30: grade = "D" - else: grade = "F" - - # Generate recommendations - recommendations = [] - high_priority_platforms = ["TikTok", "LinkedIn", "Instagram", "YouTube"] - - for platform in missing_platforms[:3]: # Top 3 missing platforms - priority = "high" if platform['name'] in high_priority_platforms else "medium" - recommendations.append({ - "platform": platform['name'], - "priority": priority, - "reason": self._get_platform_recommendation_reason(platform['name']), - "estimated_setup_time": self._get_setup_time_estimate(platform['name']), - "potential_reach": self._get_reach_estimate(platform['name']) - }) - - # Find strongest presence by follower count - strongest_platform = "None" - if found_platforms: - # Sort by follower count, then by engagement level - sorted_platforms = sorted(found_platforms, key=lambda p: ( - p.get('followers_count') or p.get('subscribers_count') or 0, - 1 if p.get('engagement_level') == 'high' else 0 - ), reverse=True) - strongest_platform = sorted_platforms[0]['name'] - - return { - "digital_presence_score": grade, - "final_score": round(final_score, 1), - "strongest_presence": strongest_platform, - "biggest_opportunity": missing_platforms[0]['name'] if missing_platforms else "None", - "total_followers": total_followers, - "average_engagement": "high" if high_engagement_count > len(found_platforms) / 2 else "medium" if high_engagement_count > 0 else "low", - "verification_gaps": len([p for p in found_platforms if not p.get('verified')]), - "industry_benchmark": f"Above average ({completion_rate:.0f}% vs 52% industry average)" if completion_rate > 52 else f"Below average ({completion_rate:.0f}% vs 52% industry average)", - "recommendations": recommendations - } - - def _get_platform_recommendation_reason(self, platform_name: str) -> str: - """Get tailored recommendation reason for each platform.""" - reasons = { - "TikTok": "Fastest-growing platform for viral marketing and reaching Gen Z/Millennial audiences", - "LinkedIn": "Essential for B2B networking, thought leadership, and professional credibility", - "Instagram": "Visual storytelling platform with high engagement rates and shopping features", - "YouTube": "Largest video platform for content marketing and SEO benefits", - "Pinterest": "Perfect for visual discovery and driving website traffic", - "X (Twitter)": "Real-time engagement, news, and customer service platform", - "Facebook": "Largest social network with comprehensive business tools and advertising", - "Medium": "Professional publishing platform for thought leadership and content marketing", - "Google Business": "Critical for local SEO and customer reviews", - "Threads": "Growing text-based platform from Meta with Instagram integration", - "Reddit": "Community engagement and authentic brand discussions", - "Quora": "Q&A platform for establishing expertise and driving organic traffic", - "Tumblr": "Creative community platform for visual and multimedia content", - "Blue Sky": "Emerging decentralized social platform gaining traction" - } - return reasons.get(platform_name, "Expanding brand presence to reach new audiences") - - def _get_setup_time_estimate(self, platform_name: str) -> str: - """Get setup time estimate for each platform.""" - times = { - "TikTok": "1-2 hours", - "LinkedIn": "2-3 hours", - "Instagram": "1-2 hours", - "YouTube": "3-4 hours", - "Pinterest": "2-3 hours", - "X (Twitter)": "1 hour", - "Facebook": "2-3 hours", - "Medium": "1 hour", - "Google Business": "2-4 hours", - "Threads": "30 minutes", - "Reddit": "1-2 hours", - "Quora": "1 hour", - "Tumblr": "1 hour", - "Blue Sky": "30 minutes" - } - return times.get(platform_name, "1-2 hours") - - def _get_reach_estimate(self, platform_name: str) -> str: - """Get potential reach estimate for each platform.""" - reaches = { - "TikTok": "500K+ monthly views potential", - "LinkedIn": "10K+ professional network reach", - "Instagram": "100K+ visual content engagement", - "YouTube": "1M+ video discovery potential", - "Pinterest": "50K+ monthly pin impressions", - "X (Twitter)": "25K+ real-time engagement", - "Facebook": "200K+ social network reach", - "Medium": "5K+ thought leadership readers", - "Google Business": "Local search dominance", - "Threads": "10K+ text-based engagement", - "Reddit": "Community-driven viral potential", - "Quora": "Expert authority positioning", - "Tumblr": "Creative community engagement", - "Blue Sky": "Early adopter advantage" - } - return reaches.get(platform_name, "Expanded audience reach") - - def _create_fallback_analysis(self, search_results: Dict[str, Any]) -> Dict[str, Any]: - """ - Create fallback analysis when GPT-4 analysis fails. - """ - platforms = [] - found_count = 0 - - for platform in self.PLATFORMS: - platform_data = search_results.get(platform, {}) - has_results = len(platform_data.get('results', [])) > 0 - - if has_results: - found_count += 1 - platforms.append({ - "name": platform, - "found": True, - "verified": None, - "profile_url": "Found in search results", - "confidence": "medium", - "search_ranking": None, - "notes": "Found via SERP search - manual verification needed", - "activity_level": "unknown", - "last_updated": None - }) - else: - platforms.append({ - "name": platform, - "found": False, - "verified": None, - "profile_url": None, - "confidence": None, - "search_ranking": None, - "notes": "No results found in SERP search", - "activity_level": "unknown", - "last_updated": None - }) - - return { - "platforms": platforms, - "summary": { - "total_platforms_checked": len(self.PLATFORMS), - "platforms_found": found_count, - "platforms_missing": len(self.PLATFORMS) - found_count, - "completion_percentage": round((found_count / len(self.PLATFORMS)) * 100), - "verification_rate": 0, - "average_search_ranking": 0.0 - } - } - - def _create_knowledge_fallback_analysis(self, brand_name: str) -> Dict[str, Any]: - """ - Create basic fallback analysis based on brand recognition. - """ - # For well-known brands, assume basic presence - major_brands = ["tesla", "apple", "google", "microsoft", "amazon", "meta", "netflix", "nike", "coca-cola"] - is_major_brand = any(brand.lower() in brand_name.lower() for brand in major_brands) - - platforms = [] - found_count = 0 - - for platform in self.PLATFORMS: - # Assume major brands have presence on key platforms - likely_present = is_major_brand and platform in [ - "Google Business", "LinkedIn", "YouTube", "Instagram", - "X (Twitter)", "Facebook" - ] - - if likely_present: - found_count += 1 - platforms.append({ - "name": platform, - "found": True, - "verified": None, - "profile_url": f"Likely present - search required", - "confidence": "low", - "search_ranking": None, - "notes": f"Knowledge-based: {brand_name} likely has {platform} presence", - "activity_level": "unknown", - "last_updated": None - }) - else: - platforms.append({ - "name": platform, - "found": False, - "verified": None, - "profile_url": None, - "confidence": None, - "search_ranking": None, - "notes": f"Knowledge-based: {platform} presence uncertain for {brand_name}", - "activity_level": "unknown", - "last_updated": None - }) - - return { - "platforms": platforms, - "summary": { - "total_platforms_checked": len(self.PLATFORMS), - "platforms_found": found_count, - "platforms_missing": len(self.PLATFORMS) - found_count, - "completion_percentage": round((found_count / len(self.PLATFORMS)) * 100), - "verification_rate": 0, - "average_search_ranking": 0.0 - } - } - - def _format_html_response(self, ai_result: Dict, brand_name: str, website_url: str, competitor_data: Dict, insights: Dict) -> str: - """Format response as beautiful HTML dashboard.""" - try: - # Format follower numbers for display - def format_followers(count): - if not count: - return "N/A" - if count >= 1000000: - return f"{count/1000000:.1f}M" - elif count >= 1000: - return f"{count/1000:.1f}K" - else: - return str(count) - - # Get platform icons (simple SVG placeholders) - def get_platform_icon(platform_name): - icons = { - "LinkedIn": "%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%230077B5' viewBox='0 0 24 24'%3E%3Cpath d='M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z'/%3E%3C/svg%3E", - "YouTube": "%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23FF0000' viewBox='0 0 24 24'%3E%3Cpath d='M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z'/%3E%3C/svg%3E", - "Instagram": "%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23E4405F' viewBox='0 0 24 24'%3E%3Cpath d='M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z'/%3E%3C/svg%3E", - "TikTok": "%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23000000' viewBox='0 0 24 24'%3E%3Cpath d='M12.53.02C13.84 0 15.14.01 16.44 0c.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z'/%3E%3C/svg%3E", - "X (Twitter)": "%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%231DA1F2' viewBox='0 0 24 24'%3E%3Cpath d='M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z'/%3E%3C/svg%3E", - "Facebook": "%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%231877F2' viewBox='0 0 24 24'%3E%3Cpath d='M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z'/%3E%3C/svg%3E" - } - return icons.get(platform_name, "%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23718096' viewBox='0 0 24 24'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z'/%3E%3C/svg%3E") - - # Prepare template data - platforms = ai_result.get('platforms', []) - summary = ai_result.get('summary', {}) - - # Add formatted followers and icons to each platform - for platform in platforms: - platform['followers_formatted'] = format_followers(platform.get('followers_count')) - platform['icon'] = get_platform_icon(platform['name']) - - context = { - 'brand_name': brand_name, - 'website_url': website_url, - 'analysis_date': datetime.now().strftime('%B %d, %Y'), - 'digital_presence_score': insights.get('digital_presence_score', 'N/A'), - 'final_score': insights.get('final_score', 0), - 'platforms_found': summary.get('platforms_found', 0), - 'total_platforms': summary.get('total_platforms_checked', 14), - 'completion_percentage': summary.get('completion_percentage', 0), - 'total_followers_formatted': format_followers(insights.get('total_followers', 0)), - 'average_engagement': insights.get('average_engagement', 'unknown'), - 'verified_accounts': summary.get('verified_accounts', 0), - 'platforms': platforms, - 'recommendations': insights.get('recommendations', []), - 'processing_time': 'Real-time SERP + GPT-4o analysis', - 'analysis_method': 'SerpAPI + GPT-4o with follower tracking', - 'analyzer_version': '2.1 Pro Enhanced' - } - - return render_to_string('brand_analysis_dashboard.html', context) - - except Exception as e: - logger.error(f"Error formatting HTML response: {e}") - # Fallback to JSON if HTML rendering fails - return self._format_json_response(ai_result, brand_name, website_url, competitor_data, insights) - - def _format_json_response(self, ai_result: Dict, brand_name: str, website_url: str, competitor_data: Dict, insights: Dict) -> Dict[str, Any]: - """Fallback JSON response format.""" - """Format the successful analysis response.""" - return { - "status": "success", - "brand_analysis": { - "brand_name": brand_name, - "website": website_url, - "analysis_date": datetime.now().isoformat(), - "processing_time": "Real-time SERP + GPT-4o analysis", - "analysis_method": "SerpAPI + GPT-4o with follower tracking" - }, - "data": ai_result, - "competitor_analysis": competitor_data, - "insights": insights, - "meta": { - "analyzer_version": "2.1 Pro Enhanced", - "platforms_supported": len(self.PLATFORMS), - "analysis_method": "Real-time SERP search + GPT-4o analysis", - "model": "GPT-4o", - "serp_provider": self.serp_provider, - "features": [ - "live_verification", - "actual_urls", - "search_rankings", - "follower_counts", - "engagement_metrics", - "verification_badges", - "account_age_estimation", - "profile_completeness", - "follower_weighted_scoring", - "competitor_analysis", - "actionable_insights" - ] - } - } - - def _format_success_response(self, ai_result: Dict, brand_name: str, website_url: str, competitor_data: Dict, insights: Dict) -> Dict[str, Any]: - """Format the successful analysis response with enhanced Pro features.""" - return { - "status": "success", - "brand_analysis": { - "brand_name": brand_name, - "website": website_url, - "analysis_date": datetime.now().isoformat(), - "processing_time": "Real-time SERP + GPT-4o analysis", - "analysis_method": "SerpAPI + GPT-4o with follower tracking" - }, - "data": { - **ai_result, - "pro_features": { - "total_followers": insights.get("total_followers", 0), - "follower_weighted_score": insights.get("final_score", 0), - "verification_gaps": insights.get("verification_gaps", 0), - "strongest_presence": insights.get("strongest_presence", "None"), - "biggest_opportunity": insights.get("biggest_opportunity", "None"), - "industry_benchmark": insights.get("industry_benchmark", "N/A") - } - }, - "competitor_analysis": competitor_data, - "insights": insights, - "meta": { - "analyzer_version": "2.1 Pro Enhanced", - "platforms_supported": len(self.PLATFORMS), - "analysis_method": "Real-time SERP search + GPT-4o analysis", - "model": "GPT-4o", - "serp_provider": self.serp_provider, - "is_pro_version": True, - "features": [ - "live_verification", - "actual_urls", - "search_rankings", - "follower_counts", - "engagement_metrics", - "verification_badges", - "account_age_estimation", - "profile_completeness", - "follower_weighted_scoring", - "competitor_analysis", - "actionable_insights" - ] - } - } - - def _create_error_response(self, error_message: str) -> Dict[str, Any]: - """Create standardized error response.""" - return { - "status": "error", - "error": { - "message": error_message, - "timestamp": datetime.now().isoformat(), - "code": "ANALYSIS_FAILED" - } - } - -def analyze_brand_presence_pro(brand_name: str, website_url: str, include_competitor_analysis: bool = False) -> Dict[str, Any]: - """ - Convenience function for analyzing brand presence with Pro features. - - Args: - brand_name: The brand name to analyze - website_url: The brand's website URL - include_competitor_analysis: Whether to include competitor insights - - Returns: - Enhanced analysis results dictionary - """ - analyzer_pro = BrandPresenceAnalyzerPro() - return analyzer_pro.analyze_brand_presence(brand_name, website_url, include_competitor_analysis) \ No newline at end of file diff --git a/build.sh b/build.sh deleted file mode 100755 index 75f837b..0000000 --- a/build.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -# Build script for Render deployment - -set -o errexit # exit on error - -echo "Installing dependencies..." -pip install -r requirements.txt - -echo "Collecting static files..." -python manage.py collectstatic --noinput - -echo "Build completed successfully!" \ No newline at end of file diff --git a/debug_middleware.py b/debug_middleware.py deleted file mode 100644 index 6dda50b..0000000 --- a/debug_middleware.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -Debug middleware for troubleshooting Dokploy 404 issues -""" -import logging - -logger = logging.getLogger(__name__) - -class DebugRequestMiddleware: - """Middleware to log all incoming requests for debugging""" - - def __init__(self, get_response): - self.get_response = get_response - - def __call__(self, request): - # Log incoming request details - logger.info(f"๐ŸŒ INCOMING REQUEST:") - logger.info(f" - Method: {request.method}") - logger.info(f" - Path: {request.path}") - logger.info(f" - Full Path: {request.get_full_path()}") - logger.info(f" - Host: {request.get_host()}") - logger.info(f" - User Agent: {request.META.get('HTTP_USER_AGENT', 'Unknown')[:100]}") - logger.info(f" - Remote IP: {request.META.get('REMOTE_ADDR', 'Unknown')}") - logger.info(f" - Headers: {dict(request.headers)}") - - # Process request - response = self.get_response(request) - - # Log response - logger.info(f"๐Ÿ“ค RESPONSE:") - logger.info(f" - Status Code: {response.status_code}") - logger.info(f" - Content Type: {response.get('Content-Type', 'Unknown')}") - - return response diff --git a/debug_settings.py b/debug_settings.py deleted file mode 100644 index 1db9ca1..0000000 --- a/debug_settings.py +++ /dev/null @@ -1,32 +0,0 @@ -# Debug settings for Dokploy troubleshooting -from netcop_hub.settings import * - -# Enable debug mode -DEBUG = True - -# Add debug middleware at the top -MIDDLEWARE = ['debug_middleware.DebugRequestMiddleware'] + MIDDLEWARE - -# Enhanced logging -LOGGING['loggers']['root']['level'] = 'DEBUG' -LOGGING['handlers']['console']['level'] = 'DEBUG' - -# Print all URLs at startup -print("๐Ÿ”— Available URL patterns:") -try: - from django.urls import get_resolver - resolver = get_resolver() - url_patterns = [] - - def extract_patterns(patterns, prefix=''): - for pattern in patterns: - if hasattr(pattern, 'url_patterns'): - extract_patterns(pattern.url_patterns, prefix + str(pattern.pattern)) - else: - url_patterns.append(prefix + str(pattern.pattern)) - - extract_patterns(resolver.url_patterns) - for pattern in url_patterns[:20]: # Show first 20 - print(f" - {pattern}") -except Exception as e: - print(f" - Error loading URL patterns: {e}") diff --git a/deploy-dokploy.sh b/deploy-dokploy.sh deleted file mode 100755 index 7601ea6..0000000 --- a/deploy-dokploy.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash -# Deployment helper script for Dokploy -# This script helps configure environment variables before deployment - -set -e - -echo "๐Ÿš€ Dokploy Deployment Configuration Helper" -echo "==========================================" - -# Get domain from user -read -p "Enter your domain (e.g., quamtumtaskai.netcoptech.com): " DOMAIN -if [ -z "$DOMAIN" ]; then - echo "โŒ Domain is required" - exit 1 -fi - -# Generate secret key if not provided -if [ -z "$SECRET_KEY" ]; then - echo "๐Ÿ”‘ Generating SECRET_KEY..." - SECRET_KEY=$(python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())') -fi - -# Set default environment variables -export DOMAIN="$DOMAIN" -export SECRET_KEY="$SECRET_KEY" -export DEBUG="false" -export ALLOWED_HOSTS="$DOMAIN,*" -export CSRF_TRUSTED_ORIGINS="https://$DOMAIN,http://$DOMAIN" -export SECURE_SSL_REDIRECT="false" -export SECURE_PROXY_SSL_HEADER="HTTP_X_FORWARDED_PROTO,https" -export DOKPLOY_PROJECT_NAME="quantum-tasks-ai" -export PYTHONUNBUFFERED="1" - -echo "โœ… Configuration complete:" -echo " - Domain: $DOMAIN" -echo " - SECRET_KEY: ${SECRET_KEY:0:20}..." -echo " - ALLOWED_HOSTS: $ALLOWED_HOSTS" -echo " - SSL Redirect: $SECURE_SSL_REDIRECT" - -echo "" -echo "๐Ÿ“‹ Environment variables to set in Dokploy:" -echo "DOMAIN=$DOMAIN" -echo "SECRET_KEY=$SECRET_KEY" -echo "DEBUG=false" -echo "ALLOWED_HOSTS=$ALLOWED_HOSTS" -echo "CSRF_TRUSTED_ORIGINS=$CSRF_TRUSTED_ORIGINS" -echo "SECURE_SSL_REDIRECT=false" -echo "SECURE_PROXY_SSL_HEADER=HTTP_X_FORWARDED_PROTO,https" -echo "DOKPLOY_PROJECT_NAME=quantum-tasks-ai" -echo "PYTHONUNBUFFERED=1" - -echo "" -echo "๐Ÿ”„ After setting these variables in Dokploy, trigger a redeploy." -echo "๐ŸŒ Your app should be accessible at: https://$DOMAIN" diff --git a/docs/AGENT_CREATION.md b/docs/AGENT_CREATION.md deleted file mode 100644 index db2bb26..0000000 --- a/docs/AGENT_CREATION.md +++ /dev/null @@ -1,46 +0,0 @@ -# Simple Agent Creation Guide - -## Quick Agent Creation - -1. **Create JSON file** in `agents/configs/agents/your-agent-name.json` -2. **Add basic config**: - -```json -{ - "name": "Your Agent Name", - "description": "What your agent does", - "category": "productivity", - "price": 5, - "webhook_url": "https://your-webhook-endpoint.com", - "form_schema": { - "fields": [ - { - "name": "input", - "type": "text", - "label": "Your Input", - "required": true - } - ] - } -} -``` - -3. **Restart server** - Agent appears automatically - -## Available Categories - -Edit `agents/configs/categories/categories.json`: -- `productivity` - Work tools -- `content` - Content creation -- `analysis` - Data analysis -- `communication` - Communication tools - -## Field Types - -- `text` - Single line text -- `textarea` - Multi-line text -- `number` - Numeric input -- `email` - Email input -- `file` - File upload - -That's it! Your agent will appear in the marketplace. \ No newline at end of file diff --git a/docs/AGENT_REQUEST_TEMPLATE.md b/docs/AGENT_REQUEST_TEMPLATE.md deleted file mode 100644 index ec6c8b1..0000000 --- a/docs/AGENT_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,152 +0,0 @@ -# Agent Request Template - -Use this template when requesting new agents for quick, error-free creation using the modern file-based system. - -## How to Use This Template - -1. **Copy the template below** -2. **Fill in all required fields** -3. **Provide to Claude Code** with the request "Create agent using this template" -4. **Claude will handle** JSON config creation and deployment - ---- - -## Agent Request Template - -```markdown -## New Agent Request - -**Agent Name**: [Enter the display name for the agent] -**Type**: [Webhook OR Direct Access] -**Category**: [Choose from: analysis, career-education, document-processing, human-resources, marketing, consulting] -**Price**: [X.XX AED or 0.0 for FREE] -**Short Description**: [Brief 1-line description for marketplace] -**Full Description**: [Detailed description of what the agent does and its benefits] - -### For Webhook Agents Only: -**Form Fields**: -- Field 1: [name: field_name, type: text/textarea/select/file/url/checkbox, label: "Display Label", required: true/false] -- Field 2: [name: field_name, type: text/textarea/select/file/url/checkbox, label: "Display Label", required: true/false] -- [Add more fields as needed] - -**N8N Webhook URL**: [Your N8N webhook endpoint URL] - -### For Direct Access Agents Only: -**External Form URL**: [JotForm, Google Forms, or other external form URL] -**Custom Template Needed**: [Yes/No - specify if you need custom styling/layout] -**Custom Views Needed**: [Yes/No - specify if you need special marketplace behavior] - -### Optional Information: -**Special Requirements**: [Any unique features or customizations needed] -**Integration Notes**: [Any special setup or configuration details] -``` - ---- - -## Available Categories - -**Choose from these existing categories** (avoid creating new ones): - -- ๐Ÿง  **`analysis`** - Problem-solving, SWOT analysis, strategic analysis tools -- ๐ŸŽ“ **`career-education`** - Career guidance, educational resources, professional development -- ๐Ÿ“„ **`document-processing`** - PDF analysis, file processing, document tools -- ๐Ÿ’ผ **`human-resources`** - Job postings, HR automation, talent management -- ๐Ÿ“ข **`marketing`** - Social ads, branding, content marketing, advertising -- ๐Ÿ’ผ **`consulting`** - Business consultation, strategy services, expert advice - ---- - -## Form Field Types (Webhook Agents) - -- **`text`** - Single-line text input -- **`textarea`** - Multi-line text input -- **`select`** - Dropdown (requires options array) -- **`file`** - File upload with drag-and-drop -- **`url`** - URL input with validation -- **`checkbox`** - Boolean true/false - ---- - -## Example Requests - -### Example 1: Webhook Agent -```markdown -## New Agent Request - -**Agent Name**: Email Campaign Optimizer -**Type**: Webhook -**Category**: marketing -**Price**: 4.0 AED -**Short Description**: AI-powered email campaign optimization and A/B testing -**Full Description**: Optimize your email campaigns with AI analysis of subject lines, content, and send times. Get recommendations for better open rates and conversions. - -### Form Fields: -- Field 1: [name: email_subject, type: text, label: "Email Subject Line", required: true] -- Field 2: [name: email_content, type: textarea, label: "Email Content", required: true] -- Field 3: [name: target_audience, type: select, label: "Target Audience", required: true, options: [{"value": "b2b", "label": "Business"}, {"value": "b2c", "label": "Consumer"}]] - -**N8N Webhook URL**: http://localhost:5678/webhook/email-optimizer -``` - -### Example 2: Direct Access Agent -```markdown -## New Agent Request - -**Agent Name**: Financial Planning Consultant -**Type**: Direct Access -**Category**: consulting -**Price**: 0.0 AED -**Short Description**: Professional financial planning and investment consultation -**Full Description**: Get expert financial advice tailored to your goals. Our certified financial planners provide personalized investment strategies and retirement planning. - -### For Direct Access Agents: -**External Form URL**: https://form.jotform.com/financial-planning-form-id -**Custom Template Needed**: No -**Custom Views Needed**: No -``` - ---- - -## What Happens Next - -After you provide the completed template: - -1. โœ… **Claude creates JSON config** in `agents/configs/agents/` -2. โœ… **Agent configuration is committed to git** -3. โœ… **Agent appears in marketplace** automatically via file-based system -4. โœ… **Creates any needed templates/views** (for advanced Direct Access agents) -5. โœ… **Updates marketplace integration** if needed -6. โœ… **Railway deployment ready** - no manual database commands - -**Simple Process:** JSON file โ†’ git commit โ†’ agent appears! ๐Ÿš€ - ---- - -## Current Agents (8 Total) - -### Webhook Agents (4) -- **Social Ads Generator** - 6.00 AED - Social media ad creation -- **Job Posting Generator** - 10.00 AED - Professional job postings -- **PDF Summarizer** - 8.00 AED - Document analysis and summarization -- **5 Whys Analyzer** - 15.00 AED - Interactive root cause analysis - -### Direct Access Agents (4) -- **CyberSec Career Navigator** - FREE - Career guidance -- **AI Brand Strategist** - FREE - Brand strategy consultation -- **Lean Six Sigma Expert** - FREE - Process improvement -- **SWOT Analysis Expert** - FREE - Strategic analysis - ---- - -## Tips for Better Requests - -- โœ… **Use existing categories** - Avoid creating new ones unless absolutely necessary -- โœ… **Be specific** - Clear descriptions help users understand the agent's value -- โœ… **Test external forms** - Ensure JotForm/external URLs are working before requesting -- โœ… **Consider pricing** - Free agents get more usage, paid agents need clear value proposition -- โœ… **Think about fields** - For webhook agents, plan your form fields carefully -- โœ… **Simple is better** - The file-based system makes creation effortless - ---- - -*For comprehensive agent creation details, see `docs/AGENT_CREATION.md`* \ No newline at end of file diff --git a/dokploy.debug.json b/dokploy.debug.json deleted file mode 100644 index ba94d54..0000000 --- a/dokploy.debug.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "quantum-tasks-ai-debug", - "type": "dockerfile", - "dockerfile": "./Dockerfile.dokploy.debug", - "port": 3000, - "healthCheck": "/health/", - "buildArgs": {}, - "buildOptions": ["--no-cache"], - "env": { - "PYTHONUNBUFFERED": "1", - "DEBUG": "true", - "SECRET_KEY": "debug-key-change-in-production", - "ALLOWED_HOSTS": "*" - } -} diff --git a/dokploy.json b/dokploy.json deleted file mode 100644 index 2c3b6f4..0000000 --- a/dokploy.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "quantum-tasks-ai", - "type": "dockerfile", - "dockerfile": "./Dockerfile.dokploy", - "port": 3000, - "healthCheck": "/health/", - "buildArgs": {}, - "buildOptions": ["--no-cache"], - "security": { - "redirectHttpsToHttp": true, - "forceHttps": false - }, - "env": { - "PYTHONUNBUFFERED": "1", - "DEBUG": "false", - "SECRET_KEY": "${SECRET_KEY:-production-key-change-this-123456789}", - "ALLOWED_HOSTS": "${ALLOWED_HOSTS:-*}", - "DOKPLOY_PROJECT_NAME": "quantum-tasks-ai", - "SECURE_SSL_REDIRECT": "${SECURE_SSL_REDIRECT:-false}", - "SECURE_PROXY_SSL_HEADER": "HTTP_X_FORWARDED_PROTO,https", - "CSRF_TRUSTED_ORIGINS": "${CSRF_TRUSTED_ORIGINS:-}", - "DATABASE_URL": "${DATABASE_URL:-}", - "DOMAIN": "${DOMAIN:-quamtumtaskai.netcoptech.com}" - } -} diff --git a/production_https_settings.py b/production_https_settings.py deleted file mode 100644 index 5d7fe42..0000000 --- a/production_https_settings.py +++ /dev/null @@ -1,39 +0,0 @@ -# Production HTTPS settings for Dokploy deployment -from netcop_hub.settings import * - -# Force HTTPS in production -SECURE_SSL_REDIRECT = True -SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') - -# HSTS (HTTP Strict Transport Security) -SECURE_HSTS_SECONDS = 31536000 # 1 year -SECURE_HSTS_INCLUDE_SUBDOMAINS = True -SECURE_HSTS_PRELOAD = True - -# Cookie security -SESSION_COOKIE_SECURE = True -CSRF_COOKIE_SECURE = True -SESSION_COOKIE_HTTPONLY = True -CSRF_COOKIE_HTTPONLY = True - -# Additional security headers -SECURE_CONTENT_TYPE_NOSNIFF = True -SECURE_BROWSER_XSS_FILTER = True -X_FRAME_OPTIONS = 'DENY' - -# Trust Dokploy/Traefik proxy headers -USE_X_FORWARDED_HOST = True -USE_X_FORWARDED_PORT = True - -# CSRF trusted origins for HTTPS -CSRF_TRUSTED_ORIGINS = [ - 'https://website-quantumtaskai-wrczik-cc50ac-31-97-62-205.traefik.me', - 'http://website-quantumtaskai-wrczik-cc50ac-31-97-62-205.traefik.me', # For testing - 'https://localhost:3000', - 'http://localhost:3000', -] - -print("๐Ÿ”’ HTTPS Production Settings Loaded") -print(f" - SSL Redirect: {SECURE_SSL_REDIRECT}") -print(f" - HSTS: {SECURE_HSTS_SECONDS} seconds") -print(f" - Trusted Origins: {len(CSRF_TRUSTED_ORIGINS)} configured") diff --git a/run_dev.sh b/run_dev.sh deleted file mode 100755 index 76849ba..0000000 --- a/run_dev.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -# Development server startup script -# Ensures clean environment for Django development - -echo "๐Ÿš€ Starting Django Development Server" -echo "======================================" - -# Clear any DATABASE_URL that might interfere with local development -unset DATABASE_URL - -# Activate virtual environment -echo "๐Ÿ“ฆ Activating virtual environment..." -source venv/bin/activate - -# Check database configuration -echo "๐Ÿ” Checking database configuration..." -python manage.py check_db - -# Check for pending migrations -echo "" -echo "๐Ÿ”„ Checking for pending migrations..." -if python manage.py showmigrations --plan | grep -q '\[ \]'; then - echo "โš ๏ธ Found pending migrations. Applying them..." - python manage.py migrate - echo "โœ… Migrations applied successfully!" -else - echo "โœ… All migrations up to date!" -fi - -echo "" -echo "๐ŸŒ Starting Django server..." -echo "Visit: http://localhost:8000" -echo "Admin: http://localhost:8000/admin" -echo "" -echo "Press Ctrl+C to stop the server" -echo "======================================" - -# Start the development server -python manage.py runserver \ No newline at end of file diff --git a/scripts/auto_update_docs.py b/scripts/auto_update_docs.py deleted file mode 100755 index 0f018ba..0000000 --- a/scripts/auto_update_docs.py +++ /dev/null @@ -1,319 +0,0 @@ -#!/usr/bin/env python3 -""" -Auto Documentation Update Script -Automatically updates README.md, CLAUDE.md, and docs/ files based on recent changes -""" - -import os -import sys -import json -import subprocess -import re -from datetime import datetime -from pathlib import Path -from typing import List, Dict, Set - -class DocumentationUpdater: - def __init__(self, project_root: str = None): - self.project_root = Path(project_root) if project_root else Path.cwd() - self.changes_summary = {} - self.updated_files = [] - - def analyze_recent_changes(self, commit_count: int = 5) -> Dict: - """Analyze recent git commits to understand what changed""" - try: - # Get recent commit messages - result = subprocess.run([ - 'git', 'log', f'--oneline', f'-{commit_count}' - ], capture_output=True, text=True, cwd=self.project_root) - - commits = result.stdout.strip().split('\n') if result.stdout else [] - - # Get changed files in recent commits - result = subprocess.run([ - 'git', 'diff', 'HEAD~1', '--name-only' - ], capture_output=True, text=True, cwd=self.project_root) - - changed_files = result.stdout.strip().split('\n') if result.stdout else [] - - # Categorize changes - categories = { - 'agents': [], - 'core': [], - 'deployment': [], - 'documentation': [], - 'frontend': [], - 'backend': [] - } - - for file in changed_files: - if not file: - continue - - file_lower = file.lower() - if any(agent in file for agent in ['agent', 'processor', 'models.py']): - categories['agents'].append(file) - elif any(core in file for core in ['settings', 'urls.py', 'views.py']): - categories['core'].append(file) - elif any(deploy in file for deploy in ['railway', 'requirements', 'docker']): - categories['deployment'].append(file) - elif file_lower.endswith('.md') or 'docs/' in file: - categories['documentation'].append(file) - elif any(frontend in file for frontend in ['.html', '.css', '.js']): - categories['frontend'].append(file) - else: - categories['backend'].append(file) - - return { - 'commits': commits, - 'changed_files': changed_files, - 'categories': categories, - 'analysis_date': datetime.now().isoformat() - } - - except subprocess.CalledProcessError as e: - print(f"Error analyzing git changes: {e}") - return {} - - def find_documentation_files(self) -> Dict[str, List[Path]]: - """Find all documentation files in the project""" - doc_files = { - 'readme': [], - 'claude_md': [], - 'docs_directory': [] - } - - # Find README files - for readme in self.project_root.rglob('README.md'): - doc_files['readme'].append(readme) - - # Find CLAUDE.md files - for claude in self.project_root.rglob('CLAUDE.md'): - doc_files['claude_md'].append(claude) - - # Find docs directory files - docs_path = self.project_root / 'docs' - if docs_path.exists(): - for doc_file in docs_path.rglob('*.md'): - doc_files['docs_directory'].append(doc_file) - - return doc_files - - def should_update_documentation(self, changes: Dict) -> bool: - """Determine if documentation updates are needed""" - # Check if significant changes were made - categories = changes.get('categories', {}) - - # Always update if agents, core, or deployment changed - significant_changes = ( - categories.get('agents', []) or - categories.get('core', []) or - categories.get('deployment', []) - ) - - # Check commit messages for documentation keywords - commits = changes.get('commits', []) - doc_keywords = ['add', 'update', 'new', 'feature', 'agent', 'deploy'] - - has_doc_worthy_commits = any( - any(keyword in commit.lower() for keyword in doc_keywords) - for commit in commits - ) - - return bool(significant_changes or has_doc_worthy_commits) - - def update_claude_md(self, changes: Dict) -> bool: - """Update CLAUDE.md with recent changes""" - claude_file = self.project_root / 'CLAUDE.md' - if not claude_file.exists(): - return False - - try: - content = claude_file.read_text() - original_content = content - updated = False - - categories = changes.get('categories', {}) - - # Update project overview if agents were added/modified - if categories.get('agents'): - # This is a simplified example - in practice, you'd parse and update specific sections - overview_pattern = r'(## Project Overview.*?)(## Development Commands)' - if re.search(overview_pattern, content, re.DOTALL): - print("Found project overview section in CLAUDE.md") - # Add logic to update agent count, new agent descriptions, etc. - updated = True - - # Update commands section if new scripts were added - if any('manage.py' in f or 'script' in f for f in changes.get('changed_files', [])): - print("Detected management command changes") - updated = True - - # Update environment variables section if settings changed - if any('settings' in f or 'env' in f for f in changes.get('changed_files', [])): - print("Detected environment/settings changes") - updated = True - - # Add timestamp of last update - if updated: - timestamp_pattern = r'(Last updated: )[\d\-:T\s]+\n' - new_timestamp = f"Last updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n" - - if re.search(timestamp_pattern, content): - content = re.sub(timestamp_pattern, f"\\1{new_timestamp}", content) - else: - # Add timestamp at the end - content += f"\n\n---\nLast updated: {new_timestamp}" - - claude_file.write_text(content) - self.updated_files.append(str(claude_file)) - return True - - return updated - - except Exception as e: - print(f"Error updating CLAUDE.md: {e}") - return False - - def update_readme(self, changes: Dict) -> bool: - """Update README.md with recent changes""" - readme_file = self.project_root / 'README.md' - if not readme_file.exists(): - return False - - try: - content = readme_file.read_text() - updated = False - - categories = changes.get('categories', {}) - - # Update features section if new agents were added - if categories.get('agents'): - print("Updating README features section for new agents") - updated = True - - # Update installation section if requirements changed - if any('requirements' in f or 'setup' in f for f in changes.get('changed_files', [])): - print("Updating README installation section") - updated = True - - if updated: - readme_file.write_text(content) - self.updated_files.append(str(readme_file)) - return True - - return False - - except Exception as e: - print(f"Error updating README.md: {e}") - return False - - def update_docs_directory(self, changes: Dict) -> bool: - """Update files in docs/ directory""" - docs_path = self.project_root / 'docs' - if not docs_path.exists(): - return False - - updated_any = False - categories = changes.get('categories', {}) - - # Update agent creation guide if agent changes were made - if categories.get('agents'): - agent_guide = docs_path / 'development' / 'agent-creation.md' - if agent_guide.exists(): - print("Updating agent creation guide") - # Add new patterns, update examples, etc. - updated_any = True - self.updated_files.append(str(agent_guide)) - - # Update deployment guide if deployment files changed - if categories.get('deployment'): - deploy_guide = docs_path / 'deployment' / 'railway-deployment.md' - if deploy_guide.exists(): - print("Updating deployment guide") - updated_any = True - self.updated_files.append(str(deploy_guide)) - - return updated_any - - def generate_update_summary(self, changes: Dict) -> str: - """Generate a summary of what was updated""" - summary = [] - summary.append("=== Documentation Auto-Update Summary ===") - summary.append(f"Update Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") - summary.append("") - - # Recent commits - commits = changes.get('commits', []) - if commits: - summary.append("Recent Commits:") - for commit in commits[:3]: # Show last 3 commits - summary.append(f" - {commit}") - summary.append("") - - # Changed files by category - categories = changes.get('categories', {}) - for category, files in categories.items(): - if files: - summary.append(f"{category.title()} Changes:") - for file in files[:5]: # Show up to 5 files per category - summary.append(f" - {file}") - summary.append("") - - # Updated documentation files - if self.updated_files: - summary.append("Updated Documentation Files:") - for file in self.updated_files: - summary.append(f" - {file}") - else: - summary.append("No documentation files required updates.") - - summary.append("") - summary.append("=== End Summary ===") - - return "\n".join(summary) - - def run_update(self) -> str: - """Main method to run the documentation update process""" - print("Starting documentation auto-update...") - - # Analyze recent changes - changes = self.analyze_recent_changes() - - if not changes: - return "Error: Could not analyze recent changes" - - # Check if updates are needed - if not self.should_update_documentation(changes): - return "No significant changes detected - documentation update skipped" - - # Find documentation files - doc_files = self.find_documentation_files() - print(f"Found documentation files: {sum(len(files) for files in doc_files.values())}") - - # Update each type of documentation - updated_claude = self.update_claude_md(changes) - updated_readme = self.update_readme(changes) - updated_docs = self.update_docs_directory(changes) - - # Generate and save summary - summary = self.generate_update_summary(changes) - - # Save summary to file - summary_file = self.project_root / 'docs_update_summary.txt' - summary_file.write_text(summary) - - print(summary) - return summary - -def main(): - """Main entry point""" - project_root = sys.argv[1] if len(sys.argv) > 1 else None - - updater = DocumentationUpdater(project_root) - result = updater.run_update() - - return result - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/scripts/setup_branch_protection.sh b/scripts/setup_branch_protection.sh deleted file mode 100755 index 38447da..0000000 --- a/scripts/setup_branch_protection.sh +++ /dev/null @@ -1,143 +0,0 @@ -#!/bin/bash -# GitHub Branch Protection Setup Script -# Run this script to configure branch protection rules - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -echo -e "${GREEN}๐Ÿ” Setting up GitHub Branch Protection Rules${NC}" -echo "This script will guide you through configuring branch protection for Railway deployment control." -echo "" - -# Check if GitHub CLI is installed -if ! command -v gh &> /dev/null; then - echo -e "${RED}โŒ GitHub CLI (gh) is not installed.${NC}" - echo "Please install GitHub CLI first:" - echo " - macOS: brew install gh" - echo " - Ubuntu: sudo apt install gh" - echo " - Or visit: https://cli.github.com/" - exit 1 -fi - -# Check if user is authenticated -if ! gh auth status &> /dev/null; then - echo -e "${YELLOW}๐Ÿ”‘ You need to authenticate with GitHub first.${NC}" - echo "Run: gh auth login" - exit 1 -fi - -# Get repository information -REPO_OWNER=$(gh repo view --json owner --jq '.owner.login') -REPO_NAME=$(gh repo view --json name --jq '.name') - -echo -e "${GREEN}๐Ÿ“‹ Repository: ${REPO_OWNER}/${REPO_NAME}${NC}" -echo "" - -# Function to create branch protection rule -create_branch_protection() { - local branch=$1 - local description=$2 - - echo -e "${YELLOW}๐Ÿ›ก๏ธ Setting up protection for ${branch} branch (${description})${NC}" - - # Create branch protection rule - gh api repos/${REPO_OWNER}/${REPO_NAME}/branches/${branch}/protection \ - --method PUT \ - --field required_status_checks='{"strict":true,"contexts":[]}' \ - --field enforce_admins=true \ - --field required_pull_request_reviews='{"required_approving_review_count":1,"dismiss_stale_reviews":true,"require_code_owner_reviews":false}' \ - --field restrictions=null \ - --field allow_force_pushes=false \ - --field allow_deletions=false \ - > /dev/null 2>&1 - - if [ $? -eq 0 ]; then - echo -e "${GREEN}โœ… Branch protection enabled for ${branch}${NC}" - else - echo -e "${RED}โŒ Failed to set protection for ${branch}${NC}" - echo "This might be because:" - echo " - You don't have admin permissions on the repository" - echo " - The branch doesn't exist yet" - echo " - GitHub API rate limits" - fi -} - -# Create main branch protection (Production) -echo -e "${GREEN}Setting up main branch protection (Production deployment control)${NC}" -create_branch_protection "main" "Production deployment" - -echo "" - -# Create staging branch protection (Optional) -echo -e "${YELLOW}Do you want to protect the staging branch too? (y/n)${NC}" -read -r setup_staging - -if [[ $setup_staging =~ ^[Yy]$ ]]; then - create_branch_protection "staging" "Staging deployment" -fi - -echo "" - -# Create development branch if it doesn't exist -echo -e "${GREEN}Ensuring development branch exists...${NC}" -git show-ref --verify --quiet refs/heads/development -if [ $? -eq 0 ]; then - echo -e "${GREEN}โœ… Development branch already exists${NC}" -else - echo -e "${YELLOW}๐Ÿ“ Creating development branch...${NC}" - git checkout -b development 2>/dev/null || git checkout development - git push -u origin development -fi - -echo "" - -# Push staging branch if it doesn't exist on remote -echo -e "${GREEN}Ensuring staging branch exists on remote...${NC}" -if git ls-remote --heads origin staging | grep -q staging; then - echo -e "${GREEN}โœ… Staging branch already exists on remote${NC}" -else - echo -e "${YELLOW}๐Ÿ“ Pushing staging branch to remote...${NC}" - git push -u origin staging -fi - -echo "" - -# Set default branch to development -echo -e "${YELLOW}Do you want to set 'development' as the default branch for new PRs? (y/n)${NC}" -read -r set_default - -if [[ $set_default =~ ^[Yy]$ ]]; then - gh api repos/${REPO_OWNER}/${REPO_NAME} \ - --method PATCH \ - --field default_branch='development' \ - > /dev/null 2>&1 - - if [ $? -eq 0 ]; then - echo -e "${GREEN}โœ… Default branch set to development${NC}" - else - echo -e "${RED}โŒ Failed to set default branch${NC}" - fi -fi - -echo "" -echo -e "${GREEN}๐ŸŽ‰ Branch protection setup complete!${NC}" -echo "" -echo -e "${YELLOW}Summary of your deployment control setup:${NC}" -echo "๐Ÿ“ฆ main branch โ†’ Protected, auto-deploys to Railway production" -echo "๐Ÿงช staging branch โ†’ ${setup_staging:+Protected, }deploys to Railway staging" -echo "๐Ÿ› ๏ธ development โ†’ Unprotected, no automatic deployment" -echo "" -echo -e "${GREEN}Next steps:${NC}" -echo "1. Configure Railway to deploy only from 'main' branch" -echo "2. Optionally create staging Railway service for 'staging' branch" -echo "3. Always work on 'development' branch for new features" -echo "4. Use Pull Requests to merge: development โ†’ staging โ†’ main" -echo "" -echo -e "${YELLOW}To complete Railway configuration:${NC}" -echo "1. Go to your Railway dashboard" -echo "2. In your service settings, set 'Source Repo' branch to 'main'" -echo "3. Enable 'Auto Deploy' only for the main branch" -echo "4. For staging, create a separate service connected to 'staging' branch" \ No newline at end of file diff --git a/scripts/setup_git_hooks.sh b/scripts/setup_git_hooks.sh deleted file mode 100755 index 7cc58d9..0000000 --- a/scripts/setup_git_hooks.sh +++ /dev/null @@ -1,120 +0,0 @@ -#!/bin/bash -# Setup Git Hooks for Auto-Documentation Updates - -PROJECT_ROOT=$(pwd) -GIT_HOOKS_DIR="$PROJECT_ROOT/.git/hooks" -SCRIPTS_DIR="$PROJECT_ROOT/scripts" - -echo "Setting up git hooks for auto-documentation updates..." - -# Create post-commit hook -cat > "$GIT_HOOKS_DIR/post-commit" << 'EOF' -#!/bin/bash -# Auto-update documentation after successful commits - -PROJECT_ROOT=$(git rev-parse --show-toplevel) -SCRIPTS_DIR="$PROJECT_ROOT/scripts" -AUTO_UPDATE_SCRIPT="$SCRIPTS_DIR/auto_update_docs.py" - -# Check if the auto-update script exists -if [ -f "$AUTO_UPDATE_SCRIPT" ]; then - echo "Auto-updating documentation after commit..." - python3 "$AUTO_UPDATE_SCRIPT" "$PROJECT_ROOT" - - # Check if any documentation was updated - if [ -f "$PROJECT_ROOT/docs_update_summary.txt" ]; then - echo "Documentation auto-update completed. Check docs_update_summary.txt for details." - - # Optionally auto-commit documentation updates - # Uncomment the lines below if you want documentation updates to be auto-committed - # git add *.md docs/ CLAUDE.md README.md docs_update_summary.txt - # git commit -m "๐Ÿ“š Auto-update documentation after recent changes" - fi -else - echo "Auto-update script not found at $AUTO_UPDATE_SCRIPT" -fi -EOF - -# Make post-commit hook executable -chmod +x "$GIT_HOOKS_DIR/post-commit" - -# Create pre-push hook to ensure documentation is up to date -cat > "$GIT_HOOKS_DIR/pre-push" << 'EOF' -#!/bin/bash -# Ensure documentation is up to date before pushing - -PROJECT_ROOT=$(git rev-parse --show-toplevel) -SCRIPTS_DIR="$PROJECT_ROOT/scripts" -AUTO_UPDATE_SCRIPT="$SCRIPTS_DIR/auto_update_docs.py" - -echo "Checking documentation status before push..." - -# Run documentation update check -if [ -f "$AUTO_UPDATE_SCRIPT" ]; then - python3 "$AUTO_UPDATE_SCRIPT" "$PROJECT_ROOT" - - # Check if any updates were made - if git diff --quiet; then - echo "Documentation is up to date." - else - echo "Documentation updates were generated. Please review and commit them before pushing." - echo "Modified files:" - git diff --name-only - echo "" - echo "To commit documentation updates:" - echo " git add ." - echo " git commit -m '๐Ÿ“š Update documentation'" - echo " git push" - - # Uncomment to block push until docs are committed - # exit 1 - fi -else - echo "Auto-update script not found. Proceeding with push..." -fi -EOF - -# Make pre-push hook executable -chmod +x "$GIT_HOOKS_DIR/pre-push" - -# Create a manual trigger script -cat > "$SCRIPTS_DIR/update_docs_manual.sh" << 'EOF' -#!/bin/bash -# Manual trigger for documentation updates - -PROJECT_ROOT=$(git rev-parse --show-toplevel) -SCRIPTS_DIR="$PROJECT_ROOT/scripts" -AUTO_UPDATE_SCRIPT="$SCRIPTS_DIR/auto_update_docs.py" - -echo "Manually triggering documentation update..." - -if [ -f "$AUTO_UPDATE_SCRIPT" ]; then - python3 "$AUTO_UPDATE_SCRIPT" "$PROJECT_ROOT" - - if [ -f "$PROJECT_ROOT/docs_update_summary.txt" ]; then - echo "" - echo "Documentation update completed!" - echo "Summary saved to: docs_update_summary.txt" - echo "" - echo "To commit the updates:" - echo " git add ." - echo " git commit -m '๐Ÿ“š Manual documentation update'" - fi -else - echo "Error: Auto-update script not found at $AUTO_UPDATE_SCRIPT" - exit 1 -fi -EOF - -chmod +x "$SCRIPTS_DIR/update_docs_manual.sh" - -echo "Git hooks setup completed!" -echo "" -echo "Created hooks:" -echo " - post-commit: Auto-updates docs after each commit" -echo " - pre-push: Checks docs before pushing" -echo "" -echo "Created scripts:" -echo " - $SCRIPTS_DIR/update_docs_manual.sh: Manual documentation update trigger" -echo "" -echo "To disable auto-updates, remove or rename the hooks in .git/hooks/" \ No newline at end of file diff --git a/scripts/update_docs_manual.sh b/scripts/update_docs_manual.sh deleted file mode 100755 index 056db42..0000000 --- a/scripts/update_docs_manual.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -# Manual trigger for documentation updates - -PROJECT_ROOT=$(git rev-parse --show-toplevel) -SCRIPTS_DIR="$PROJECT_ROOT/scripts" -AUTO_UPDATE_SCRIPT="$SCRIPTS_DIR/auto_update_docs.py" - -echo "Manually triggering documentation update..." - -if [ -f "$AUTO_UPDATE_SCRIPT" ]; then - python3 "$AUTO_UPDATE_SCRIPT" "$PROJECT_ROOT" - - if [ -f "$PROJECT_ROOT/docs_update_summary.txt" ]; then - echo "" - echo "Documentation update completed!" - echo "Summary saved to: docs_update_summary.txt" - echo "" - echo "To commit the updates:" - echo " git add ." - echo " git commit -m '๐Ÿ“š Manual documentation update'" - fi -else - echo "Error: Auto-update script not found at $AUTO_UPDATE_SCRIPT" - exit 1 -fi diff --git a/start-dokploy.sh b/start-dokploy.sh deleted file mode 100755 index b7bd113..0000000 --- a/start-dokploy.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -# Dokploy startup script - -set -e # Exit on any error - -echo "๐Ÿš€ Starting Quantum Tasks AI on Dokploy" -echo "=======================================" - -# Run database migrations -echo "๐Ÿ“„ Running database migrations..." -DJANGO_SETTINGS_MODULE=netcop_hub.production_settings python manage.py migrate --noinput - -# Create cache table if needed -echo "๐Ÿ—„๏ธ Ensuring cache table exists..." -DJANGO_SETTINGS_MODULE=netcop_hub.production_settings python manage.py createcachetable || true - -# Check if we can access the database -echo "๐Ÿ” Testing database connection..." -DJANGO_SETTINGS_MODULE=netcop_hub.production_settings python manage.py check --database default - -# Start the application -echo "๐ŸŒ Starting gunicorn server on port 3000..." -echo "Health check endpoint: /health/" -echo "๐Ÿ”’ SSL redirect: $SECURE_SSL_REDIRECT" -echo "๐ŸŒ Allowed hosts: $ALLOWED_HOSTS" -echo "=======================================" - -DJANGO_SETTINGS_MODULE=netcop_hub.production_settings exec gunicorn netcop_hub.wsgi:application - --bind 0.0.0.0:3000 \ - --workers 2 \ - --timeout 120 \ - --max-requests 1000 \ - --preload \ - --log-level info \ - --access-logfile - \ - --error-logfile - diff --git a/start.sh b/start.sh deleted file mode 100755 index 3cc383e..0000000 --- a/start.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -# Start script for Render deployment - -set -o errexit # exit on error - -echo "Running database migrations..." -python manage.py migrate - -echo "Creating admin user if needed..." -python manage.py reset_admin --password=RenderTemp123! || true - -echo "Starting gunicorn server..." -exec gunicorn netcop_hub.wsgi:application --bind 0.0.0.0:$PORT --workers 1 --timeout 60 \ No newline at end of file diff --git a/tests/check_agents.py b/tests/check_agents.py deleted file mode 100644 index d9c76bb..0000000 --- a/tests/check_agents.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python -import os -import sys -import django - -# Add the project root to Python path -sys.path.insert(0, '/home/amit/projects/quantumtaskai_django') - -# Set Django settings -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'quantumtaskai_hub.settings') -django.setup() - -from agent_base.models import BaseAgent - -print("๐Ÿ” Checking agents in database:") -print("=" * 40) - -agents = BaseAgent.objects.all() -if agents: - for agent in agents: - print(f"โœ… {agent.name} ({agent.slug})") - print(f" Category: {agent.category}") - print(f" Price: {agent.price} AED") - print(f" Type: {agent.agent_type}") - print(f" Active: {agent.is_active}") - print() -else: - print("โŒ No agents found in database") - -print(f"Total agents: {agents.count()}") \ No newline at end of file diff --git a/tests/simple_test.py b/tests/simple_test.py deleted file mode 100644 index 966239e..0000000 --- a/tests/simple_test.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python -import os -import sys -import django - -# Add the project root to Python path -sys.path.insert(0, '/home/amit/Desktop/quantum_ai') - -# Set Django settings -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'quantumtaskai_hub.settings') -django.setup() - -from agent_base.models import BaseAgent -from core.views import homepage_view -from django.http import HttpRequest -from django.contrib.auth.models import AnonymousUser - -def test_agent_visibility(): - print("๐Ÿงช Testing Agent System Integration") - print("=" * 40) - - # Check agents in database - agents = BaseAgent.objects.filter(is_active=True) - print(f"โœ… Active agents in database: {agents.count()}") - - for agent in agents: - print(f" ๐Ÿ“‹ {agent.name} ({agent.slug})") - print(f" Category: {agent.category}") - print(f" Price: {agent.price} AED") - print(f" Type: {agent.agent_type}") - print() - - # Test the homepage view directly - print("๐Ÿงช Testing Homepage View") - print("-" * 20) - - request = HttpRequest() - request.method = 'GET' - request.user = AnonymousUser() - request.META = {'HTTP_HOST': 'testserver'} - - try: - response = homepage_view(request) - print(f"โœ… Homepage view response status: {response.status_code}") - - # Check if the response contains weather agent - if hasattr(response, 'content'): - content = response.content.decode('utf-8') - if 'Weather Reporter' in content: - print("โœ… Weather Reporter found in homepage HTML") - else: - print("โŒ Weather Reporter not found in homepage HTML") - - if 'Use Now' in content: - print("โœ… 'Use Now' buttons found") - else: - print("โŒ 'Use Now' buttons not found") - - except Exception as e: - print(f"โŒ Error in homepage view: {e}") - -def check_url_structure(): - print("\n๐Ÿงช Checking URL Structure") - print("=" * 40) - - from django.urls import reverse - - try: - # Test core URLs - homepage_url = reverse('core:homepage') - print(f"โœ… Homepage URL: {homepage_url}") - - marketplace_url = reverse('agent_base:marketplace') - print(f"โœ… Marketplace URL: {marketplace_url}") - - # Test weather reporter URL - weather_url = reverse('weather_reporter:detail') - print(f"โœ… Weather Reporter URL: {weather_url}") - - except Exception as e: - print(f"โŒ URL resolution error: {e}") - -if __name__ == '__main__': - test_agent_visibility() - check_url_structure() \ No newline at end of file diff --git a/tests/test_homepage.py b/tests/test_homepage.py deleted file mode 100644 index a13c6b6..0000000 --- a/tests/test_homepage.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python -import os -import sys -import django - -# Add the project root to Python path -sys.path.insert(0, '/home/amit/projects/quantumtaskai_django') - -# Set Django settings -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'quantumtaskai_hub.settings') -django.setup() - -from django.test import Client -from django.contrib.auth import get_user_model - -User = get_user_model() - -def test_homepage(): - print("๐Ÿงช Testing Homepage Agent Display") - print("=" * 40) - - # Create a test client - client = Client() - - # Get homepage - response = client.get('/') - print(f"โœ… Homepage response status: {response.status_code}") - - # Check if agents are in context - if 'featured_agents' in response.context: - agents = response.context['featured_agents'] - print(f"โœ… Featured agents found: {agents.count()}") - - for agent in agents: - print(f" ๐Ÿ“‹ {agent.name} ({agent.slug}) - {agent.price} AED") - else: - print("โŒ No featured_agents in context") - - # Check if Weather Reporter is in the HTML - html_content = response.content.decode('utf-8') - if 'Weather Reporter' in html_content: - print("โœ… Weather Reporter found in HTML") - else: - print("โŒ Weather Reporter not found in HTML") - - if 'Use Now' in html_content: - print("โœ… 'Use Now' buttons found in HTML") - else: - print("โŒ 'Use Now' buttons not found in HTML") - -def test_agent_direct_access(): - print("\n๐Ÿงช Testing Direct Agent Access") - print("=" * 40) - - client = Client() - - # Test direct access to weather reporter - response = client.get('/agents/weather-reporter/') - print(f"โœ… Weather Reporter direct access: {response.status_code}") - - if response.status_code == 200: - html_content = response.content.decode('utf-8') - if 'Weather Reporter Agent' in html_content: - print("โœ… Weather Reporter page loads correctly") - else: - print("โŒ Weather Reporter page content issue") - elif response.status_code == 302: - print(f"โœ… Redirected to: {response.url}") - else: - print(f"โŒ Unexpected status code: {response.status_code}") - -if __name__ == '__main__': - test_homepage() - test_agent_direct_access() \ No newline at end of file