Spaces:
Runtime error
Runtime error
def create_multipage_app(): | |
with gr.Blocks( | |
theme=gr.themes.Soft( | |
primary_hue=gr.themes.colors.blue, | |
secondary_hue=gr.themes.colors.green, | |
neutral_hue=gr.themes.colors.slate, | |
), | |
title="⚡ Power Systems Consultant - Enhanced", | |
css=ENHANCED_CSS | |
) as app: | |
# Global state management | |
current_page = gr.State("cover") | |
user_state = gr.State(None) | |
session_state = gr.State(None) | |
# Page 1: Cover Page (Always visible initially) | |
with gr.Column(visible=True, elem_classes=["cover-page"]) as cover_page: | |
with gr.HTML() as cover_content: | |
gr.HTML(""" | |
<div class="cover-hero"> | |
<div class="cover-icon">⚡</div> | |
<h1 class="cover-title">Power Systems Consultant</h1> | |
<p class="cover-subtitle"> | |
Advanced AI-powered platform for electrical power systems analysis, fault calculations, | |
protection design, and engineering excellence. Experience the future of power systems consulting. | |
</p> | |
</div> | |
""") | |
with gr.Row(): | |
signin_nav_btn = gr.Button("🔐 Sign Inimport gradio as gr | |
import os | |
import json | |
import time | |
import uuid | |
from datetime import datetime | |
from groq import Groq | |
import pandas as pd | |
from typing import Dict, List, Tuple, Optional | |
import re | |
import hashlib | |
import sqlite3 | |
import sys | |
# Add utils directory to path | |
sys.path.append(os.path.join(os.path.dirname(__file__), 'utils')) | |
# Import your external utilities | |
try: | |
from utils.diagram_generator import DiagramGenerator as ExternalDiagramGenerator | |
from utils.rag_system import RAGSystem | |
EXTERNAL_UTILS_AVAILABLE = True | |
print("✅ External utilities loaded successfully!") | |
except ImportError as e: | |
print(f"⚠️ External utilities not found: {e}") | |
print(" Using internal implementations...") | |
EXTERNAL_UTILS_AVAILABLE = False | |
# Initialize components with better error handling | |
def get_groq_client(): | |
api_key = os.getenv("GROQ_API_KEY") | |
if not api_key: | |
print("⚠️ GROQ_API_KEY not found. Using demo mode.") | |
return None | |
try: | |
return Groq(api_key=api_key) | |
except Exception as e: | |
print(f"❌ Groq client initialization failed: {e}") | |
return None | |
class UserManager: | |
def __init__(self): | |
self.db_path = 'users.db' | |
self.init_database() | |
def init_database(self): | |
"""Initialize SQLite database for users""" | |
try: | |
conn = sqlite3.connect(self.db_path) | |
cursor = conn.cursor() | |
cursor.execute(''' | |
CREATE TABLE IF NOT EXISTS users ( | |
id INTEGER PRIMARY KEY AUTOINCREMENT, | |
username TEXT UNIQUE NOT NULL, | |
email TEXT UNIQUE NOT NULL, | |
password_hash TEXT NOT NULL, | |
full_name TEXT, | |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | |
last_login TIMESTAMP | |
) | |
''') | |
cursor.execute(''' | |
CREATE TABLE IF NOT EXISTS chat_sessions ( | |
id INTEGER PRIMARY KEY AUTOINCREMENT, | |
user_id INTEGER, | |
session_id TEXT UNIQUE, | |
title TEXT, | |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | |
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | |
FOREIGN KEY (user_id) REFERENCES users (id) | |
) | |
''') | |
cursor.execute(''' | |
CREATE TABLE IF NOT EXISTS messages ( | |
id INTEGER PRIMARY KEY AUTOINCREMENT, | |
session_id TEXT, | |
role TEXT, | |
content TEXT, | |
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | |
FOREIGN KEY (session_id) REFERENCES chat_sessions (session_id) | |
) | |
''') | |
conn.commit() | |
conn.close() | |
print("✅ Database initialized successfully") | |
except Exception as e: | |
print(f"❌ Database initialization failed: {e}") | |
def hash_password(self, password: str) -> str: | |
"""Hash password using SHA-256""" | |
return hashlib.sha256(password.encode()).hexdigest() | |
def create_user(self, username: str, email: str, password: str, full_name: str = "") -> Tuple[bool, str]: | |
"""Create new user account""" | |
try: | |
conn = sqlite3.connect(self.db_path) | |
cursor = conn.cursor() | |
password_hash = self.hash_password(password) | |
cursor.execute( | |
"INSERT INTO users (username, email, password_hash, full_name) VALUES (?, ?, ?, ?)", | |
(username, email, password_hash, full_name) | |
) | |
conn.commit() | |
conn.close() | |
return True, "Account created successfully!" | |
except sqlite3.IntegrityError as e: | |
if "username" in str(e): | |
return False, "Username already exists" | |
elif "email" in str(e): | |
return False, "Email already registered" | |
else: | |
return False, "Registration failed" | |
except Exception as e: | |
return False, f"Error: {str(e)}" | |
def authenticate_user(self, username: str, password: str) -> Tuple[bool, Optional[Dict]]: | |
"""Authenticate user login""" | |
try: | |
conn = sqlite3.connect(self.db_path) | |
cursor = conn.cursor() | |
password_hash = self.hash_password(password) | |
cursor.execute( | |
"SELECT id, username, email, full_name FROM users WHERE username = ? AND password_hash = ?", | |
(username, password_hash) | |
) | |
user = cursor.fetchone() | |
if user: | |
cursor.execute( | |
"UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = ?", | |
(user[0],) | |
) | |
conn.commit() | |
user_data = { | |
'id': user[0], | |
'username': user[1], | |
'email': user[2], | |
'full_name': user[3] or user[1] | |
} | |
conn.close() | |
return True, user_data | |
conn.close() | |
return False, None | |
except Exception as e: | |
print(f"Authentication error: {e}") | |
return False, None | |
# Enhanced diagram generator with more diagram types | |
class InternalDiagramGenerator: | |
def generate_single_line_diagram(self, config: Dict) -> str: | |
"""Generate single line diagram SVG""" | |
return f""" | |
<svg width="800" height="500" viewBox="0 0 800 500" xmlns="http://www.w3.org/2000/svg"> | |
<!-- Background --> | |
<rect width="800" height="500" fill="#f8fafc" stroke="#cbd5e1" stroke-width="2" rx="10"/> | |
<text x="400" y="30" text-anchor="middle" font-family="Arial" font-size="20" font-weight="bold" fill="#1e293b"> | |
Single Line Diagram - Power System | |
</text> | |
<!-- Generator --> | |
<circle cx="100" cy="250" r="40" fill="none" stroke="#3b82f6" stroke-width="3"/> | |
<text x="100" y="255" text-anchor="middle" font-family="Arial" font-size="14" font-weight="bold" fill="#3b82f6">G</text> | |
<text x="100" y="300" text-anchor="middle" font-family="Arial" font-size="10" fill="#3b82f6">Generator</text> | |
<!-- Connection to Transformer 1 --> | |
<line x1="140" y1="250" x2="200" y2="250" stroke="#1e293b" stroke-width="3"/> | |
<!-- Transformer 1 --> | |
<circle cx="220" cy="230" r="15" fill="none" stroke="#ef4444" stroke-width="2"/> | |
<circle cx="220" cy="270" r="15" fill="none" stroke="#ef4444" stroke-width="2"/> | |
<line x1="205" y1="250" x2="235" y2="250" stroke="#ef4444" stroke-width="2"/> | |
<text x="220" y="205" text-anchor="middle" font-family="Arial" font-size="12" font-weight="bold" fill="#ef4444">T1</text> | |
<text x="220" y="300" text-anchor="middle" font-family="Arial" font-size="9" fill="#ef4444">25kV/138kV</text> | |
<!-- Connection to Bus --> | |
<line x1="235" y1="250" x2="320" y2="250" stroke="#1e293b" stroke-width="3"/> | |
<!-- Main Bus --> | |
<rect x="320" y="240" width="160" height="20" fill="none" stroke="#10b981" stroke-width="4"/> | |
<text x="400" y="235" text-anchor="middle" font-family="Arial" font-size="12" font-weight="bold" fill="#10b981">138kV Bus</text> | |
<!-- Transmission Line 1 --> | |
<line x1="480" y1="250" x2="550" y2="250" stroke="#1e293b" stroke-width="3"/> | |
<line x1="515" y1="235" x2="515" y2="265" stroke="#8b5cf6" stroke-width="2"/> | |
<line x1="510" y1="240" x2="520" y2="240" stroke="#8b5cf6" stroke-width="2"/> | |
<line x1="510" y1="260" x2="520" y2="260" stroke="#8b5cf6" stroke-width="2"/> | |
<text x="515" y="220" text-anchor="middle" font-family="Arial" font-size="10" fill="#8b5cf6">Line 1</text> | |
<!-- Load Bus --> | |
<circle cx="600" cy="250" r="25" fill="none" stroke="#f59e0b" stroke-width="3"/> | |
<text x="600" y="255" text-anchor="middle" font-family="Arial" font-size="12" font-weight="bold" fill="#f59e0b">Load</text> | |
<text x="600" y="285" text-anchor="middle" font-family="Arial" font-size="9" fill="#f59e0b">Industrial</text> | |
<!-- Protection Elements --> | |
<rect x="170" y="240" width="20" height="20" fill="none" stroke="#dc2626" stroke-width="2"/> | |
<text x="180" y="252" text-anchor="middle" font-family="Arial" font-size="8" fill="#dc2626">CB</text> | |
<rect x="290" y="240" width="20" height="20" fill="none" stroke="#dc2626" stroke-width="2"/> | |
<text x="300" y="252" text-anchor="middle" font-family="Arial" font-size="8" fill="#dc2626">CB</text> | |
<!-- Legend --> | |
<rect x="20" y="350" width="200" height="120" fill="white" stroke="#cbd5e1" stroke-width="1" rx="5"/> | |
<text x="120" y="370" text-anchor="middle" font-family="Arial" font-size="12" font-weight="bold" fill="#1e293b">Legend</text> | |
<circle cx="40" cy="390" r="8" fill="none" stroke="#3b82f6" stroke-width="2"/> | |
<text x="55" y="395" font-family="Arial" font-size="10" fill="#1e293b">Generator</text> | |
<rect x="32" y="405" width="16" height="10" fill="none" stroke="#dc2626" stroke-width="1"/> | |
<text x="55" y="413" font-family="Arial" font-size="10" fill="#1e293b">Circuit Breaker</text> | |
<circle cx="35" cy="430" r="5" fill="none" stroke="#ef4444" stroke-width="1"/> | |
<circle cx="45" cy="430" r="5" fill="none" stroke="#ef4444" stroke-width="1"/> | |
<text x="55" y="435" font-family="Arial" font-size="10" fill="#1e293b">Transformer</text> | |
<rect x="32" y="445" width="16" height="8" fill="none" stroke="#10b981" stroke-width="2"/> | |
<text x="55" y="452" font-family="Arial" font-size="10" fill="#1e293b">Bus</text> | |
</svg> | |
""" | |
def generate_protection_diagram(self, config: Dict) -> str: | |
"""Generate protection scheme diagram""" | |
return """ | |
<svg width="900" height="600" viewBox="0 0 900 600" xmlns="http://www.w3.org/2000/svg"> | |
<!-- Background --> | |
<rect width="900" height="600" fill="#f8fafc" stroke="#cbd5e1" stroke-width="2" rx="10"/> | |
<text x="450" y="30" text-anchor="middle" font-family="Arial" font-size="20" font-weight="bold" fill="#1e293b"> | |
Power System Protection Scheme | |
</text> | |
<!-- Generator and Protection --> | |
<g transform="translate(50,100)"> | |
<!-- Generator --> | |
<circle cx="60" cy="100" r="40" fill="none" stroke="#3b82f6" stroke-width="3"/> | |
<text x="60" y="105" text-anchor="middle" font-family="Arial" font-size="14" fill="#3b82f6">G</text> | |
<!-- Generator Protection --> | |
<rect x="40" y="50" width="40" height="25" fill="#fef3c7" stroke="#f59e0b" stroke-width="2" rx="3"/> | |
<text x="60" y="67" text-anchor="middle" font-family="Arial" font-size="9" fill="#92400e">87G</text> | |
<!-- Overcurrent Relay --> | |
<rect x="110" y="85" width="30" height="30" fill="#ddd6fe" stroke="#8b5cf6" stroke-width="2" rx="3"/> | |
<text x="125" y="105" text-anchor="middle" font-family="Arial" font-size="10" fill="#6b46c1">51</text> | |
<!-- Circuit Breaker --> | |
<rect x="150" y="90" width="20" height="20" fill="none" stroke="#dc2626" stroke-width="3"/> | |
<line x1="155" y1="95" x2="165" y2="105" stroke="#dc2626" stroke-width="2"/> | |
<text x="160" y="125" text-anchor="middle" font-family="Arial" font-size="8" fill="#dc2626">52G</text> | |
</g> | |
<!-- Transformer and Protection --> | |
<g transform="translate(300,100)"> | |
<!-- Transformer --> | |
<circle cx="40" cy="80" r="20" fill="none" stroke="#ef4444" stroke-width="2"/> | |
<circle cx="40" cy="120" r="20" fill="none" stroke="#ef4444" stroke-width="2"/> | |
<text x="40" y="65" text-anchor="middle" font-family="Arial" font-size="12" fill="#ef4444">T1</text> | |
<!-- Differential Protection --> | |
<rect x="15" y="40" width="50" height="20" fill="#fef3c7" stroke="#f59e0b" stroke-width="2" rx="3"/> | |
<text x="40" y="53" text-anchor="middle" font-family="Arial" font-size="10" fill="#92400e">87T</text> | |
<!-- Buchholz Relay --> | |
<rect x="70" y="90" width="35" height="20" fill="#dcfce7" stroke="#16a34a" stroke-width="2" rx="3"/> | |
<text x="87" y="103" text-anchor="middle" font-family="Arial" font-size="9" fill="#15803d">63</text> | |
<!-- OLTC Protection --> | |
<rect x="15" y="150" width="50" height="20" fill="#dbeafe" stroke="#2563eb" stroke-width="2" rx="3"/> | |
<text x="40" y="163" text-anchor="middle" font-family="Arial" font-size="9" fill="#1d4ed8">OLTC</text> | |
</g> | |
<!-- Transmission Line and Protection --> | |
<g transform="translate(450,100)"> | |
<!-- Line --> | |
<line x1="0" y1="100" x2="150" y2="100" stroke="#1e293b" stroke-width="3"/> | |
<line x1="75" y1="85" x2="75" y2="115" stroke="#8b5cf6" stroke-width="2"/> | |
<!-- Distance Protection --> | |
<rect x="30" y="60" width="30" height="25" fill="#fde68a" stroke="#f59e0b" stroke-width="2" rx="3"/> | |
<text x="45" y="77" text-anchor="middle" font-family="Arial" font-size="10" fill="#92400e">21</text> | |
<!-- Pilot Protection --> | |
<rect x="90" y="60" width="30" height="25" fill="#fecaca" stroke="#ef4444" stroke-width="2" rx="3"/> | |
<text x="105" y="77" text-anchor="middle" font-family="Arial" font-size="10" fill="#dc2626">85</text> | |
<!-- Communication --> | |
<path d="M 45 60 L 105 60" stroke="#6b7280" stroke-width="1" stroke-dasharray="3,3"/> | |
<text x="75" y="45" text-anchor="middle" font-family="Arial" font-size="8" fill="#6b7280">Pilot Wire</text> | |
</g> | |
<!-- Bus Protection --> | |
<g transform="translate(650,100)"> | |
<!-- Bus --> | |
<rect x="0" y="90" width="100" height="20" fill="none" stroke="#10b981" stroke-width="4"/> | |
<text x="50" y="85" text-anchor="middle" font-family="Arial" font-size="12" fill="#10b981">Main Bus</text> | |
<!-- Bus Differential --> | |
<rect x="25" y="50" width="50" height="25" fill="#fef3c7" stroke="#f59e0b" stroke-width="2" rx="3"/> | |
<text x="50" y="67" text-anchor="middle" font-family="Arial" font-size="10" fill="#92400e">87B</text> | |
<!-- Bus Protection --> | |
<rect x="25" y="130" width="50" height="25" fill="#e0e7ff" stroke="#6366f1" stroke-width="2" rx="3"/> | |
<text x="50" y="147" text-anchor="middle" font-family="Arial" font-size="10" fill="#4f46e5">50BF</text> | |
</g> | |
<!-- Protection Coordination Chart --> | |
<g transform="translate(50,350)"> | |
<rect x="0" y="0" width="400" height="200" fill="white" stroke="#cbd5e1" stroke-width="2" rx="5"/> | |
<text x="200" y="20" text-anchor="middle" font-family="Arial" font-size="14" font-weight="bold" fill="#1e293b"> | |
Time-Current Coordination | |
</text> | |
<!-- Axes --> | |
<line x1="50" y1="180" x2="350" y2="180" stroke="#374151" stroke-width="2"/> | |
<line x1="50" y1="50" x2="50" y2="180" stroke="#374151" stroke-width="2"/> | |
<!-- Labels --> | |
<text x="200" y="200" text-anchor="middle" font-family="Arial" font-size="10" fill="#374151">Current (A)</text> | |
<text x="20" y="115" text-anchor="middle" font-family="Arial" font-size="10" fill="#374151" transform="rotate(-90, 20, 115)">Time (s)</text> | |
<!-- Curves --> | |
<path d="M 70 160 Q 150 120 250 80 Q 300 60 330 50" stroke="#ef4444" stroke-width="2" fill="none"/> | |
<text x="270" y="70" font-family="Arial" font-size="8" fill="#ef4444">Generator (51G)</text> | |
<path d="M 90 170 Q 180 130 280 90 Q 320 70 340 60" stroke="#8b5cf6" stroke-width="2" fill="none"/> | |
<text x="290" y="90" font-family="Arial" font-size="8" fill="#8b5cf6">Feeder (51F)</text> | |
</g> | |
<!-- Device Legend --> | |
<rect x="500" y="350" width="350" height="200" fill="white" stroke="#cbd5e1" stroke-width="2" rx="5"/> | |
<text x="675" y="370" text-anchor="middle" font-family="Arial" font-size="14" font-weight="bold" fill="#1e293b"> | |
IEEE Device Numbers | |
</text> | |
<g transform="translate(520,380)"> | |
<text x="0" y="15" font-family="Arial" font-size="11" font-weight="bold" fill="#ef4444">21 - Distance Relay</text> | |
<text x="0" y="35" font-family="Arial" font-size="11" font-weight="bold" fill="#8b5cf6">51 - AC Time Overcurrent</text> | |
<text x="0" y="55" font-family="Arial" font-size="11" font-weight="bold" fill="#dc2626">52 - AC Circuit Breaker</text> | |
<text x="0" y="75" font-family="Arial" font-size="11" font-weight="bold" fill="#16a34a">63 - Pressure Switch</text> | |
<text x="0" y="95" font-family="Arial" font-size="11" font-weight="bold" fill="#ef4444">85 - Carrier/Pilot Relay</text> | |
<text x="170" y="15" font-family="Arial" font-size="11" font-weight="bold" fill="#f59e0b">87 - Differential Relay</text> | |
<text x="170" y="35" font-family="Arial" font-size="11" font-weight="bold" fill="#f59e0b">87G - Generator Differential</text> | |
<text x="170" y="55" font-family="Arial" font-size="11" font-weight="bold" fill="#f59e0b">87T - Transformer Differential</text> | |
<text x="170" y="75" font-family="Arial" font-size="11" font-weight="bold" fill="#f59e0b">87B - Bus Differential</text> | |
<text x="170" y="95" font-family="Arial" font-size="11" font-weight="bold" fill="#6366f1">50BF - Breaker Failure</text> | |
</g> | |
<text x="450" y="580" text-anchor="middle" font-family="Arial" font-size="10" fill="#64748b"> | |
Comprehensive protection scheme with coordination and backup protection | |
</text> | |
</svg> | |
""" | |
class PowerSystemsConsultant: | |
def __init__(self): | |
self.groq_client = get_groq_client() | |
# Initialize diagram generator (external or internal) | |
if EXTERNAL_UTILS_AVAILABLE: | |
try: | |
self.diagram_generator = ExternalDiagramGenerator() | |
print("✅ Using external DiagramGenerator") | |
except Exception as e: | |
print(f"⚠️ External DiagramGenerator failed: {e}") | |
self.diagram_generator = InternalDiagramGenerator() | |
print(" Falling back to internal DiagramGenerator") | |
else: | |
self.diagram_generator = InternalDiagramGenerator() | |
print("📐 Using internal DiagramGenerator") | |
# Initialize RAG system if available | |
if EXTERNAL_UTILS_AVAILABLE: | |
try: | |
self.rag_system = RAGSystem() | |
print("✅ RAG System initialized successfully") | |
self.has_rag = True | |
except Exception as e: | |
print(f"⚠️ RAG System initialization failed: {e}") | |
print(" Continuing without RAG capabilities") | |
self.rag_system = None | |
self.has_rag = False | |
else: | |
self.rag_system = None | |
self.has_rag = False | |
print("📚 RAG System not available") | |
try: | |
self.user_manager = UserManager() | |
except Exception as e: | |
print(f"❌ UserManager initialization failed: {e}") | |
self.user_manager = None | |
self.current_user = None | |
self.current_session = None | |
def generate_demo_response(self, user_query: str, chat_history: List[Tuple[str, str]]) -> Tuple[str, str]: | |
"""Generate demo response when Groq API is not available""" | |
demo_responses = { | |
"fault": "**Fault Analysis Demo Response:**\n\nFor a three-phase fault, the fault current is calculated as:\n\n`I_fault = V_nominal / Z_total`\n\nWhere:\n- V_nominal is the system nominal voltage\n- Z_total is the total impedance to the fault point\n\nThis includes positive sequence impedance of generators, transformers, and lines up to the fault location.", | |
"protection": "**Protection System Demo Response:**\n\nPower system protection involves multiple layers:\n\n1. **Primary Protection**: Fastest, most selective (e.g., differential relays)\n2. **Backup Protection**: Slower but covers larger area (e.g., overcurrent relays)\n3. **Emergency Protection**: Last resort (e.g., under-frequency load shedding)\n\nCoordination ensures proper sequence and selectivity between protection devices.", | |
"transformer": "**Transformer Demo Response:**\n\nTransformer protection typically includes:\n\n- **87T**: Differential protection (primary)\n- **51**: Overcurrent protection (backup)\n- **63**: Buchholz relay (gas accumulation)\n- **26**: Thermal protection\n- **71**: Gas density relay (SF6)\n\nThe transformer equivalent circuit uses T or π models for analysis.", | |
"default": "**Power Systems AI Consultant (Demo Mode):**\n\nI can help with fault analysis, protection systems, load flow studies, stability analysis, and more. This is demo mode - please set GROQ_API_KEY for full functionality.\n\n**Common Topics:**\n- Short circuit calculations\n- Protection coordination\n- Power quality analysis\n- Equipment sizing\n- Standards interpretation" | |
} | |
# Simple keyword matching for demo | |
query_lower = user_query.lower() | |
if any(word in query_lower for word in ['fault', 'short circuit']): | |
response = demo_responses["fault"] | |
elif any(word in query_lower for word in ['protection', 'relay', 'coordination']): | |
response = demo_responses["protection"] | |
elif any(word in query_lower for word in ['transformer', 'differential']): | |
response = demo_responses["transformer"] | |
else: | |
response = demo_responses["default"] | |
# Check for diagram request | |
diagram_svg = None | |
if any(keyword in query_lower for keyword in ['diagram', 'single line', 'drawing', 'circuit', 'protection scheme']): | |
try: | |
if 'protection' in query_lower: | |
diagram_svg = self.diagram_generator.generate_protection_diagram({}) | |
else: | |
diagram_svg = self.diagram_generator.generate_single_line_diagram({}) | |
except Exception as e: | |
print(f"Demo diagram generation error: {e}") | |
return response, diagram_svg | |
def generate_response(self, user_query: str, chat_history: List[Tuple[str, str]], session_id: str = None) -> Tuple[str, str]: | |
"""Generate response using Groq LLM with optional RAG enhancement""" | |
# Use demo mode if Groq client is not available | |
if not self.groq_client: | |
return self.generate_demo_response(user_query, chat_history) | |
try: | |
# Check if query is asking for a diagram | |
diagram_svg = None | |
diagram_requested = any(keyword in user_query.lower() for keyword in | |
['diagram', 'single line', 'drawing', 'circuit', 'protection scheme']) | |
if diagram_requested: | |
try: | |
# Try different diagram types based on query | |
if 'protection' in user_query.lower(): | |
if hasattr(self.diagram_generator, 'generate_protection_diagram'): | |
diagram_svg = self.diagram_generator.generate_protection_diagram({}) | |
else: | |
diagram_svg = self.diagram_generator.generate_single_line_diagram({}) | |
else: | |
diagram_svg = self.diagram_generator.generate_single_line_diagram({}) | |
except Exception as e: | |
print(f"Diagram generation error: {e}") | |
diagram_svg = None | |
# Enhanced system prompt | |
system_prompt = """You are a Power Systems Expert AI assistant specializing in electrical power systems. | |
You help with fault analysis, protection systems, standards interpretation, and engineering calculations. | |
Provide clear, technical explanations with practical examples and safety considerations. | |
When users request diagrams, explain that diagrams are being generated separately. | |
Focus on providing detailed technical explanations alongside visual representations.""" | |
# Use RAG for enhanced context if available | |
if self.has_rag and self.rag_system: | |
try: | |
# Get relevant context from RAG system | |
relevant_docs = self.rag_system.query(user_query, top_k=3) | |
if relevant_docs: | |
context = "\n".join([doc['content'] for doc in relevant_docs]) | |
system_prompt += f"\n\nRelevant technical context:\n{context}" | |
except Exception as e: | |
print(f"RAG query error: {e}") | |
# Prepare conversation context | |
messages = [{"role": "system", "content": system_prompt}] | |
# Add chat history (last 5 exchanges) | |
for human_msg, ai_msg in chat_history[-5:]: | |
if human_msg and ai_msg: # Ensure messages are not None | |
messages.append({"role": "user", "content": human_msg}) | |
messages.append({"role": "assistant", "content": ai_msg}) | |
# Add current query | |
messages.append({"role": "user", "content": user_query}) | |
# Generate response using Groq | |
response = self.groq_client.chat.completions.create( | |
model="llama3-70b-8192", | |
messages=messages, | |
max_tokens=2000, | |
temperature=0.8 | |
) | |
text_response = response.choices[0].message.content | |
# Add RAG attribution if used | |
if self.has_rag and any(keyword in user_query.lower() for keyword in ['standard', 'code', 'regulation']): | |
text_response += "\n\n*Enhanced with technical documentation database*" | |
return text_response, diagram_svg | |
except Exception as e: | |
error_msg = f"Error generating response: {str(e)}. Falling back to demo mode." | |
print(error_msg) | |
return self.generate_demo_response(user_query, chat_history) | |
def generate_practice_pack(self, topic: str, difficulty: str, num_questions: int) -> str: | |
"""Generate practice questions pack with optional RAG enhancement""" | |
if not self.groq_client: | |
return self.generate_demo_practice_pack(topic, difficulty, num_questions) | |
try: | |
# Enhanced prompt with RAG context if available | |
practice_prompt = f"""Generate {num_questions} power systems practice questions about {topic} at {difficulty} level. | |
Format with numbered questions, multiple choice options, and detailed solutions.""" | |
# Add RAG context for practice questions if available | |
if self.has_rag and self.rag_system: | |
try: | |
topic_docs = self.rag_system.query(f"{topic} practice questions examples", top_k=2) | |
if topic_docs: | |
context = "\n".join([doc['content'][:500] for doc in topic_docs]) | |
practice_prompt += f"\n\nReference context:\n{context}" | |
except Exception as e: | |
print(f"RAG practice enhancement error: {e}") | |
messages = [ | |
{"role": "system", "content": "You are an expert power systems engineer creating practice questions."}, | |
{"role": "user", "content": practice_prompt} | |
] | |
response = self.groq_client.chat.completions.create( | |
model="llama3-70b-8192", | |
messages=messages, | |
max_tokens=3000, | |
temperature=0.7 | |
) | |
result = response.choices[0].message.content | |
if self.has_rag: | |
result += "\n\n*Practice questions enhanced with technical documentation*" | |
return result | |
except Exception as e: | |
print(f"Practice generation error: {e}") | |
return self.generate_demo_practice_pack(topic, difficulty, num_questions) | |
def generate_demo_practice_pack(self, topic: str, difficulty: str, num_questions: int) -> str: | |
"""Generate demo practice questions when API is not available""" | |
demo_questions = { | |
"Fault Analysis": f""" | |
# {topic} Practice Pack - {difficulty} Level (Demo Mode) | |
## Question 1: Three-Phase Fault Analysis | |
A three-phase fault occurs at a bus with the following system data: | |
- System voltage: 138 kV | |
- Generator reactance: j0.15 pu | |
- Transformer reactance: j0.10 pu | |
- Line reactance: j0.08 pu | |
**Calculate the fault current in amperes.** | |
**Options:** | |
A) 12,500 A | |
B) 15,240 A | |
C) 18,960 A | |
D) 21,340 A | |
**Solution:** | |
Total reactance = 0.15 + 0.10 + 0.08 = 0.33 pu | |
Base current = 100 MVA / (√3 × 138 kV) = 418.4 A | |
Fault current = 418.4 / 0.33 = 12,677 A ≈ **12,500 A (Answer: A)** | |
--- | |
## Question 2: Symmetrical Components | |
For an unbalanced system with line currents: | |
- Ia = 100∠0° A | |
- Ib = 80∠-130° A | |
- Ic = 60∠110° A | |
**Calculate the positive sequence current I₁.** | |
**Solution:** | |
I₁ = (1/3)[Ia + a×Ib + a²×Ic] | |
Where a = 1∠120° | |
This is a demo version. Set GROQ_API_KEY for complete practice packs. | |
""", | |
"Protection Systems": f""" | |
# {topic} Practice Pack - {difficulty} Level (Demo Mode) | |
## Question 1: Overcurrent Relay Coordination | |
Two overcurrent relays are installed in series: | |
- Relay A: CT ratio 800:5, Time dial 0.5 | |
- Relay B: CT ratio 400:5, Time dial 0.3 | |
- Coordination time interval: 0.3 seconds | |
**For a fault current of 6000 A, calculate the operating time of each relay.** | |
**Solution:** | |
Primary current through Relay A = 6000 A | |
Secondary current = 6000 × (5/800) = 37.5 A | |
Using standard inverse curve equation... | |
This is a demo version. Set GROQ_API_KEY for complete solutions. | |
--- | |
## Question 2: Distance Relay Zones | |
A 100-mile transmission line has the following protection zones: | |
- Zone 1: 80% of line | |
- Zone 2: 120% of line + 50% of next line | |
- Zone 3: 200% of line | |
**Calculate the reach settings for each zone if line impedance is 0.8 Ω/mile.** | |
This is a demo version with {num_questions} questions requested. | |
""" | |
} | |
return demo_questions.get(topic, f""" | |
# {topic} Practice Pack - {difficulty} Level (Demo Mode) | |
## Demo Content | |
This is a demo version of the practice pack generator. | |
**Topics Available:** | |
- Fault Analysis | |
- Protection Systems | |
- Power Flow Studies | |
- Stability Analysis | |
- Harmonics & Power Quality | |
**To unlock full functionality:** | |
1. Set your GROQ_API_KEY environment variable | |
2. Restart the application | |
3. Generate comprehensive practice packs with detailed solutions | |
**Demo Features:** | |
- {num_questions} questions requested | |
- {difficulty} difficulty level | |
- Topic: {topic} | |
Set GROQ_API_KEY for complete practice generation! | |
""") | |
def explain_standard(self, standard: str) -> str: | |
"""Explain power systems standard with RAG enhancement""" | |
if not self.groq_client: | |
return self.generate_demo_standard_explanation(standard) | |
try: | |
standard_prompt = f"""Provide a comprehensive explanation of the {standard} standard including | |
purpose, key requirements, practical applications, and implementation considerations.""" | |
# Enhanced with RAG if available | |
if self.has_rag and self.rag_system: | |
try: | |
standard_docs = self.rag_system.query(f"{standard} standard explanation", top_k=3) | |
if standard_docs: | |
context = "\n".join([doc['content'] for doc in standard_docs]) | |
standard_prompt += f"\n\nTechnical documentation context:\n{context}" | |
except Exception as e: | |
print(f"RAG standard enhancement error: {e}") | |
messages = [ | |
{"role": "system", "content": "You are an expert in power systems standards."}, | |
{"role": "user", "content": standard_prompt} | |
] | |
response = self.groq_client.chat.completions.create( | |
model="llama3-70b-8192", | |
messages=messages, | |
max_tokens=2500, | |
temperature=0.6 | |
) | |
result = response.choices[0].message.content | |
if self.has_rag: | |
result += "\n\n*Enhanced with official standards documentation*" | |
return result | |
except Exception as e: | |
print(f"Standard explanation error: {e}") | |
return self.generate_demo_standard_explanation(standard) | |
def generate_demo_standard_explanation(self, standard: str) -> str: | |
"""Generate demo standard explanation when API is not available""" | |
demo_standards = { | |
"IEEE C37.2 - Device Function Numbers": """ | |
# IEEE C37.2 - Device Function Numbers (Demo Mode) | |
## Purpose | |
IEEE C37.2 defines standard device function numbers and contact designations for power system devices. This standard provides a universal numbering system for protective and control devices. | |
## Key Device Numbers | |
### Protection Devices | |
- **21** - Distance Relay | |
- **50** - Instantaneous Overcurrent | |
- **51** - Time Overcurrent | |
- **67** - Directional Overcurrent | |
- **87** - Differential Relay | |
### Control & Monitoring | |
- **25** - Synchronizing Check | |
- **27** - Undervoltage Relay | |
- **59** - Overvoltage Relay | |
- **81** - Frequency Relay | |
### Circuit Breakers & Switches | |
- **52** - AC Circuit Breaker | |
- **89** - Line Switch | |
- **94** - Tripping Relay | |
## Practical Applications | |
- Standardized protection schemes | |
- Universal documentation | |
- Simplified maintenance | |
- International compatibility | |
**This is demo mode. Set GROQ_API_KEY for comprehensive standard explanations.** | |
""", | |
"IEEE 1547 - Distributed Generation": """ | |
# IEEE 1547 - Distributed Generation (Demo Mode) | |
## Purpose | |
IEEE 1547 establishes criteria and requirements for interconnection of distributed resources (DR) with electric power systems (EPS). | |
## Key Requirements | |
### Voltage Regulation | |
- Voltage ride-through capabilities | |
- Power factor requirements | |
- Harmonic limits | |
### Protection Requirements | |
- Anti-islanding protection | |
- Overvoltage/undervoltage protection | |
- Frequency protection | |
### Power Quality | |
- Total harmonic distortion limits | |
- Flicker requirements | |
- DC injection limits | |
**This is demo mode. Set GROQ_API_KEY for detailed standard analysis.** | |
""" | |
} | |
return demo_standards.get(standard, f""" | |
# {standard} (Demo Mode) | |
## Standard Overview | |
This standard covers important aspects of power system design, operation, or protection. | |
## Demo Content Available | |
The full explanation of {standard} would include: | |
- **Purpose & Scope**: Why this standard exists | |
- **Key Requirements**: Technical specifications | |
- **Applications**: Where and how it's used | |
- **Implementation**: Practical guidance | |
- **Updates**: Recent revisions and changes | |
## Access Full Content | |
Set GROQ_API_KEY environment variable to access: | |
- Comprehensive explanations | |
- Detailed technical requirements | |
- Implementation examples | |
- Related standards references | |
- Practical applications | |
**Demo mode provides limited information.** | |
""") | |
def create_chat_session(self, user_id: int, title: str = None) -> str: | |
"""Create new chat session""" | |
session_id = str(uuid.uuid4()) | |
if not self.user_manager: | |
return session_id | |
try: | |
conn = sqlite3.connect(self.user_manager.db_path) | |
cursor = conn.cursor() | |
cursor.execute( | |
"INSERT INTO chat_sessions (user_id, session_id, title) VALUES (?, ?, ?)", | |
(user_id, session_id, title or f"Chat {datetime.now().strftime('%m/%d %H:%M')}") | |
) | |
conn.commit() | |
conn.close() | |
return session_id | |
except Exception as e: | |
print(f"Session creation error: {e}") | |
return session_id | |
# Initialize the consultant with enhanced error handling | |
try: | |
consultant = PowerSystemsConsultant() | |
if EXTERNAL_UTILS_AVAILABLE: | |
if consultant.groq_client: | |
initialization_status = "✅ Power Systems Consultant initialized with full functionality!" | |
else: | |
initialization_status = "⚠️ Power Systems Consultant initialized in demo mode (no GROQ_API_KEY)" | |
else: | |
initialization_status = "✅ Power Systems Consultant initialized with internal utilities!" | |
except Exception as e: | |
consultant = None | |
initialization_status = f"❌ Initialization failed: {str(e)}" | |
# Enhanced CSS with better responsiveness and accessibility | |
ENHANCED_CSS = """ | |
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap'); | |
* { | |
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; | |
box-sizing: border-box; | |
} | |
:root { | |
--primary: #3b82f6; | |
--primary-light: #60a5fa; | |
--primary-dark: #2563eb; | |
--secondary: #10b981; | |
--secondary-light: #34d399; | |
--accent: #8b5cf6; | |
--warning: #f59e0b; | |
--danger: #ef4444; | |
--success: #10b981; | |
--bg-primary: #f8fafc; | |
--bg-secondary: #f1f5f9; | |
--bg-tertiary: #e2e8f0; | |
--bg-card: rgba(255, 255, 255, 0.95); | |
--bg-glass: rgba(255, 255, 255, 0.8); | |
--text-primary: #0f172a; | |
--text-secondary: #475569; | |
--text-muted: #64748b; | |
--border: rgba(148, 163, 184, 0.3); | |
--border-light: rgba(148, 163, 184, 0.2); | |
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); | |
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); | |
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1); | |
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1); | |
--shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25); | |
} | |
/* Enhanced accessibility */ | |
*:focus { | |
outline: 2px solid var(--primary); | |
outline-offset: 2px; | |
} | |
.gradio-container { | |
background: linear-gradient(135deg, var(--bg-primary) 0%, var(--bg-secondary) 50%, var(--bg-tertiary) 100%); | |
color: var(--text-primary); | |
min-height: 100vh; | |
padding: 0; | |
} | |
/* Cover page with fixed positioning */ | |
.cover-page { | |
min-height: 100vh; | |
background: linear-gradient(135deg, | |
#f8fafc 0%, | |
#e2e8f0 30%, | |
#cbd5e1 60%, | |
#94a3b8 100%); | |
display: flex !important; | |
flex-direction: column !important; | |
align-items: center !important; | |
justify-content: center !important; | |
text-align: center; | |
padding: 2rem; | |
position: relative; | |
overflow: hidden; | |
} | |
.cover-page::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
background: | |
radial-gradient(circle at 20% 80%, rgba(59, 130, 246, 0.1) 0%, transparent 50%), | |
radial-gradient(circle at 80% 20%, rgba(139, 92, 246, 0.1) 0%, transparent 50%), | |
radial-gradient(circle at 40% 40%, rgba(16, 185, 129, 0.05) 0%, transparent 50%); | |
pointer-events: none; | |
} | |
.cover-hero { | |
z-index: 10; | |
max-width: 800px; | |
margin-bottom: 4rem; | |
position: relative; | |
} | |
.cover-icon { | |
font-size: 6rem; | |
background: linear-gradient(135deg, var(--primary), var(--accent)); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
margin-bottom: 2rem; | |
display: block; | |
line-height: 1; | |
animation: pulse 2s ease-in-out infinite alternate; | |
} | |
@keyframes pulse { | |
0% { transform: scale(1); } | |
100% { transform: scale(1.05); } | |
} | |
.cover-title { | |
font-size: clamp(2.5rem, 6vw, 4.5rem); | |
font-weight: 800; | |
background: linear-gradient(135deg, var(--text-primary), var(--text-secondary)); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
margin-bottom: 1.5rem; | |
line-height: 1.1; | |
position: relative; | |
} | |
.cover-subtitle { | |
font-size: clamp(1rem, 2.5vw, 1.25rem); | |
color: var(--text-secondary); | |
margin-bottom: 3rem; | |
line-height: 1.6; | |
max-width: 600px; | |
margin-left: auto; | |
margin-right: auto; | |
} | |
.cover-buttons { | |
display: flex; | |
gap: 2rem; | |
flex-wrap: wrap; | |
justify-content: center; | |
z-index: 10; | |
position: relative; | |
} | |
.cover-btn { | |
padding: 1.25rem 3rem !important; | |
font-size: 1.1rem !important; | |
font-weight: 600 !important; | |
border-radius: 50px !important; | |
border: none !important; | |
cursor: pointer !important; | |
transition: all 0.3s ease !important; | |
text-transform: uppercase !important; | |
letter-spacing: 0.5px !important; | |
position: relative !important; | |
overflow: hidden !important; | |
min-width: 200px !important; | |
text-decoration: none !important; | |
display: inline-flex !important; | |
align-items: center !important; | |
justify-content: center !important; | |
gap: 0.5rem !important; | |
} | |
.cover-btn-primary { | |
background: linear-gradient(135deg, var(--primary), var(--primary-light)) !important; | |
color: white !important; | |
box-shadow: var(--shadow-lg) !important; | |
} | |
.cover-btn-secondary { | |
background: linear-gradient(135deg, var(--secondary), var(--secondary-light)) !important; | |
color: white !important; | |
box-shadow: var(--shadow-lg) !important; | |
} | |
.cover-btn:hover { | |
transform: translateY(-3px) scale(1.05) !important; | |
box-shadow: var(--shadow-xl) !important; | |
} | |
.cover-btn:active { | |
transform: translateY(-1px) scale(1.02) !important; | |
} | |
/* Authentication Pages */ | |
.auth-page { | |
min-height: 100vh; | |
background: linear-gradient(135deg, #f8fafc, #e2e8f0); | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
padding: 2rem; | |
} | |
.auth-container { | |
background: var(--bg-card); | |
backdrop-filter: blur(20px); | |
border: 1px solid var(--border); | |
border-radius: 24px; | |
padding: 3rem; | |
width: 100%; | |
max-width: 450px; | |
box-shadow: var(--shadow-2xl); | |
} | |
.auth-header { | |
text-align: center; | |
margin-bottom: 2.5rem; | |
} | |
.auth-icon { | |
font-size: 3rem; | |
background: linear-gradient(135deg, var(--primary), var(--accent)); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
margin-bottom: 1rem; | |
} | |
.auth-title { | |
font-size: 2rem; | |
font-weight: 700; | |
color: var(--text-primary); | |
margin-bottom: 0.5rem; | |
} | |
.auth-subtitle { | |
color: var(--text-secondary); | |
font-size: 0.95rem; | |
} | |
/* Services Page */ | |
.services-page { | |
min-height: 100vh; | |
background: linear-gradient(135deg, #f8fafc, #e2e8f0, #cbd5e1); | |
padding: 2rem; | |
} | |
.services-header { | |
text-align: center; | |
margin-bottom: 4rem; | |
max-width: 800px; | |
margin-left: auto; | |
margin-right: auto; | |
} | |
.services-title { | |
font-size: clamp(2.5rem, 6vw, 4rem); | |
font-weight: 800; | |
background: linear-gradient(135deg, var(--text-primary), var(--text-secondary)); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
margin-bottom: 1rem; | |
} | |
.services-subtitle { | |
font-size: 1.2rem; | |
color: var(--text-secondary); | |
margin-bottom: 2rem; | |
} | |
.user-welcome { | |
background: var(--bg-card); | |
border: 1px solid var(--border); | |
border-radius: 16px; | |
padding: 1.5rem; | |
margin-bottom: 2rem; | |
text-align: center; | |
backdrop-filter: blur(20px); | |
animation: fadeIn 0.5s ease-out; | |
} | |
@keyframes fadeIn { | |
0% { opacity: 0; transform: translateY(20px); } | |
100% { opacity: 1; transform: translateY(0); } | |
} | |
.services-grid { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); | |
gap: 2rem; | |
max-width: 1200px; | |
margin: 0 auto; | |
} | |
.service-card { | |
background: var(--bg-card); | |
backdrop-filter: blur(20px); | |
border: 1px solid var(--border); | |
border-radius: 20px; | |
padding: 2.5rem; | |
text-align: center; | |
transition: all 0.4s ease; | |
cursor: pointer; | |
position: relative; | |
overflow: hidden; | |
animation: slideIn 0.6s ease-out; | |
} | |
@keyframes slideIn { | |
0% { opacity: 0; transform: translateY(30px); } | |
100% { opacity: 1; transform: translateY(0); } | |
} | |
.service-card::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
height: 4px; | |
background: linear-gradient(135deg, var(--primary), var(--accent)); | |
transform: scaleX(0); | |
transition: transform 0.3s ease; | |
} | |
.service-card:hover::before { | |
transform: scaleX(1); | |
} | |
.service-card:hover { | |
transform: translateY(-8px); | |
border-color: var(--primary); | |
box-shadow: var(--shadow-2xl); | |
} | |
.service-icon { | |
font-size: 4rem; | |
background: linear-gradient(135deg, var(--primary), var(--accent)); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
margin-bottom: 1.5rem; | |
display: block; | |
transition: transform 0.3s ease; | |
} | |
.service-card:hover .service-icon { | |
transform: scale(1.1) rotate(5deg); | |
} | |
.service-title { | |
font-size: 1.5rem; | |
font-weight: 700; | |
color: var(--text-primary); | |
margin-bottom: 1rem; | |
} | |
.service-description { | |
color: var(--text-secondary); | |
line-height: 1.6; | |
margin-bottom: 2rem; | |
} | |
.service-button { | |
background: linear-gradient(135deg, var(--primary), var(--primary-light)) !important; | |
border: none !important; | |
border-radius: 50px !important; | |
padding: 0.875rem 2rem !important; | |
font-weight: 600 !important; | |
color: white !important; | |
text-transform: uppercase !important; | |
letter-spacing: 0.5px !important; | |
transition: all 0.3s ease !important; | |
cursor: pointer !important; | |
} | |
.service-button:hover { | |
transform: translateY(-2px) !important; | |
box-shadow: var(--shadow-lg) !important; | |
} | |
/* Chat Interface - Dark section */ | |
.dark-section { | |
--bg-primary: #0f172a; | |
--bg-secondary: #1e293b; | |
--bg-tertiary: #334155; | |
--bg-card: rgba(30, 41, 59, 0.9); | |
--text-primary: #f8fafc; | |
--text-secondary: #cbd5e1; | |
--text-muted: #94a3b8; | |
--border: rgba(255, 255, 255, 0.1); | |
} | |
.dark-section .chatbot-page { | |
background: linear-gradient(135deg, var(--bg-primary), var(--bg-secondary)); | |
min-height: 100vh; | |
display: flex; | |
flex-direction: column; | |
} | |
.dark-section .chatbot-header { | |
background: var(--bg-card); | |
backdrop-filter: blur(20px); | |
border-bottom: 1px solid var(--border); | |
padding: 1.5rem 2rem; | |
color: var(--text-primary); | |
} | |
/* Enhanced Form Inputs */ | |
.gradio-textbox input, .gradio-textbox textarea { | |
background: rgba(255, 255, 255, 0.95) !important; | |
border: 2px solid var(--border) !important; | |
border-radius: 12px !important; | |
padding: 1rem !important; | |
color: var(--text-primary) !important; | |
font-size: 1rem !important; | |
transition: all 0.3s ease !important; | |
} | |
.gradio-textbox input:focus, .gradio-textbox textarea:focus { | |
border-color: var(--primary) !important; | |
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.1) !important; | |
background: white !important; | |
transform: translateY(-1px) !important; | |
} | |
.gradio-button { | |
background: linear-gradient(135deg, var(--primary), var(--primary-light)) !important; | |
border: none !important; | |
border-radius: 12px !important; | |
padding: 1rem 2rem !important; | |
font-weight: 600 !important; | |
font-size: 1rem !important; | |
color: white !important; | |
cursor: pointer !important; | |
transition: all 0.3s ease !important; | |
text-transform: uppercase !important; | |
letter-spacing: 0.5px !important; | |
} | |
.gradio-button:hover { | |
transform: translateY(-2px) !important; | |
box-shadow: var(--shadow-xl) !important; | |
background: linear-gradient(135deg, var(--primary-light), var(--primary)) !important; | |
} | |
.gradio-button:active { | |
transform: translateY(0px) !important; | |
} | |
/* Enhanced dropdown and slider styling */ | |
.gradio-dropdown select { | |
background: rgba(255, 255, 255, 0.95) !important; | |
border: 2px solid var(--border) !important; | |
border-radius: 12px !important; | |
padding: 1rem !important; | |
color: var(--text-primary) !important; | |
font-size: 1rem !important; | |
} | |
.gradio-slider input[type="range"] { | |
background: var(--primary) !important; | |
} | |
/* Loading states */ | |
.loading { | |
opacity: 0.7; | |
pointer-events: none; | |
position: relative; | |
} | |
.loading::after { | |
content: ''; | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
width: 24px; | |
height: 24px; | |
border: 3px solid var(--primary); | |
border-top: 3px solid transparent; | |
border-radius: 50%; | |
animation: spin 1s linear infinite; | |
} | |
@keyframes spin { | |
0% { transform: translate(-50%, -50%) rotate(0deg); } | |
100% { transform: translate(-50%, -50%) rotate(360deg); } | |
} | |
/* Responsive Design */ | |
@media (max-width: 768px) { | |
.cover-buttons { | |
flex-direction: column; | |
align-items: center; | |
} | |
.cover-btn { | |
width: 100% !important; | |
max-width: 300px !important; | |
} | |
.services-grid { | |
grid-template-columns: 1fr; | |
gap: 1.5rem; | |
} | |
.auth-container { | |
padding: 2rem; | |
margin: 1rem; | |
} | |
} | |
@media (max-width: 480px) { | |
.cover-page { | |
padding: 1rem; | |
} | |
.cover-icon { | |
font-size: 4rem; | |
} | |
.service-card { | |
padding: 2rem; | |
} | |
} | |
/* Print styles */ | |
@media print { | |
.cover-page::before, | |
.service-card::before { | |
display: none; | |
} | |
* { | |
background: white !important; | |
color: black !important; | |
} | |
} | |
/* High contrast mode support */ | |
@media (prefers-contrast: high) { | |
:root { | |
--border: #000; | |
--text-secondary: #000; | |
} | |
} | |
/* Reduced motion support */ | |
@media (prefers-reduced-motion: reduce) { | |
*, | |
*::before, | |
*::after { | |
animation-duration: 0.01ms !important; | |
animation-iteration-count: 1 !important; | |
transition-duration: 0.01ms !important; | |
} | |
} | |
""" | |
def create_multipage_app(): | |
with gr.Blocks( | |
theme=gr.themes.Soft( | |
primary_hue=gr.themes.colors.blue, | |
secondary_hue=gr.themes.colors.green, | |
neutral_hue=gr.themes.colors.slate, | |
), | |
title="⚡ Power Systems Consultant - Enhanced", | |
css=ENHANCED_CSS | |
) as app: | |
# Global state management | |
current_page = gr.State("cover") | |
user_state = gr.State(None) | |
session_state = gr.State(None) | |
# Page 1: Cover Page (Always visible initially) | |
with gr.Column(visible=True, elem_classes=["cover-page"]) as cover_page: | |
with gr.HTML() as cover_content: | |
gr.HTML(""" | |
<div class="cover-hero"> | |
<div class="cover-icon">⚡</div> | |
<h1 class="cover-title">Power Systems Consultant</h1> | |
<p class="cover-subtitle"> | |
Advanced AI-powered platform for electrical power systems analysis, fault calculations, | |
protection design, and engineering excellence. Experience the future of power systems consulting. | |
</p> | |
</div> | |
""") | |
with gr.Row(): | |
signin_nav_btn = gr.Button("🔐 Sign In", elem_classes=["cover-btn", "cover-btn-primary"]) | |
signup_nav_btn = gr.Button("🚀 Create Account", elem_classes=["cover-btn", "cover-btn-secondary"]) | |
# Page 2: Sign In Page | |
with gr.Column(visible=False, elem_classes=["auth-page"]) as signin_page: | |
with gr.Column(elem_classes=["auth-container"]): | |
gr.HTML(""" | |
<div class="auth-header"> | |
<div class="auth-icon">🔐</div> | |
<h2 class="auth-title">Welcome Back</h2> | |
<p class="auth-subtitle">Sign in to your Power Systems workspace</p> | |
</div> | |
""") | |
login_username = gr.Textbox( | |
label="Username", | |
placeholder="Enter your username" | |
) | |
login_password = gr.Textbox( | |
label="Password", | |
type="password", | |
placeholder="Enter your password" | |
) | |
with gr.Row(): | |
signin_btn = gr.Button("Sign In", variant="primary") | |
back_cover_signin = gr.Button("← Back", variant="secondary") | |
signin_status = gr.Textbox(label="Status", interactive=False, visible=False) | |
# Page 3: Sign Up Page | |
with gr.Column(visible=False, elem_classes=["auth-page"]) as signup_page: | |
with gr.Column(elem_classes=["auth-container"]): | |
gr.HTML(""" | |
<div class="auth-header"> | |
<div class="auth-icon">🚀</div> | |
<h2 class="auth-title">Join Our Community</h2> | |
<p class="auth-subtitle">Create your Power Systems workspace</p> | |
</div> | |
""") | |
reg_username = gr.Textbox( | |
label="Username", | |
placeholder="Choose a unique username" | |
) | |
reg_email = gr.Textbox( | |
label="Email", | |
placeholder="[email protected]" | |
) | |
reg_full_name = gr.Textbox( | |
label="Full Name", | |
placeholder="Your full name" | |
) | |
reg_password = gr.Textbox( | |
label="Password", | |
type="password", | |
placeholder="Create a strong password (min 6 chars)" | |
) | |
reg_confirm_password = gr.Textbox( | |
label="Confirm Password", | |
type="password", | |
placeholder="Confirm your password" | |
) | |
with gr.Row(): | |
signup_btn = gr.Button("Create Account", variant="primary") | |
back_cover_signup = gr.Button("← Back", variant="secondary") | |
signup_status = gr.Textbox(label="Status", interactive=False, visible=False) | |
# Page 4: Services Selection Page | |
with gr.Column(visible=False, elem_classes=["services-page"]) as services_page: | |
user_welcome_display = gr.HTML(""" | |
<div class="services-header"> | |
<h1 class="services-title">Power Systems Workspace</h1> | |
<p class="services-subtitle">Choose your engineering tool and start analyzing power systems</p> | |
<div class="user-welcome" id="user-welcome-display"> | |
<div style="display: flex; align-items: center; justify-content: center; gap: 1rem;"> | |
<div style="background: linear-gradient(135deg, var(--success), var(--secondary-light)); border-radius: 50%; width: 50px; height: 50px; display: flex; align-items: center; justify-content: center;"> | |
<span style="color: white; font-size: 1.2rem;">👤</span> | |
</div> | |
<div> | |
<h3 style="margin: 0; color: var(--text-primary);">Welcome to Power Systems!</h3> | |
<p style="margin: 0; color: var(--text-secondary); font-size: 0.9rem;">Ready to tackle power systems challenges?</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
""") | |
with gr.Row(): | |
with gr.Column(elem_classes=["service-card"]): | |
gr.HTML(""" | |
<div style="text-align: center;"> | |
<div class="service-icon">🤖</div> | |
<h3 class="service-title">AI Consultant</h3> | |
<p class="service-description"> | |
Interactive chat with AI expert for fault analysis, protection systems, | |
calculations, and engineering guidance with professional diagram generation. | |
</p> | |
</div> | |
""") | |
chatbot_service_btn = gr.Button("Launch Consultant", elem_classes=["service-button"]) | |
with gr.Column(elem_classes=["service-card"]): | |
gr.HTML(""" | |
<div style="text-align: center;"> | |
<div class="service-icon">📚</div> | |
<h3 class="service-title">Practice Generator</h3> | |
<p class="service-description"> | |
Generate customized practice questions with detailed solutions, | |
step-by-step calculations, and standards references for exam preparation. | |
</p> | |
</div> | |
""") | |
practice_service_btn = gr.Button("Generate Practice", elem_classes=["service-button"]) | |
with gr.Row(): | |
with gr.Column(elem_classes=["service-card"]): | |
gr.HTML(""" | |
<div style="text-align: center;"> | |
<div class="service-icon">📋</div> | |
<h3 class="service-title">Standards Explorer</h3> | |
<p class="service-description"> | |
Comprehensive explanations of IEEE, IEC, and international standards | |
with practical applications and implementation guidelines. | |
</p> | |
</div> | |
""") | |
standards_service_btn = gr.Button("Explore Standards", elem_classes=["service-button"]) | |
with gr.Column(elem_classes=["service-card"]): | |
gr.HTML(""" | |
<div style="text-align: center;"> | |
<div class="service-icon">🎓</div> | |
<h3 class="service-title">Study Resources</h3> | |
<p class="service-description"> | |
Essential formulas, concepts, calculations, and comprehensive | |
exam preparation materials for power systems engineering. | |
</p> | |
</div> | |
""") | |
study_service_btn = gr.Button("Access Resources", elem_classes=["service-button"]) | |
with gr.Row(): | |
logout_btn = gr.Button("🚪 Sign Out", variant="secondary") | |
# Page 5: Chatbot Interface | |
with gr.Column(visible=False, elem_classes=["dark-section"]) as chatbot_page: | |
with gr.Column(elem_classes=["chatbot-page"]): | |
gr.HTML(""" | |
<div class="chatbot-header"> | |
<h2 style="color: var(--text-primary); margin: 0;">🤖 AI Power Systems Consultant</h2> | |
<p style="color: var(--text-secondary); margin: 0.5rem 0 0 0;">Ask about fault analysis, protection systems, calculations, or request diagrams</p> | |
</div> | |
""") | |
with gr.Row(): | |
back_services_chat = gr.Button("← Back to Services", variant="secondary") | |
new_chat_btn = gr.Button("➕ New Chat", variant="primary") | |
# Chat interface with enhanced styling | |
chatbot = gr.Chatbot( | |
height=400, | |
show_label=False, | |
avatar_images=("👤", "🤖"), | |
bubble_full_width=False | |
) | |
# Diagram display with loading state | |
diagram_display = gr.HTML(""" | |
<div style="background: var(--bg-card); border: 1px solid var(--border); border-radius: 16px; padding: 2rem; margin: 1rem 0; text-align: center;"> | |
<div style="color: var(--text-muted); padding: 2rem;"> | |
<div style="font-size: 3rem; margin-bottom: 1rem;">📐</div> | |
<p>Professional engineering diagrams will appear here</p> | |
<small>Ask for single line diagrams, protection schemes, or circuit drawings</small> | |
</div> | |
</div> | |
""") | |
# Enhanced input area | |
with gr.Row(): | |
msg = gr.Textbox( | |
placeholder="Ask about faults, protection, calculations, or request diagrams...", | |
show_label=False, | |
lines=3, | |
scale=4 | |
) | |
with gr.Column(scale=1): | |
submit_btn = gr.Button("🚀 Send", variant="primary") | |
clear_btn = gr.Button("🗑️ Clear", variant="secondary") | |
# Quick action buttons | |
with gr.Row(): | |
with gr.Column(): | |
gr.HTML("<p><strong>Quick Actions:</strong></p>") | |
quick_fault_btn = gr.Button("⚡ Fault Analysis", size="sm") | |
quick_protection_btn = gr.Button("🛡️ Protection", size="sm") | |
with gr.Column(): | |
gr.HTML("<p><strong>Diagrams:</strong></p>") | |
quick_sld_btn = gr.Button("📐 Single Line", size="sm") | |
quick_protection_diagram_btn = gr.Button("🔧 Protection Scheme", size="sm") | |
# Page 6: Practice Generator | |
with gr.Column(visible=False, elem_classes=["services-page"]) as practice_page: | |
gr.HTML(""" | |
<div class="services-header"> | |
<h1 class="services-title">📚 Practice Generator</h1> | |
<p class="services-subtitle">AI-generated practice questions with detailed solutions</p> | |
</div> | |
""") | |
back_services_practice = gr.Button("← Back to Services", variant="secondary") | |
with gr.Row(): | |
with gr.Column(scale=1): | |
gr.HTML('<h3 style="color: var(--text-primary); margin-bottom: 2rem;">⚙️ Customize Practice Pack</h3>') | |
topic_input = gr.Dropdown( | |
choices=[ | |
"Fault Analysis", "Protection Systems", "Power Flow", | |
"Stability Studies", "Relay Coordination", "Transformers", | |
"Transmission Lines", "Load Flow", "Short Circuit Analysis", | |
"Harmonics", "Power Quality", "SCADA Systems", "Synchronous Machines", | |
"Power Electronics", "Renewable Energy Integration" | |
], | |
label="Select Topic", | |
value="Fault Analysis" | |
) | |
difficulty_input = gr.Radio( | |
choices=["Beginner", "Intermediate", "Advanced", "Expert"], | |
label="Difficulty Level", | |
value="Intermediate" | |
) | |
num_questions_input = gr.Slider( | |
minimum=5, | |
maximum=25, | |
step=1, | |
value=10, | |
label="Number of Questions" | |
) | |
include_solutions = gr.Checkbox( | |
label="Include detailed solutions", | |
value=True | |
) | |
generate_btn = gr.Button("🚀 Generate Practice Pack", variant="primary") | |
# Practice pack templates | |
with gr.Accordion("📋 Practice Templates", open=False): | |
template_pe_exam = gr.Button("PE Exam Prep", variant="secondary", size="sm") | |
template_ieee_standards = gr.Button("IEEE Standards", variant="secondary", size="sm") | |
template_fault_calc = gr.Button("Fault Calculations", variant="secondary", size="sm") | |
with gr.Column(scale=2): | |
practice_output = gr.Textbox( | |
label="Generated Practice Pack", | |
lines=25, | |
placeholder="Your customized practice questions will appear here...", | |
show_label=False | |
) | |
# Export options | |
with gr.Row(): | |
export_pdf_btn = gr.Button("📄 Export PDF", variant="secondary", size="sm") | |
export_txt_btn = gr.Button("📝 Export Text", variant="secondary", size="sm") | |
# Page 7: Standards Explorer | |
with gr.Column(visible=False, elem_classes=["services-page"]) as standards_page: | |
gr.HTML(""" | |
<div class="services-header"> | |
<h1 class="services-title">📋 Standards Explorer</h1> | |
<p class="services-subtitle">Comprehensive power systems standards library</p> | |
</div> | |
""") | |
back_services_standards = gr.Button("← Back to Services", variant="secondary") | |
with gr.Row(): | |
with gr.Column(scale=1): | |
gr.HTML('<h3 style="color: var(--text-primary); margin-bottom: 2rem;">📖 Explore Standards</h3>') | |
standard_category = gr.Radio( | |
choices=["IEEE Standards", "IEC Standards", "ANSI Standards", "All Standards"], | |
label="Category", | |
value="IEEE Standards" | |
) | |
standard_input = gr.Dropdown( | |
choices=[ | |
"IEEE C37.2 - Device Function Numbers", | |
"IEEE 1547 - Distributed Generation", | |
"IEEE 519 - Harmonic Control", | |
"IEEE C57.12.00 - Transformers", | |
"IEEE 242 - Protection & Coordination", | |
"IEEE 399 - Industrial Power Systems", | |
"IEEE C37.010 - Application Guide", | |
"IEC 61850 - Substation Automation", | |
"IEC 60909 - Short-Circuit Calculations", | |
"IEC 61131 - PLC Programming", | |
"IEC 60255 - Electrical Relays", | |
"ANSI C84.1 - Voltage Ratings" | |
], | |
label="Select Standard", | |
value="IEEE C37.2 - Device Function Numbers" | |
) | |
detail_level = gr.Radio( | |
choices=["Overview", "Detailed", "Implementation Guide"], | |
label="Detail Level", | |
value="Detailed" | |
) | |
explain_btn = gr.Button("🔍 Explain Standard", variant="primary") | |
# Quick access buttons | |
with gr.Accordion("🚀 Quick Access", open=False): | |
quick_protection_standard = gr.Button("Protection Standards", variant="secondary", size="sm") | |
quick_safety_standard = gr.Button("Safety Standards", variant="secondary", size="sm") | |
quick_testing_standard = gr.Button("Testing Standards", variant="secondary", size="sm") | |
with gr.Column(scale=2): | |
standard_output = gr.Textbox( | |
label="Standard Explanation", | |
lines=25, | |
placeholder="Detailed standard explanation will appear here...", | |
show_label=False | |
) | |
# Related standards | |
related_standards = gr.HTML(""" | |
<div style="background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px; padding: 1rem; margin-top: 1rem;"> | |
<h4 style="color: var(--text-primary); margin: 0 0 1rem 0;">🔗 Related Standards</h4> | |
<p style="color: var(--text-secondary); margin: 0;">Select a standard to see related documents</p> | |
</div> | |
""") | |
# Page 8: Enhanced Study Resources | |
with gr.Column(visible=False, elem_classes=["services-page"]) as study_page: | |
gr.HTML(""" | |
<div class="services-header"> | |
<h1 class="services-title">🎓 Study Resources</h1> | |
<p class="services-subtitle">Essential formulas, concepts, and exam preparation materials</p> | |
</div> | |
""") | |
back_services_study = gr.Button("← Back to Services", variant="secondary") | |
# Enhanced study sections | |
with gr.Tabs(): | |
with gr.Tab("⚡ Formulas & Equations"): | |
with gr.Accordion("⚡ Fault Analysis Formulas", open=True): | |
gr.HTML(""" | |
<div style="background: rgba(59, 130, 246, 0.1); border: 1px solid rgba(59, 130, 246, 0.3); border-radius: 16px; padding: 2rem; margin: 1.5rem 0;"> | |
<h4 style="color: var(--primary); margin-bottom: 1rem; display: flex; align-items: center; gap: 0.75rem;"> | |
⚡ Three-Phase Fault Current | |
</h4> | |
<div style="background: rgba(0,0,0,0.1); border-radius: 8px; padding: 1rem; margin: 1rem 0;"> | |
<code style="color: var(--primary); font-size: 1.1rem; font-weight: bold;"> | |
I_fault = V_nominal / Z_total | |
</code> | |
</div> | |
<p style="color: var(--text-secondary); margin: 0.5rem 0;"> | |
Where: V_nominal = System nominal voltage, Z_total = Total impedance to fault | |
</p> | |
<h4 style="color: var(--primary); margin: 2rem 0 1rem 0;">⚠️ Line-to-Ground Fault</h4> | |
<div style="background: rgba(0,0,0,0.1); border-radius: 8px; padding: 1rem; margin: 1rem 0;"> | |
<code style="color: var(--primary); font-size: 1.1rem; font-weight: bold;"> | |
I_fault = 3 × V_nominal / (Z1 + Z2 + Z0) | |
</code> | |
</div> | |
<h4 style="color: var(--primary); margin: 2rem 0 1rem 0;">↔️ Line-to-Line Fault</h4> | |
<div style="background: rgba(0,0,0,0.1); border-radius: 8px; padding: 1rem; margin: 1rem 0;"> | |
<code style="color: var(--primary); font-size: 1.1rem; font-weight: bold;"> | |
I_fault = √3 × V_nominal / (Z1 + Z2) | |
</code> | |
</div> | |
</div> | |
""") | |
with gr.Accordion("🧮 Power System Calculations", open=False): | |
gr.HTML(""" | |
<div style="background: rgba(16, 185, 129, 0.1); border: 1px solid rgba(16, 185, 129, 0.3); border-radius: 16px; padding: 2rem; margin: 1.5rem 0;"> | |
<h4 style="color: var(--secondary); margin-bottom: 1rem;">🧮 Three-Phase Power Formulas</h4> | |
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; margin: 2rem 0;"> | |
<div> | |
<h6 style="color: var(--secondary); margin: 0 0 1rem 0;">Real Power</h6> | |
<div style="background: rgba(0,0,0,0.1); border-radius: 8px; padding: 1rem;"> | |
<code style="color: var(--secondary); font-weight: bold;">P = √3 × V × I × cos(φ)</code> | |
</div> | |
</div> | |
<div> | |
<h6 style="color: var(--secondary); margin: 0 0 1rem 0;">Reactive Power</h6> | |
<div style="background: rgba(0,0,0,0.1); border-radius: 8px; padding: 1rem;"> | |
<code style="color: var(--secondary); font-weight: bold;">Q = √3 × V × I × sin(φ)</code> | |
</div> | |
</div> | |
</div> | |
<div style="margin-top: 2rem;"> | |
<h6 style="color: var(--secondary); margin: 0 0 1rem 0;">Apparent Power</h6> | |
<div style="background: rgba(0,0,0,0.1); border-radius: 8px; padding: 1rem;"> | |
<code style="color: var(--secondary); font-weight: bold;">S = √(P² + Q²) = √3 × V × I</code> | |
</div> | |
</div> | |
</div> | |
""") | |
with gr.Tab("🛡️ Protection Systems"): | |
with gr.Accordion("🛡️ Protection Principles", open=True): | |
gr.HTML(""" | |
<div style="background: rgba(139, 92, 246, 0.1); border: 1px solid rgba(139, 92, 246, 0.3); border-radius: 16px; padding: 2rem; margin: 1.5rem 0;"> | |
<h4 style="color: var(--accent); margin-bottom: 2rem;">Protection System Requirements</h4> | |
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.5rem;"> | |
<div style="background: rgba(59, 130, 246, 0.1); padding: 1.5rem; border-radius: 10px;"> | |
<h6 style="color: var(--primary); margin: 0 0 1rem 0;">🎯 Selectivity</h6> | |
<p style="color: var(--text-secondary); margin: 0; line-height: 1.6;"> | |
Only the protection closest to the fault should operate, isolating the minimum system area. | |
</p> | |
</div> | |
<div style="background: rgba(16, 185, 129, 0.1); padding: 1.5rem; border-radius: 10px;"> | |
<h6 style="color: var(--secondary); margin: 0 0 1rem 0;">⚡ Speed</h6> | |
<p style="color: var(--text-secondary); margin: 0; line-height: 1.6;"> | |
Fast fault clearing to maintain system stability and minimize equipment damage. | |
</p> | |
</div> | |
<div style="background: rgba(245, 158, 11, 0.1); padding: 1.5rem; border-radius: 10px;"> | |
<h6 style="color: var(--warning); margin: 0 0 1rem 0;">🔒 Security</h6> | |
<p style="color: var(--text-secondary); margin: 0; line-height: 1.6;"> | |
Protection should not operate for external faults or during normal conditions. | |
</p> | |
</div> | |
<div style="background: rgba(239, 68, 68, 0.1); padding: 1.5rem; border-radius: 10px;"> | |
<h6 style="color: var(--danger); margin: 0 0 1rem 0;">🛡️ Dependability</h6> | |
<p style="color: var(--text-secondary); margin: 0; line-height: 1.6;"> | |
Protection must operate when required, with backup protection available. | |
</p> | |
</div> | |
</div> | |
</div> | |
""") | |
with gr.Tab("🧮 Interactive Calculator"): | |
gr.HTML(""" | |
<div style="background: var(--bg-card); border: 1px solid var(--border); border-radius: 20px; padding: 2.5rem; margin: 2rem 0;"> | |
<h3 style="color: var(--text-primary); margin-bottom: 2rem; text-align: center;"> | |
🧮 Power Systems Calculator | |
</h3> | |
</div> | |
""") | |
with gr.Tabs(): | |
with gr.Tab("⚡ Power Calculations"): | |
with gr.Row(): | |
with gr.Column(): | |
calc_voltage = gr.Number(label="Voltage (V)", value=480) | |
calc_current = gr.Number(label="Current (A)", value=100) | |
calc_pf = gr.Slider(0.1, 1.0, 0.85, label="Power Factor", step=0.01) | |
calc_btn = gr.Button("🧮 Calculate Power", variant="primary") | |
with gr.Column(): | |
calc_results = gr.HTML(""" | |
<div style="background: var(--bg-card); padding: 2rem; border-radius: 15px; border: 1px solid var(--border); min-height: 250px; display: flex; align-items: center; justify-content: center;"> | |
<div style="text-align: center; color: var(--text-secondary);"> | |
<div style="font-size: 3rem; margin-bottom: 1rem; opacity: 0.5;">🧮</div> | |
<p>Enter values and click Calculate</p> | |
</div> | |
</div> | |
""") | |
with gr.Tab("🔧 Fault Current"): | |
with gr.Row(): | |
with gr.Column(): | |
fault_voltage = gr.Number(label="System Voltage (kV)", value=138) | |
fault_mva = gr.Number(label="MVA Base", value=100) | |
fault_z1 = gr.Number(label="Positive Sequence Z (pu)", value=0.15) | |
fault_z2 = gr.Number(label="Negative Sequence Z (pu)", value=0.15) | |
fault_z0 = gr.Number(label="Zero Sequence Z (pu)", value=0.05) | |
fault_calc_btn = gr.Button("⚡ Calculate Fault Current", variant="primary") | |
with gr.Column(): | |
fault_results = gr.HTML(""" | |
<div style="background: var(--bg-card); padding: 2rem; border-radius: 15px; border: 1px solid var(--border); min-height: 250px; display: flex; align-items: center; justify-content: center;"> | |
<div style="text-align: center; color: var(--text-secondary);"> | |
<div style="font-size: 3rem; margin-bottom: 1rem; opacity: 0.5;">⚡</div> | |
<p>Enter system parameters</p> | |
</div> | |
</div> | |
""") | |
# Event handler functions with enhanced error handling | |
def handle_signin(username, password): | |
if not consultant or not consultant.user_manager: | |
return gr.update(), gr.update(), gr.update(), gr.update(), None, "⚠️ System not fully initialized" | |
if not username or not password: | |
return gr.update(), gr.update(), gr.update(), gr.update(), None, "⚠️ Please enter both username and password" | |
success, user_data = consultant.user_manager.authenticate_user(username, password) | |
if success: | |
return ( | |
gr.update(visible=False), # Hide signin page | |
gr.update(visible=False), # Hide signup page | |
gr.update(visible=False), # Hide cover page | |
gr.update(visible=True), # Show services page | |
user_data, # Store user data | |
f"✅ Welcome back, {user_data['full_name']}!" | |
) | |
else: | |
return gr.update(), gr.update(), gr.update(), gr.update(), None, "❌ Invalid credentials" | |
# Email validation | |
if "@" not in email or "." not in email: | |
return gr.update(), gr.update(), gr.update(), gr.update(), None, "⚠️ Please enter a valid email address" | |
success, message = consultant.user_manager.create_user(username, email, password, full_name) | |
if success: | |
# Auto-login after successful registration | |
success, user_data = consultant.user_manager.authenticate_user(username, password) | |
if success: | |
return ( | |
gr.update(visible=False), # Hide signin page | |
gr.update(visible=False), # Hide signup page | |
gr.update(visible=False), # Hide cover page | |
gr.update(visible=True), # Show services page | |
user_data, # Store user data | |
f"✅ Account created successfully! Welcome {user_data['full_name']}!" | |
) | |
return gr.update(), gr.update(), gr.update(), gr.update(), None, f"❌ {message}" | |
def handle_new_chat(user_data): | |
if not consultant or not user_data: | |
return None, [] | |
session_id = consultant.create_chat_session(user_data['id']) | |
return session_id, [] | |
def handle_chat_message(message, history, user_data, session_id): | |
if not consultant or not user_data: | |
return history, "", None | |
if not message.strip(): | |
return history, message, None | |
try: | |
response, diagram_svg = consultant.generate_response(message, history, session_id) | |
new_history = history + [[message, response]] | |
# Update diagram display if available | |
if diagram_svg: | |
diagram_html = f""" | |
<div style="background: white; padding: 1rem; border-radius: 12px; margin: 1rem 0; box-shadow: var(--shadow);"> | |
{diagram_svg} | |
</div> | |
""" | |
else: | |
diagram_html = """ | |
<div style="background: var(--bg-card); border: 1px solid var(--border); border-radius: 16px; padding: 2rem; margin: 1rem 0; text-align: center;"> | |
<div style="color: var(--text-muted); padding: 2rem;"> | |
<div style="font-size: 3rem; margin-bottom: 1rem;">📐</div> | |
<p>Professional engineering diagrams will appear here</p> | |
<small>Ask for single line diagrams, protection schemes, or circuit drawings</small> | |
</div> | |
</div> | |
""" | |
return new_history, "", diagram_html | |
except Exception as e: | |
error_response = f"❌ Error: {str(e)}" | |
return history + [[message, error_response]], "", None | |
def clear_chat(): | |
return [], "", """ | |
<div style="background: var(--bg-card); border: 1px solid var(--border); border-radius: 16px; padding: 2rem; margin: 1rem 0; text-align: center;"> | |
<div style="color: var(--text-muted); padding: 2rem;"> | |
<div style="font-size: 3rem; margin-bottom: 1rem;">📐</div> | |
<p>Professional engineering diagrams will appear here</p> | |
<small>Ask for single line diagrams, protection schemes, or circuit drawings</small> | |
</div> | |
</div> | |
""" | |
def handle_quick_action(action, history, user_data, session_id): | |
"""Handle quick action buttons""" | |
quick_messages = { | |
"fault": "Explain three-phase fault analysis and show me a single line diagram", | |
"protection": "Describe power system protection coordination and show a protection scheme", | |
"sld": "Generate a single line diagram for a typical power system", | |
"protection_diagram": "Create a comprehensive protection scheme diagram" | |
} | |
message = quick_messages.get(action, "") | |
if message: | |
return handle_chat_message(message, history, user_data, session_id) | |
return history, "", None | |
def handle_practice_generation(topic, difficulty, num_questions, include_solutions, user_data): | |
if not consultant or not user_data: | |
return "⚠️ Please log in to access this feature." | |
try: | |
result = consultant.generate_practice_pack(topic, difficulty, int(num_questions)) | |
if not include_solutions: | |
# Simple processing to remove solution sections | |
lines = result.split('\n') | |
filtered_lines = [] | |
skip_solution = False | |
for line in lines: | |
if line.strip().lower().startswith('**solution'): | |
skip_solution = True | |
continue | |
elif line.strip().startswith('---') or line.strip().startswith('##'): | |
skip_solution = False | |
if not skip_solution: | |
filtered_lines.append(line) | |
result = '\n'.join(filtered_lines) | |
return result | |
except Exception as e: | |
return f"❌ Error generating practice pack: {str(e)}" | |
def handle_standard_explanation(standard, detail_level, user_data): | |
if not consultant or not user_data: | |
return "⚠️ Please log in to access this feature." | |
try: | |
result = consultant.explain_standard(standard) | |
# Add detail level processing | |
if detail_level == "Overview": | |
lines = result.split('\n')[:15] # First 15 lines for overview | |
result = '\n'.join(lines) + "\n\n*This is an overview. Select 'Detailed' for complete information.*" | |
elif detail_level == "Implementation Guide": | |
result += "\n\n## Implementation Checklist\n- Review standard requirements\n- Assess current system compliance\n- Plan implementation phases\n- Conduct testing and validation\n- Document compliance" | |
return result | |
except Exception as e: | |
return f"❌ Error explaining standard: {str(e)}" | |
def calculate_power(voltage, current, pf): | |
if voltage and current and pf and voltage > 0 and current > 0: | |
try: | |
p = (3**0.5) * voltage * current * pf / 1000 # kW | |
q = (3**0.5) * voltage * current * ((1-pf**2)**0.5) / 1000 # kVAR | |
s = (3**0.5) * voltage * current / 1000 # kVA | |
return f""" | |
<div style="background: var(--bg-card); padding: 2rem; border-radius: 15px; border: 1px solid var(--border);"> | |
<h4 style="color: var(--text-primary); margin-bottom: 2rem; text-align: center;"> | |
📊 Power Calculation Results | |
</h4> | |
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1.5rem;"> | |
<div style="background: rgba(16, 185, 129, 0.1); padding: 1.5rem; border-radius: 10px; text-align: center;"> | |
<h6 style="color: var(--secondary); margin: 0 0 0.5rem 0;">Real Power</h6> | |
<p style="font-size: 1.5rem; font-weight: 700; margin: 0; color: var(--text-primary);">{p:.2f} kW</p> | |
</div> | |
<div style="background: rgba(245, 158, 11, 0.1); padding: 1.5rem; border-radius: 10px; text-align: center;"> | |
<h6 style="color: var(--warning); margin: 0 0 0.5rem 0;">Reactive Power</h6> | |
<p style="font-size: 1.5rem; font-weight: 700; margin: 0; color: var(--text-primary);">{q:.2f} kVAR</p> | |
</div> | |
<div style="background: rgba(59, 130, 246, 0.1); padding: 1.5rem; border-radius: 10px; text-align: center;"> | |
<h6 style="color: var(--primary); margin: 0 0 0.5rem 0;">Apparent Power</h6> | |
<p style="font-size: 1.5rem; font-weight: 700; margin: 0; color: var(--text-primary);">{s:.2f} kVA</p> | |
</div> | |
</div> | |
<div style="margin-top: 2rem; padding: 1rem; background: rgba(0,0,0,0.05); border-radius: 8px;"> | |
<h6 style="color: var(--text-primary); margin: 0 0 1rem 0;">Calculation Details:</h6> | |
<p style="margin: 0.5rem 0; color: var(--text-secondary);">P = √3 × V × I × cos(φ) = √3 × {voltage} × {current} × {pf:.3f} = {p:.2f} kW</p> | |
<p style="margin: 0.5rem 0; color: var(--text-secondary);">Q = √3 × V × I × sin(φ) = √3 × {voltage} × {current} × {((1-pf**2)**0.5):.3f} = {q:.2f} kVAR</p> | |
<p style="margin: 0.5rem 0; color: var(--text-secondary);">S = √3 × V × I = √3 × {voltage} × {current} = {s:.2f} kVA</p> | |
</div> | |
</div> | |
""" | |
except Exception as e: | |
return f""" | |
<div style="background: var(--bg-card); padding: 2rem; border-radius: 15px; border: 1px solid var(--border); text-align: center;"> | |
<div style="font-size: 3rem; color: var(--danger); margin-bottom: 1rem;">❌</div> | |
<p>Calculation error: {str(e)}</p> | |
</div> | |
""" | |
else: | |
return """ | |
<div style="background: var(--bg-card); padding: 2rem; border-radius: 15px; border: 1px solid var(--border); text-align: center;"> | |
<div style="font-size: 3rem; color: var(--warning); margin-bottom: 1rem;">⚠️</div> | |
<p>Please enter valid positive values for all fields</p> | |
</div> | |
""" | |
def calculate_fault_current(voltage, mva_base, z1, z2, z0): | |
if all(v is not None and v > 0 for v in [voltage, mva_base, z1]): | |
try: | |
# Base current calculation | |
base_current = mva_base * 1000 / (voltage * (3**0.5)) # Base current in A | |
# Three-phase fault | |
i_3ph = base_current / z1 | |
# Single line-to-ground fault | |
if z2 is not None and z0 is not None and z2 > 0 and z0 > 0: | |
i_slg = 3 * base_current / (z1 + z2 + z0) | |
else: | |
i_slg = None | |
# Line-to-line fault | |
if z2 is not None and z2 > 0: | |
i_ll = (3**0.5) * base_current / (z1 + z2) | |
else: | |
i_ll = None | |
result_html = f""" | |
<div style="background: var(--bg-card); padding: 2rem; border-radius: 15px; border: 1px solid var(--border);"> | |
<h4 style="color: var(--text-primary); margin-bottom: 2rem; text-align: center;"> | |
⚡ Fault Current Analysis | |
</h4> | |
<div style="margin-bottom: 2rem; padding: 1rem; background: rgba(59, 130, 246, 0.1); border-radius: 8px;"> | |
<h6 style="color: var(--primary); margin: 0 0 1rem 0;">System Parameters:</h6> | |
<p style="margin: 0.3rem 0; color: var(--text-secondary);">Voltage: {voltage} kV</p> | |
<p style="margin: 0.3rem 0; color: var(--text-secondary);">MVA Base: {mva_base} MVA</p> | |
<p style="margin: 0.3rem 0; color: var(--text-secondary);">Base Current: {base_current:.1f} A</p> | |
</div> | |
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.5rem;"> | |
<div style="background: rgba(239, 68, 68, 0.1); padding: 1.5rem; border-radius: 10px; text-align: center;"> | |
<h6 style="color: var(--danger); margin: 0 0 0.5rem 0;">Three-Phase Fault</h6> | |
<p style="font-size: 1.3rem; font-weight: 700; margin: 0; color: var(--text-primary);">{i_3ph:.0f} A</p> | |
<small style="color: var(--text-secondary);">I = I_base / Z₁</small> | |
</div> | |
""" | |
if i_slg is not None: | |
result_html += f""" | |
<div style="background: rgba(245, 158, 11, 0.1); padding: 1.5rem; border-radius: 10px; text-align: center;"> | |
<h6 style="color: var(--warning); margin: 0 0 0.5rem 0;">Line-to-Ground</h6> | |
<p style="font-size: 1.3rem; font-weight: 700; margin: 0; color: var(--text-primary);">{i_slg:.0f} A</p> | |
<small style="color: var(--text-secondary);">I = 3×I_base / (Z₁+Z₂+Z₀)</small> | |
</div> | |
""" | |
if i_ll is not None: | |
result_html += f""" | |
<div style="background: rgba(139, 92, 246, 0.1); padding: 1.5rem; border-radius: 10px; text-align: center;"> | |
<h6 style="color: var(--accent); margin: 0 0 0.5rem 0;">Line-to-Line</h6> | |
<p style="font-size: 1.3rem; font-weight: 700; margin: 0; color: var(--text-primary);">{i_ll:.0f} A</p> | |
<small style="color: var(--text-secondary);">I = √3×I_base / (Z₁+Z₂)</small> | |
</div> | |
""" | |
result_html += """ | |
</div> | |
</div> | |
""" | |
return result_html | |
except Exception as e: | |
return f""" | |
<div style="background: var(--bg-card); padding: 2rem; border-radius: 15px; border: 1px solid var(--border); text-align: center;"> | |
<div style="font-size: 3rem; color: var(--danger); margin-bottom: 1rem;">❌</div> | |
<p>Calculation error: {str(e)}</p> | |
</div> | |
""" | |
else: | |
return """ | |
<div style="background: var(--bg-card); padding: 2rem; border-radius: 15px; border: 1px solid var(--border); text-align: center;"> | |
<div style="font-size: 3rem; color: var(--warning); margin-bottom: 1rem;">⚠️</div> | |
<p>Please enter valid positive values for voltage, MVA base, and impedances</p> | |
</div> | |
""" | |
# Navigation event handlers | |
signin_nav_btn.click( | |
fn=lambda: (gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)), | |
outputs=[cover_page, signin_page, signup_page] | |
) | |
signup_nav_btn.click( | |
fn=lambda: (gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)), | |
outputs=[cover_page, signin_page, signup_page] | |
) | |
back_cover_signin.click( | |
fn=lambda: (gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)), | |
outputs=[cover_page, signin_page, signup_page] | |
) | |
back_cover_signup.click( | |
fn=lambda: (gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)), | |
outputs=[cover_page, signin_page, signup_page] | |
) | |
# Authentication event handlers | |
signin_btn.click( | |
fn=handle_signin, | |
inputs=[login_username, login_password], | |
outputs=[signin_page, signup_page, cover_page, services_page, user_state, signin_status] | |
) | |
signup_btn.click( | |
fn=handle_signup, | |
inputs=[reg_username, reg_email, reg_password, reg_confirm_password, reg_full_name], | |
outputs=[signin_page, signup_page, cover_page, services_page, user_state, signup_status] | |
) | |
# Service navigation | |
chatbot_service_btn.click( | |
fn=lambda: (gr.update(visible=False), gr.update(visible=True)), | |
outputs=[services_page, chatbot_page] | |
) | |
practice_service_btn.click( | |
fn=lambda: (gr.update(visible=False), gr.update(visible=True)), | |
outputs=[services_page, practice_page] | |
) | |
standards_service_btn.click( | |
fn=lambda: (gr.update(visible=False), gr.update(visible=True)), | |
outputs=[services_page, standards_page] | |
) | |
study_service_btn.click( | |
fn=lambda: (gr.update(visible=False), gr.update(visible=True)), | |
outputs=[services_page, study_page] | |
) | |
# Back to services navigation | |
back_services_chat.click( | |
fn=lambda: (gr.update(visible=True), gr.update(visible=False)), | |
outputs=[services_page, chatbot_page] | |
) | |
back_services_practice.click( | |
fn=lambda: (gr.update(visible=True), gr.update(visible=False)), | |
outputs=[services_page, practice_page] | |
) | |
back_services_standards.click( | |
fn=lambda: (gr.update(visible=True), gr.update(visible=False)), | |
outputs=[services_page, standards_page] | |
) | |
back_services_study.click( | |
fn=lambda: (gr.update(visible=True), gr.update(visible=False)), | |
outputs=[services_page, study_page] | |
) | |
# Logout handler | |
logout_btn.click( | |
fn=lambda: (gr.update(visible=True), gr.update(visible=False), None, None), | |
outputs=[cover_page, services_page, user_state, session_state] | |
) | |
# Chat functionality | |
new_chat_btn.click( | |
fn=handle_new_chat, | |
inputs=[user_state], | |
outputs=[session_state, chatbot] | |
) | |
submit_btn.click( | |
fn=handle_chat_message, | |
inputs=[msg, chatbot, user_state, session_state], | |
outputs=[chatbot, msg, diagram_display] | |
) | |
msg.submit( | |
fn=handle_chat_message, | |
inputs=[msg, chatbot, user_state, session_state], | |
outputs=[chatbot, msg, diagram_display] | |
) | |
clear_btn.click( | |
fn=clear_chat, | |
outputs=[chatbot, msg, diagram_display] | |
) | |
# Quick action buttons | |
quick_fault_btn.click( | |
fn=lambda h, u, s: handle_quick_action("fault", h, u, s), | |
inputs=[chatbot, user_state, session_state], | |
outputs=[chatbot, msg, diagram_display] | |
) | |
quick_protection_btn.click( | |
fn=lambda h, u, s: handle_quick_action("protection", h, u, s), | |
inputs=[chatbot, user_state, session_state], | |
outputs=[chatbot, msg, diagram_display] | |
) | |
quick_sld_btn.click( | |
fn=lambda h, u, s: handle_quick_action("sld", h, u, s), | |
inputs=[chatbot, user_state, session_state], | |
outputs=[chatbot, msg, diagram_display] | |
) | |
quick_protection_diagram_btn.click( | |
fn=lambda h, u, s: handle_quick_action("protection_diagram", h, u, s), | |
inputs=[chatbot, user_state, session_state], | |
outputs=[chatbot, msg, diagram_display] | |
) | |
# Practice generator functionality | |
generate_btn.click( | |
fn=handle_practice_generation, | |
inputs=[topic_input, difficulty_input, num_questions_input, include_solutions, user_state], | |
outputs=[practice_output] | |
) | |
# Standards explorer functionality | |
explain_btn.click( | |
fn=handle_standard_explanation, | |
inputs=[standard_input, detail_level, user_state], | |
outputs=[standard_output] | |
) | |
# Calculator functionality | |
calc_btn.click( | |
fn=calculate_power, | |
inputs=[calc_voltage, calc_current, calc_pf], | |
outputs=[calc_results] | |
) | |
fault_calc_btn.click( | |
fn=calculate_fault_current, | |
inputs=[fault_voltage, fault_mva, fault_z1, fault_z2, fault_z0], | |
outputs=[fault_results] | |
) | |
return app | |
# Launch the enhanced application | |
if __name__ == "__main__": | |
app = create_multipage_app() | |
# Enhanced startup information | |
print("⚡" * 80) | |
print("🚀 POWER SYSTEMS CONSULTANT v5.0 - PRODUCTION READY") | |
print("⚡" * 80) | |
print() | |
print("🔧 LATEST ENHANCEMENTS:") | |
print(" ✅ Enhanced error handling and fallback modes") | |
print(" ✅ Demo mode when GROQ_API_KEY not available") | |
print(" ✅ Improved diagram generation with multiple types") | |
print(" ✅ Advanced interactive calculators") | |
print(" ✅ Quick action buttons for common tasks") | |
print(" ✅ Enhanced practice generator with templates") | |
print(" ✅ Comprehensive standards library") | |
print(" ✅ Better accessibility and responsive design") | |
print(" ✅ Improved authentication with validation") | |
print(" ✅ Professional animations and transitions") | |
print() | |
print("🎨 MODERN INTERFACE:") | |
print(" ✨ Responsive multi-page design") | |
print(" 🔐 Secure user authentication system") | |
print(" 🎯 Interactive service dashboard") | |
print(" 💬 Enhanced chatbot with quick actions") | |
print(" 📚 Advanced practice generator") | |
print(" 📋 Comprehensive standards explorer") | |
print(" 🎓 Interactive study resources") | |
print(" 🧮 Multiple calculators (power, fault current)") | |
print() | |
print("🔧 CORE FEATURES:") | |
print(" 🤖 AI-powered consultant (Groq LLM)") | |
print(" 📊 Professional SVG diagrams") | |
print(" 🎯 Customizable practice questions") | |
print(" 📋 Standards explanations") | |
print(" 🧮 Engineering calculators") | |
print(" 💾 SQLite user database") | |
print(" 📜 Chat session management") | |
print(" 🛡️ Fallback modes for reliability") | |
print() | |
# System status | |
if consultant: | |
if consultant.groq_client: | |
print("🤖 GROQ API: ✅ Connected - Full AI functionality available") | |
else: | |
print("🤖 GROQ API: ⚠️ Demo mode - Set GROQ_API_KEY for full functionality") | |
if consultant.user_manager: | |
print("💾 Database: ✅ SQLite ready") | |
else: | |
print("💾 Database: ⚠️ Limited functionality") | |
if EXTERNAL_UTILS_AVAILABLE: | |
print("🔧 External Utils: ✅ Available") | |
if hasattr(consultant, 'has_rag') and consultant.has_rag: | |
print("📚 RAG System: ✅ Enhanced responses") | |
else: | |
print("📚 RAG System: ⚠️ Not available") | |
else: | |
print("🔧 External Utils: ⚠️ Using internal implementations") | |
else: | |
print("❌ SYSTEM ERROR: Consultant not initialized") | |
print() | |
print("🌐 ACCESS INFORMATION:") | |
print(" 📍 URL: http://localhost:7860") | |
print(" 🎯 Flow: Cover → Auth → Services → Tools") | |
print(" 💡 Demo: Works without API keys") | |
print() | |
print("🚀 PROFESSIONAL POWER SYSTEMS PLATFORM") | |
print("⚡" * 80) | |
try: | |
app.launch( | |
server_name="0.0.0.0", | |
server_port=7860, | |
share=False, | |
show_api=False, | |
show_error=True, | |
quiet=False, | |
favicon_path=None, | |
ssl_verify=False | |
) | |
except Exception as e: | |
print(f"❌ Launch Error: {str(e)}") | |
print("💡 Try different port or check system requirements") |