Spaces:
Runtime error
Runtime error
import 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 | |
from utils.diagram_generator import DiagramGenerator | |
from utils.rag_system import EnhancedRAGSystem | |
# Initialize components | |
def get_groq_client(): | |
api_key = os.getenv("GROQ_API_KEY") | |
if not api_key: | |
raise ValueError("GROQ_API_KEY environment variable not set") | |
return Groq(api_key=api_key) | |
class UserManager: | |
def __init__(self): | |
self.db_path = 'users.db' | |
self.init_database() | |
def init_database(self): | |
"""Initialize SQLite database for users""" | |
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() | |
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: | |
# Update last login | |
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: | |
return False, None | |
def get_user_sessions(self, user_id: int) -> List[Dict]: | |
"""Get chat sessions for user""" | |
try: | |
conn = sqlite3.connect(self.db_path) | |
cursor = conn.cursor() | |
cursor.execute( | |
"SELECT session_id, title, created_at FROM chat_sessions WHERE user_id = ? ORDER BY updated_at DESC", | |
(user_id,) | |
) | |
sessions = cursor.fetchall() | |
conn.close() | |
return [ | |
{ | |
'session_id': session[0], | |
'title': session[1] or f"Chat {session[0][:8]}", | |
'created_at': session[2] | |
} | |
for session in sessions | |
] | |
except Exception as e: | |
return [] | |
class PowerSystemsConsultant: | |
def __init__(self): | |
self.groq_client = get_groq_client() | |
self.rag_system = EnhancedRAGSystem() | |
self.diagram_generator = DiagramGenerator() | |
self.user_manager = UserManager() | |
self.current_user = None | |
self.current_session = None | |
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 RAG context""" | |
try: | |
# Check if query is asking for a diagram | |
diagram_type = self.detect_diagram_request(user_query) | |
diagram_svg = None | |
if diagram_type: | |
diagram_svg = self.generate_diagram(diagram_type, user_query) | |
# Retrieve relevant context | |
context = self.rag_system.retrieve_context(user_query) | |
# Prepare system prompt | |
system_prompt = f"""You are a Power Systems Mini-Consultant AI assistant specializing in electrical power systems. | |
You help with: | |
1. Fault analysis and calculations | |
2. Protection system design | |
3. Standards interpretation (IEEE, IEC, ANSI) | |
4. Study materials and exam preparation | |
5. Practice problem generation | |
6. Engineering diagrams and visualizations | |
Use the following context from the knowledge base: | |
{context} | |
Provide clear, technical explanations with practical examples. Include relevant formulas, standards references, and safety considerations when appropriate. | |
If a user asks for diagrams or visual representations, mention that you can generate professional engineering diagrams including: | |
- Single line diagrams | |
- Fault analysis diagrams | |
- Protection coordination curves | |
- Phasor diagrams | |
- Impedance diagrams | |
""" | |
# Prepare conversation context | |
messages = [{"role": "system", "content": system_prompt}] | |
# Add chat history | |
for human_msg, ai_msg in chat_history[-5:]: # Last 5 exchanges | |
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 | |
# Save to database if session exists | |
if session_id and self.current_user: | |
self.save_message(session_id, "user", user_query) | |
self.save_message(session_id, "assistant", text_response) | |
return text_response, diagram_svg | |
except Exception as e: | |
error_msg = f"Error generating response: {str(e)}. Please check your GROQ_API_KEY and try again." | |
return error_msg, None | |
def detect_diagram_request(self, query: str) -> Optional[str]: | |
"""Detect if user is requesting a diagram""" | |
query_lower = query.lower() | |
diagram_keywords = { | |
'single line': 'single_line', | |
'single-line': 'single_line', | |
'fault analysis': 'fault_analysis', | |
'fault diagram': 'fault_analysis', | |
'protection coordination': 'protection_coordination', | |
'coordination curve': 'protection_coordination', | |
'phasor diagram': 'phasor', | |
'phasor': 'phasor', | |
'impedance diagram': 'impedance', | |
'distance protection': 'impedance', | |
'r-x diagram': 'impedance' | |
} | |
for keyword, diagram_type in diagram_keywords.items(): | |
if keyword in query_lower: | |
return diagram_type | |
return None | |
def generate_diagram(self, diagram_type: str, query: str) -> str: | |
"""Generate appropriate diagram based on type""" | |
try: | |
if diagram_type == 'single_line': | |
return self.diagram_generator.generate_single_line_diagram({}) | |
elif diagram_type == 'fault_analysis': | |
fault_type = 'line_to_ground' | |
if 'line to line' in query.lower() or 'l-l' in query.lower(): | |
fault_type = 'line_to_line' | |
elif 'three phase' in query.lower() or '3-phase' in query.lower(): | |
fault_type = 'three_phase' | |
return self.diagram_generator.generate_fault_analysis_diagram(fault_type) | |
elif diagram_type == 'protection_coordination': | |
return self.diagram_generator.generate_protection_coordination_diagram() | |
elif diagram_type == 'phasor': | |
condition = 'balanced' | |
if 'unbalanced' in query.lower() or 'fault' in query.lower(): | |
condition = 'unbalanced' | |
return self.diagram_generator.generate_phasor_diagram(condition) | |
elif diagram_type == 'impedance': | |
return self.diagram_generator.generate_impedance_diagram() | |
except Exception as e: | |
return f"<text>Error generating diagram: {str(e)}</text>" | |
return None | |
def generate_practice_pack(self, topic: str, difficulty: str, num_questions: int) -> str: | |
"""Generate practice questions pack""" | |
try: | |
practice_prompt = f"""Generate {num_questions} power systems practice questions about {topic} at {difficulty} level. | |
Format the response with: | |
1. Question number and statement | |
2. Multiple choice options (A, B, C, D) when appropriate | |
3. Detailed solution with step-by-step calculations | |
4. Key concepts and formulas used | |
5. References to relevant standards | |
Focus on practical engineering scenarios and include numerical calculations where applicable.""" | |
messages = [ | |
{"role": "system", "content": "You are an expert power systems engineer creating practice questions for students and professionals."}, | |
{"role": "user", "content": practice_prompt} | |
] | |
response = self.groq_client.chat.completions.create( | |
model="llama3-70b-8192", | |
messages=messages, | |
max_tokens=3000, | |
temperature=0.7 | |
) | |
return response.choices[0].message.content | |
except Exception as e: | |
return f"Error generating practice pack: {str(e)}" | |
def explain_standard(self, standard: str) -> str: | |
"""Explain power systems standard""" | |
try: | |
standard_prompt = f"""Provide a comprehensive explanation of the {standard} standard including: | |
1. Purpose and scope of the standard | |
2. Key requirements and specifications | |
3. Practical applications in power systems | |
4. Important clauses and sections | |
5. Relationship to other standards | |
6. Implementation considerations | |
7. Common misconceptions or challenges | |
Make it practical and useful for power systems engineers.""" | |
messages = [ | |
{"role": "system", "content": "You are an expert in power systems standards and regulations with deep knowledge of IEEE, IEC, and ANSI 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 | |
) | |
return response.choices[0].message.content | |
except Exception as e: | |
return f"Error explaining standard: {str(e)}" | |
def create_chat_session(self, user_id: int, title: str = None) -> str: | |
"""Create new chat session""" | |
session_id = str(uuid.uuid4()) | |
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: | |
return str(uuid.uuid4()) # Fallback to temporary session | |
def save_message(self, session_id: str, role: str, content: str): | |
"""Save message to database""" | |
try: | |
conn = sqlite3.connect(self.user_manager.db_path) | |
cursor = conn.cursor() | |
cursor.execute( | |
"INSERT INTO messages (session_id, role, content) VALUES (?, ?, ?)", | |
(session_id, role, content) | |
) | |
# Update session timestamp | |
cursor.execute( | |
"UPDATE chat_sessions SET updated_at = CURRENT_TIMESTAMP WHERE session_id = ?", | |
(session_id,) | |
) | |
conn.commit() | |
conn.close() | |
except Exception as e: | |
pass # Continue without saving if database error | |
def load_chat_history(self, session_id: str) -> List[Tuple[str, str]]: | |
"""Load chat history from database""" | |
try: | |
conn = sqlite3.connect(self.user_manager.db_path) | |
cursor = conn.cursor() | |
cursor.execute( | |
"SELECT role, content FROM messages WHERE session_id = ? ORDER BY timestamp", | |
(session_id,) | |
) | |
messages = cursor.fetchall() | |
conn.close() | |
# Convert to chat history format | |
history = [] | |
current_pair = [None, None] | |
for role, content in messages: | |
if role == "user": | |
if current_pair[0] is not None: | |
history.append((current_pair[0], current_pair[1] or "")) | |
current_pair = [content, None] | |
elif role == "assistant": | |
current_pair[1] = content | |
# Add the last pair if complete | |
if current_pair[0] is not None: | |
history.append((current_pair[0], current_pair[1] or "")) | |
return history | |
except Exception as e: | |
return [] | |
# Initialize the consultant | |
try: | |
consultant = PowerSystemsConsultant() | |
initialization_status = "✅ Power Systems Consultant initialized successfully!" | |
except Exception as e: | |
consultant = None | |
initialization_status = f"❌ Initialization failed: {str(e)}" | |
# Global state management | |
current_user = gr.State(None) | |
current_session = gr.State(None) | |
chat_sessions = gr.State([]) | |
# UI Functions | |
def login_user(username, password): | |
"""Handle user login""" | |
if not consultant: | |
return False, None, "System not initialized", gr.update(), gr.update() | |
success, user_data = consultant.user_manager.authenticate_user(username, password) | |
if success: | |
consultant.current_user = user_data | |
sessions = consultant.user_manager.get_user_sessions(user_data['id']) | |
# Create session list for sidebar | |
session_choices = [(f"{s['title']} ({s['created_at'][:10]})", s['session_id']) for s in sessions] | |
return ( | |
True, | |
user_data, | |
f"Welcome back, {user_data['full_name']}!", | |
gr.update(choices=session_choices, value=session_choices[0][1] if session_choices else None), | |
gr.update(visible=True) | |
) | |
else: | |
return False, None, "Invalid username or password", gr.update(), gr.update() | |
def register_user(username, email, password, confirm_password, full_name): | |
"""Handle user registration""" | |
if not consultant: | |
return False, "System not initialized" | |
if password != confirm_password: | |
return False, "Passwords do not match" | |
if len(password) < 6: | |
return False, "Password must be at least 6 characters" | |
success, message = consultant.user_manager.create_user(username, email, password, full_name) | |
return success, message | |
def start_new_chat(user_data): | |
"""Start a new chat session""" | |
if not consultant or not user_data: | |
return None, [] | |
session_id = consultant.create_chat_session(user_data['id']) | |
consultant.current_session = session_id | |
# Update sessions list | |
sessions = consultant.user_manager.get_user_sessions(user_data['id']) | |
session_choices = [(f"{s['title']} ({s['created_at'][:10]})", s['session_id']) for s in sessions] | |
return session_id, session_choices | |
def load_selected_chat(session_id, user_data): | |
"""Load selected chat session""" | |
if not consultant or not session_id: | |
return [] | |
consultant.current_session = session_id | |
history = consultant.load_chat_history(session_id) | |
return history | |
def chat_interface(message, history, user_data, session_id): | |
"""Main chat interface function""" | |
if not consultant: | |
return history + [["", "❌ System not initialized. Please check GROQ_API_KEY."]], "", None | |
if not user_data: | |
return history + [["", "Please log in to continue."]], "", None | |
if not message.strip(): | |
return history + [[message, "Please enter a question about power systems."]], "", None | |
try: | |
# Generate response and potential diagram | |
response, diagram_svg = consultant.generate_response(message, history, session_id) | |
# Update history | |
new_history = history + [[message, response]] | |
return new_history, "", diagram_svg | |
except Exception as e: | |
return history + [[message, f"Error: {str(e)}"]], "", None | |
def generate_practice_questions(topic, difficulty, num_questions, user_data): | |
"""Generate practice pack interface""" | |
if not consultant: | |
return "❌ System not initialized. Please check GROQ_API_KEY." | |
if not user_data: | |
return "Please log in to access this feature." | |
try: | |
practice_pack = consultant.generate_practice_pack(topic, difficulty, int(num_questions)) | |
return practice_pack | |
except Exception as e: | |
return f"Error generating practice pack: {str(e)}" | |
def explain_standard_interface(standard, user_data): | |
"""Standards explanation interface""" | |
if not consultant: | |
return "❌ System not initialized. Please check GROQ_API_KEY." | |
if not user_data: | |
return "Please log in to access this feature." | |
try: | |
explanation = consultant.explain_standard(standard) | |
return explanation | |
except Exception as e: | |
return f"Error explaining standard: {str(e)}" | |
# Create the main application interface with modern design | |
def create_app(): | |
with gr.Blocks( | |
theme=gr.themes.Monochrome( | |
primary_hue=gr.themes.colors.blue, | |
secondary_hue=gr.themes.colors.cyan, | |
neutral_hue=gr.themes.colors.slate, | |
), | |
title="⚡ Power Systems Consultant", | |
css=""" | |
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap'); | |
@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css'); | |
* { | |
font-family: 'Inter', system-ui, -apple-system, sans-serif; | |
box-sizing: border-box; | |
} | |
/* Dark theme variables */ | |
:root { | |
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
--secondary-gradient: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); | |
--success-gradient: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); | |
--warning-gradient: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); | |
--danger-gradient: linear-gradient(135deg, #fa709a 0%, #fee140 100%); | |
--dark-bg: #0f0f23; | |
--card-bg: rgba(255, 255, 255, 0.05); | |
--glass-bg: rgba(255, 255, 255, 0.1); | |
--text-primary: #ffffff; | |
--text-secondary: rgba(255, 255, 255, 0.7); | |
--border-color: rgba(255, 255, 255, 0.1); | |
--shadow-lg: 0 25px 50px -12px rgba(0, 0, 0, 0.25); | |
--shadow-xl: 0 35px 60px -12px rgba(0, 0, 0, 0.35); | |
} | |
/* Root styling */ | |
.gradio-container { | |
background: var(--dark-bg); | |
color: var(--text-primary); | |
min-height: 100vh; | |
} | |
/* Animated header */ | |
.hero-header { | |
background: var(--primary-gradient); | |
padding: 4rem 2rem; | |
border-radius: 25px; | |
margin: 2rem 0; | |
text-align: center; | |
position: relative; | |
overflow: hidden; | |
box-shadow: var(--shadow-xl); | |
animation: slideInDown 0.8s ease-out; | |
} | |
.hero-header::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
background: linear-gradient(45deg, rgba(255,255,255,0.1) 0%, transparent 50%, rgba(255,255,255,0.1) 100%); | |
animation: shimmer 3s ease-in-out infinite; | |
} | |
.hero-title { | |
font-size: 3.5rem; | |
font-weight: 800; | |
margin: 0; | |
background: linear-gradient(45deg, #ffffff, #f0f9ff); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
text-shadow: 0 4px 8px rgba(0,0,0,0.3); | |
position: relative; | |
z-index: 1; | |
} | |
.hero-subtitle { | |
font-size: 1.25rem; | |
margin: 1rem 0 0 0; | |
color: rgba(255,255,255,0.9); | |
font-weight: 400; | |
position: relative; | |
z-index: 1; | |
} | |
.hero-icon { | |
font-size: 4rem; | |
margin-bottom: 1rem; | |
color: #fbbf24; | |
animation: pulse 2s ease-in-out infinite; | |
position: relative; | |
z-index: 1; | |
} | |
/* Status cards */ | |
.status-card { | |
background: var(--glass-bg); | |
backdrop-filter: blur(20px); | |
border: 1px solid var(--border-color); | |
border-radius: 20px; | |
padding: 2rem; | |
margin: 2rem 0; | |
box-shadow: var(--shadow-lg); | |
position: relative; | |
overflow: hidden; | |
animation: fadeInUp 0.6s ease-out; | |
} | |
.status-success { | |
background: linear-gradient(135deg, rgba(34, 197, 94, 0.1), rgba(34, 197, 94, 0.05)); | |
border-color: rgba(34, 197, 94, 0.3); | |
} | |
.status-error { | |
background: linear-gradient(135deg, rgba(239, 68, 68, 0.1), rgba(239, 68, 68, 0.05)); | |
border-color: rgba(239, 68, 68, 0.3); | |
} | |
/* Modern login/register cards */ | |
.auth-container { | |
background: var(--glass-bg); | |
backdrop-filter: blur(20px); | |
border: 1px solid var(--border-color); | |
border-radius: 25px; | |
padding: 3rem; | |
max-width: 500px; | |
margin: 2rem auto; | |
box-shadow: var(--shadow-xl); | |
position: relative; | |
overflow: hidden; | |
animation: zoomIn 0.5s ease-out; | |
} | |
.auth-container::before { | |
content: ''; | |
position: absolute; | |
top: -2px; | |
left: -2px; | |
right: -2px; | |
bottom: -2px; | |
background: var(--primary-gradient); | |
border-radius: 25px; | |
z-index: -1; | |
opacity: 0.7; | |
} | |
.auth-title { | |
text-align: center; | |
font-size: 2rem; | |
font-weight: 700; | |
margin-bottom: 2rem; | |
background: var(--primary-gradient); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
} | |
/* Enhanced input fields */ | |
.gradio-textbox, .gradio-dropdown { | |
background: rgba(255, 255, 255, 0.05) !important; | |
border: 2px solid rgba(255, 255, 255, 0.1) !important; | |
border-radius: 15px !important; | |
padding: 1rem !important; | |
color: var(--text-primary) !important; | |
font-size: 1rem !important; | |
transition: all 0.3s ease !important; | |
backdrop-filter: blur(10px) !important; | |
} | |
.gradio-textbox:focus, .gradio-dropdown:focus { | |
border-color: #667eea !important; | |
box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.2) !important; | |
transform: translateY(-2px) !important; | |
} | |
/* Modern buttons */ | |
.gradio-button { | |
background: var(--primary-gradient) !important; | |
border: none !important; | |
border-radius: 15px !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; | |
box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3) !important; | |
position: relative !important; | |
overflow: hidden !important; | |
} | |
.gradio-button:hover { | |
transform: translateY(-3px) !important; | |
box-shadow: 0 15px 35px rgba(102, 126, 234, 0.4) !important; | |
} | |
.gradio-button:active { | |
transform: translateY(-1px) !important; | |
} | |
.gradio-button::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: -100%; | |
width: 100%; | |
height: 100%; | |
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); | |
transition: left 0.5s; | |
} | |
.gradio-button:hover::before { | |
left: 100%; | |
} | |
/* Feature cards */ | |
.feature-card { | |
background: var(--glass-bg); | |
backdrop-filter: blur(20px); | |
border: 1px solid var(--border-color); | |
border-radius: 25px; | |
padding: 2.5rem; | |
margin: 1.5rem 0; | |
box-shadow: var(--shadow-lg); | |
transition: all 0.4s ease; | |
position: relative; | |
overflow: hidden; | |
animation: fadeInUp 0.8s ease-out; | |
} | |
.feature-card:hover { | |
transform: translateY(-8px); | |
box-shadow: var(--shadow-xl); | |
} | |
.feature-card::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
height: 4px; | |
background: var(--primary-gradient); | |
border-radius: 25px 25px 0 0; | |
} | |
.feature-title { | |
font-size: 1.5rem; | |
font-weight: 700; | |
margin-bottom: 1rem; | |
background: var(--primary-gradient); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
display: flex; | |
align-items: center; | |
gap: 0.75rem; | |
} | |
.feature-description { | |
color: var(--text-secondary); | |
font-size: 1.1rem; | |
line-height: 1.6; | |
} | |
/* Modern sidebar */ | |
.modern-sidebar { | |
background: var(--glass-bg); | |
backdrop-filter: blur(20px); | |
border: 1px solid var(--border-color); | |
border-radius: 25px; | |
padding: 2rem; | |
margin: 1rem; | |
min-height: 700px; | |
box-shadow: var(--shadow-lg); | |
position: relative; | |
overflow: hidden; | |
} | |
.sidebar-header { | |
text-align: center; | |
margin-bottom: 2rem; | |
padding-bottom: 1rem; | |
border-bottom: 1px solid var(--border-color); | |
} | |
.sidebar-title { | |
font-size: 1.25rem; | |
font-weight: 700; | |
background: var(--primary-gradient); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
gap: 0.5rem; | |
} | |
/* Chat interface styling */ | |
.chat-container { | |
background: var(--glass-bg); | |
backdrop-filter: blur(20px); | |
border: 1px solid var(--border-color); | |
border-radius: 25px; | |
overflow: hidden; | |
box-shadow: var(--shadow-lg); | |
margin: 1rem; | |
min-height: 600px; | |
} | |
.chat-header { | |
background: var(--primary-gradient); | |
padding: 1.5rem; | |
text-align: center; | |
color: white; | |
font-weight: 600; | |
font-size: 1.1rem; | |
position: relative; | |
} | |
.chat-header::after { | |
content: ''; | |
position: absolute; | |
bottom: 0; | |
left: 50%; | |
transform: translateX(-50%); | |
width: 60px; | |
height: 3px; | |
background: rgba(255,255,255,0.5); | |
border-radius: 3px; | |
} | |
/* Chatbot messages */ | |
.message-wrap { | |
padding: 1rem; | |
margin: 0.5rem; | |
border-radius: 20px; | |
animation: messageSlide 0.4s ease-out; | |
backdrop-filter: blur(10px); | |
border: 1px solid rgba(255,255,255,0.1); | |
} | |
.user-message { | |
background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(118, 75, 162, 0.2)); | |
margin-left: 2rem; | |
text-align: right; | |
} | |
.bot-message { | |
background: linear-gradient(135deg, rgba(34, 197, 94, 0.1), rgba(34, 197, 94, 0.05)); | |
margin-right: 2rem; | |
} | |
/* Diagram container */ | |
.diagram-container { | |
background: var(--glass-bg); | |
backdrop-filter: blur(20px); | |
border: 2px solid var(--border-color); | |
border-radius: 20px; | |
padding: 2rem; | |
margin: 2rem 0; | |
text-align: center; | |
box-shadow: var(--shadow-lg); | |
transition: all 0.3s ease; | |
} | |
.diagram-container:hover { | |
transform: scale(1.02); | |
box-shadow: var(--shadow-xl); | |
} | |
.diagram-placeholder { | |
color: var(--text-secondary); | |
font-style: italic; | |
padding: 3rem; | |
border: 2px dashed var(--border-color); | |
border-radius: 15px; | |
background: rgba(255,255,255,0.02); | |
} | |
/* Tab styling */ | |
.gradio-tabs { | |
background: transparent; | |
} | |
.gradio-tabitem { | |
background: var(--glass-bg); | |
backdrop-filter: blur(20px); | |
border: 1px solid var(--border-color); | |
border-radius: 20px; | |
padding: 2rem; | |
margin-top: 1rem; | |
box-shadow: var(--shadow-lg); | |
} | |
/* Progress and loading animations */ | |
.loading-spinner { | |
display: inline-block; | |
width: 20px; | |
height: 20px; | |
border: 3px solid rgba(255,255,255,0.3); | |
border-radius: 50%; | |
border-top-color: #667eea; | |
animation: spin 1s ease-in-out infinite; | |
} | |
/* Accordions */ | |
.gradio-accordion { | |
background: var(--glass-bg) !important; | |
border: 1px solid var(--border-color) !important; | |
border-radius: 20px !important; | |
margin: 1rem 0 !important; | |
overflow: hidden !important; | |
box-shadow: var(--shadow-lg) !important; | |
backdrop-filter: blur(20px) !important; | |
} | |
.formula-box { | |
background: rgba(102, 126, 234, 0.1); | |
border: 1px solid rgba(102, 126, 234, 0.3); | |
border-radius: 15px; | |
padding: 2rem; | |
margin: 1.5rem 0; | |
font-family: 'Courier New', monospace; | |
position: relative; | |
overflow: hidden; | |
} | |
.formula-box::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
height: 3px; | |
background: var(--primary-gradient); | |
} | |
.formula-title { | |
font-size: 1.2rem; | |
font-weight: 700; | |
color: #667eea; | |
margin-bottom: 1rem; | |
display: flex; | |
align-items: center; | |
gap: 0.5rem; | |
} | |
.formula-code { | |
background: rgba(0,0,0,0.3); | |
border-radius: 8px; | |
padding: 0.75rem 1rem; | |
font-family: 'Courier New', monospace; | |
color: #fbbf24; | |
border-left: 4px solid #667eea; | |
margin: 0.5rem 0; | |
display: block; | |
overflow-x: auto; | |
} | |
/* Animations */ | |
@keyframes slideInDown { | |
from { transform: translateY(-50px); opacity: 0; } | |
to { transform: translateY(0); opacity: 1; } | |
} | |
@keyframes fadeInUp { | |
from { transform: translateY(30px); opacity: 0; } | |
to { transform: translateY(0); opacity: 1; } | |
} | |
@keyframes zoomIn { | |
from { transform: scale(0.9); opacity: 0; } | |
to { transform: scale(1); opacity: 1; } | |
} | |
@keyframes shimmer { | |
0% { transform: translateX(-100%); } | |
100% { transform: translateX(100%); } | |
} | |
@keyframes pulse { | |
0%, 100% { opacity: 1; transform: scale(1); } | |
50% { opacity: 0.8; transform: scale(1.05); } | |
} | |
@keyframes spin { | |
to { transform: rotate(360deg); } | |
} | |
@keyframes messageSlide { | |
from { transform: translateX(-20px); opacity: 0; } | |
to { transform: translateX(0); opacity: 1; } | |
} | |
/* Responsive design */ | |
@media (max-width: 768px) { | |
.hero-title { font-size: 2.5rem; } | |
.hero-subtitle { font-size: 1rem; } | |
.auth-container { margin: 1rem; padding: 2rem; } | |
.feature-card { padding: 1.5rem; margin: 1rem 0; } | |
.modern-sidebar { margin: 0.5rem; padding: 1rem; } | |
.chat-container { margin: 0.5rem; } | |
} | |
/* Scrollbar styling */ | |
::-webkit-scrollbar { | |
width: 8px; | |
} | |
::-webkit-scrollbar-track { | |
background: rgba(255,255,255,0.1); | |
border-radius: 4px; | |
} | |
::-webkit-scrollbar-thumb { | |
background: var(--primary-gradient); | |
border-radius: 4px; | |
} | |
::-webkit-scrollbar-thumb:hover { | |
background: linear-gradient(135deg, #5a67d8, #6b46c1); | |
} | |
/* Enhanced interactivity */ | |
.interactive-card { | |
cursor: pointer; | |
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
position: relative; | |
} | |
.interactive-card:hover { | |
transform: translateY(-5px) scale(1.02); | |
box-shadow: 0 25px 50px rgba(102, 126, 234, 0.25); | |
} | |
.interactive-card::after { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
border-radius: inherit; | |
background: linear-gradient(45deg, transparent, rgba(255,255,255,0.05), transparent); | |
opacity: 0; | |
transition: opacity 0.3s ease; | |
} | |
.interactive-card:hover::after { | |
opacity: 1; | |
} | |
/* Glowing effects */ | |
.glow-effect { | |
position: relative; | |
overflow: hidden; | |
} | |
.glow-effect::before { | |
content: ''; | |
position: absolute; | |
top: -2px; | |
left: -2px; | |
right: -2px; | |
bottom: -2px; | |
background: var(--primary-gradient); | |
border-radius: inherit; | |
z-index: -1; | |
filter: blur(8px); | |
opacity: 0.7; | |
animation: glow 2s ease-in-out infinite alternate; | |
} | |
@keyframes glow { | |
from { filter: blur(8px) brightness(1); } | |
to { filter: blur(12px) brightness(1.2); } | |
} | |
/* Particle background effect */ | |
.particle-bg { | |
position: relative; | |
overflow: hidden; | |
} | |
.particle-bg::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background-image: | |
radial-gradient(2px 2px at 20% 30%, rgba(102, 126, 234, 0.3), transparent), | |
radial-gradient(2px 2px at 40% 70%, rgba(118, 75, 162, 0.3), transparent), | |
radial-gradient(1px 1px at 90% 40%, rgba(240, 171, 252, 0.3), transparent), | |
radial-gradient(1px 1px at 50% 50%, rgba(34, 197, 94, 0.2), transparent); | |
background-repeat: repeat; | |
background-size: 100px 100px; | |
animation: float 20s ease-in-out infinite; | |
pointer-events: none; | |
} | |
@keyframes float { | |
0%, 100% { transform: translateY(0px); } | |
50% { transform: translateY(-20px); } | |
} | |
/* Status indicators */ | |
.status-indicator { | |
display: inline-flex; | |
align-items: center; | |
gap: 0.5rem; | |
padding: 0.5rem 1rem; | |
border-radius: 50px; | |
font-size: 0.9rem; | |
font-weight: 600; | |
text-transform: uppercase; | |
letter-spacing: 0.5px; | |
} | |
.status-online { | |
background: linear-gradient(135deg, rgba(34, 197, 94, 0.2), rgba(34, 197, 94, 0.1)); | |
color: #22c55e; | |
border: 1px solid rgba(34, 197, 94, 0.3); | |
} | |
.status-offline { | |
background: linear-gradient(135deg, rgba(239, 68, 68, 0.2), rgba(239, 68, 68, 0.1)); | |
color: #ef4444; | |
border: 1px solid rgba(239, 68, 68, 0.3); | |
} | |
.status-indicator::before { | |
content: ''; | |
width: 8px; | |
height: 8px; | |
border-radius: 50%; | |
background: currentColor; | |
animation: pulse 2s ease-in-out infinite; | |
} | |
""" | |
) as app: | |
# State management | |
user_state = gr.State(None) | |
session_state = gr.State(None) | |
sessions_state = gr.State([]) | |
# Modern Hero Header | |
gr.HTML(""" | |
<div class="hero-header particle-bg"> | |
<div class="hero-icon"> | |
<i class="fas fa-bolt"></i> | |
</div> | |
<h1 class="hero-title">Power Systems Consultant</h1> | |
<p class="hero-subtitle">Advanced AI Assistant for Fault Analysis, Protection Systems & Engineering Excellence</p> | |
</div> | |
""") | |
# Dynamic Status Display | |
status_class = "status-online" if "✅" in initialization_status else "status-offline" | |
status_icon = "fas fa-check-circle" if "✅" in initialization_status else "fas fa-exclamation-triangle" | |
gr.HTML(f""" | |
<div class="status-card {'status-success' if '✅' in initialization_status else 'status-error'}"> | |
<div class="status-indicator {status_class}"> | |
<i class="{status_icon}"></i> | |
<span>System Status</span> | |
</div> | |
<p style="margin: 1rem 0 0 0; font-size: 1.1rem;">{initialization_status}</p> | |
</div> | |
""") | |
# Authentication Section with Modern Design | |
with gr.Row(): | |
with gr.Column(visible=True) as login_section: | |
gr.HTML(""" | |
<div style="text-align: center; margin: 3rem 0;"> | |
<i class="fas fa-user-shield" style="font-size: 3rem; color: #667eea; margin-bottom: 1rem;"></i> | |
<h2 style="font-size: 2.5rem; font-weight: 800; background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin: 0;">Secure Access Portal</h2> | |
<p style="color: var(--text-secondary); font-size: 1.1rem; margin-top: 1rem;">Sign in to access your personalized Power Systems workspace</p> | |
</div> | |
""") | |
with gr.Tabs(): | |
with gr.TabItem("🔑 Sign In"): | |
with gr.Column(elem_classes=["auth-container"]): | |
gr.HTML('<div class="auth-title"><i class="fas fa-sign-in-alt"></i> Welcome Back</div>') | |
login_username = gr.Textbox( | |
label="👤 Username", | |
placeholder="Enter your username", | |
elem_classes=["modern-input"] | |
) | |
login_password = gr.Textbox( | |
label="🔒 Password", | |
type="password", | |
placeholder="Enter your password", | |
elem_classes=["modern-input"] | |
) | |
login_btn = gr.Button( | |
"🚀 Sign In", | |
variant="primary", | |
size="lg", | |
elem_classes=["glow-effect"] | |
) | |
login_status = gr.Textbox( | |
label="📊 Status", | |
interactive=False, | |
elem_classes=["status-display"] | |
) | |
with gr.TabItem("📝 Create Account"): | |
with gr.Column(elem_classes=["auth-container"]): | |
gr.HTML('<div class="auth-title"><i class="fas fa-user-plus"></i> Join Our Community</div>') | |
reg_username = gr.Textbox( | |
label="👤 Username", | |
placeholder="Choose a unique username", | |
elem_classes=["modern-input"] | |
) | |
reg_email = gr.Textbox( | |
label="📧 Email", | |
placeholder="[email protected]", | |
elem_classes=["modern-input"] | |
) | |
reg_full_name = gr.Textbox( | |
label="🏷️ Full Name", | |
placeholder="Your full name", | |
elem_classes=["modern-input"] | |
) | |
reg_password = gr.Textbox( | |
label="🔒 Password", | |
type="password", | |
placeholder="Create a strong password (min 6 chars)", | |
elem_classes=["modern-input"] | |
) | |
reg_confirm_password = gr.Textbox( | |
label="🔐 Confirm Password", | |
type="password", | |
placeholder="Confirm your password", | |
elem_classes=["modern-input"] | |
) | |
register_btn = gr.Button( | |
"✨ Create Account", | |
variant="primary", | |
size="lg", | |
elem_classes=["glow-effect"] | |
) | |
register_status = gr.Textbox( | |
label="📊 Status", | |
interactive=False, | |
elem_classes=["status-display"] | |
) | |
# Main Application Interface (hidden initially) | |
with gr.Column(visible=False) as main_section: | |
# Dynamic User Welcome | |
user_welcome = gr.HTML() | |
with gr.Tabs(): | |
# AI Consultant Chat Tab | |
with gr.TabItem("🤖 AI Consultant"): | |
with gr.Row(): | |
# Modern Sidebar | |
with gr.Column(scale=1, elem_classes=["modern-sidebar"]): | |
gr.HTML(""" | |
<div class="sidebar-header"> | |
<div class="sidebar-title"> | |
<i class="fas fa-history"></i> | |
Chat Sessions | |
</div> | |
</div> | |
""") | |
new_chat_btn = gr.Button( | |
"➕ New Conversation", | |
variant="primary", | |
size="sm", | |
elem_classes=["interactive-card"] | |
) | |
chat_sessions_dropdown = gr.Dropdown( | |
label="📜 Previous Chats", | |
choices=[], | |
value=None, | |
interactive=True, | |
elem_classes=["modern-dropdown"] | |
) | |
# Quick Actions | |
gr.HTML(""" | |
<div style="margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--border-color);"> | |
<h4 style="color: var(--text-primary); margin-bottom: 1rem;"> | |
<i class="fas fa-rocket"></i> Quick Actions | |
</h4> | |
<div style="display: flex; flex-direction: column; gap: 0.5rem;"> | |
<button class="quick-action-btn" onclick="document.querySelector('textarea').value='Explain three-phase fault analysis'; document.querySelector('textarea').dispatchEvent(new Event('input', {bubbles: true}));"> | |
<i class="fas fa-zap"></i> Fault Analysis | |
</button> | |
<button class="quick-action-btn" onclick="document.querySelector('textarea').value='Generate a single line diagram'; document.querySelector('textarea').dispatchEvent(new Event('input', {bubbles: true}));"> | |
<i class="fas fa-sitemap"></i> Create Diagram | |
</button> | |
<button class="quick-action-btn" onclick="document.querySelector('textarea').value='Explain IEEE C37.2 standard'; document.querySelector('textarea').dispatchEvent(new Event('input', {bubbles: true}));"> | |
<i class="fas fa-book"></i> Standards Help | |
</button> | |
</div> | |
</div> | |
<style> | |
.quick-action-btn { | |
background: rgba(102, 126, 234, 0.1); | |
border: 1px solid rgba(102, 126, 234, 0.3); | |
border-radius: 10px; | |
padding: 0.75rem; | |
color: var(--text-primary); | |
cursor: pointer; | |
transition: all 0.3s ease; | |
text-align: left; | |
font-size: 0.9rem; | |
} | |
.quick-action-btn:hover { | |
background: rgba(102, 126, 234, 0.2); | |
transform: translateX(5px); | |
} | |
.quick-action-btn i { | |
margin-right: 0.5rem; | |
color: #667eea; | |
} | |
</style> | |
""") | |
# Main Chat Interface | |
with gr.Column(scale=3): | |
gr.HTML(""" | |
<div class="feature-card interactive-card"> | |
<div class="feature-title"> | |
<i class="fas fa-robot"></i> | |
Intelligent Power Systems Consultant | |
</div> | |
<div class="feature-description"> | |
Your AI-powered companion for fault analysis, protection systems, standards interpretation, and engineering calculations. Ask complex questions and get expert-level responses with diagrams and detailed explanations. | |
</div> | |
</div> | |
""") | |
# Enhanced Chat Interface | |
with gr.Column(elem_classes=["chat-container"]): | |
gr.HTML(""" | |
<div class="chat-header"> | |
<i class="fas fa-comments"></i> | |
Power Systems AI Assistant | |
</div> | |
""") | |
chatbot = gr.Chatbot( | |
height=500, | |
placeholder="🤖 Your AI Power Systems Consultant is ready to help...", | |
show_label=False, | |
elem_classes=["modern-chatbot"], | |
avatar_images=("🧑💼", "🤖") | |
) | |
with gr.Row(): | |
msg = gr.Textbox( | |
placeholder="💬 Ask about faults, protection, standards, calculations, or request engineering diagrams...", | |
show_label=False, | |
container=False, | |
scale=4, | |
elem_classes=["chat-input"] | |
) | |
with gr.Row(): | |
submit_btn = gr.Button( | |
"🚀 Send", | |
variant="primary", | |
scale=1, | |
elem_classes=["glow-effect"] | |
) | |
clear_btn = gr.Button( | |
"🗑️ Clear", | |
variant="secondary", | |
scale=1 | |
) | |
# Enhanced Diagram Display | |
gr.HTML(""" | |
<div class="feature-card"> | |
<div class="feature-title"> | |
<i class="fas fa-chart-line"></i> | |
Generated Engineering Diagrams | |
</div> | |
</div> | |
""") | |
diagram_display = gr.HTML( | |
value=""" | |
<div class="diagram-container"> | |
<div class="diagram-placeholder"> | |
<i class="fas fa-drafting-compass" style="font-size: 3rem; color: var(--text-secondary); margin-bottom: 1rem;"></i> | |
<p>🎨 Professional engineering diagrams will appear here when you request them</p> | |
<p style="margin-top: 1rem; font-size: 0.9rem; opacity: 0.7;">Try asking for: "Generate a single line diagram" or "Show me a phasor diagram"</p> | |
</div> | |
</div> | |
""", | |
elem_classes=["modern-diagram-display"] | |
) | |
# Practice Pack Generator Tab | |
with gr.TabItem("📚 Practice Generator"): | |
gr.HTML(""" | |
<div class="feature-card interactive-card particle-bg"> | |
<div class="feature-title"> | |
<i class="fas fa-dumbbell"></i> | |
AI-Generated Practice Questions | |
</div> | |
<div class="feature-description"> | |
Generate customized practice packs with varying difficulty levels, complete with detailed solutions, step-by-step calculations, and standards references. Perfect for exam preparation and skill development. | |
</div> | |
</div> | |
""") | |
with gr.Row(): | |
with gr.Column(scale=1): | |
gr.HTML(""" | |
<div class="feature-card"> | |
<div class="feature-title"> | |
<i class="fas fa-sliders-h"></i> | |
Customize Your Practice Pack | |
</div> | |
</div> | |
""") | |
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" | |
], | |
label="📋 Select Topic", | |
value="🔥 Fault Analysis", | |
elem_classes=["modern-dropdown"] | |
) | |
difficulty_input = gr.Radio( | |
choices=["🟢 Beginner", "🟡 Intermediate", "🔴 Advanced"], | |
label="🎯 Difficulty Level", | |
value="🟡 Intermediate", | |
elem_classes=["modern-radio"] | |
) | |
num_questions_input = gr.Slider( | |
minimum=5, | |
maximum=20, | |
step=1, | |
value=10, | |
label="🔢 Number of Questions", | |
elem_classes=["modern-slider"] | |
) | |
generate_btn = gr.Button( | |
"🎯 Generate Practice Pack", | |
variant="primary", | |
elem_classes=["glow-effect", "interactive-card"] | |
) | |
with gr.Column(scale=2): | |
practice_output = gr.Textbox( | |
label="📖 Generated Practice Pack", | |
lines=20, | |
placeholder="🚀 Your customized practice questions will appear here with detailed solutions and explanations...", | |
elem_classes=["modern-output"] | |
) | |
# Standards Explorer Tab | |
with gr.TabItem("📋 Standards Explorer"): | |
gr.HTML(""" | |
<div class="feature-card interactive-card particle-bg"> | |
<div class="feature-title"> | |
<i class="fas fa-university"></i> | |
Power Systems Standards Library | |
</div> | |
<div class="feature-description"> | |
Comprehensive explanations of IEEE, IEC, and international standards with practical applications, implementation guidelines, and real-world examples from industry experts. | |
</div> | |
</div> | |
""") | |
with gr.Row(): | |
with gr.Column(scale=1): | |
gr.HTML(""" | |
<div class="feature-card"> | |
<div class="feature-title"> | |
<i class="fas fa-search"></i> | |
Explore Standards | |
</div> | |
</div> | |
""") | |
standard_input = gr.Dropdown( | |
choices=[ | |
"📜 IEEE C37.2 - Device Function Numbers", | |
"🔌 IEEE 1547 - Distributed Generation", | |
"🌊 IEEE 519 - Harmonic Control", | |
"🔧 IEEE C57.12.00 - Transformers", | |
"🏭 IEC 61850 - Substation Automation", | |
"⚡ IEC 60909 - Short-Circuit Calculations", | |
"🔄 IEC 61131 - PLC Programming", | |
"🛡️ IEC 60255 - Electrical Relays", | |
"⚖️ ANSI C84.1 - Voltage Ratings", | |
"📊 IEEE 242 - Protection & Coordination", | |
"🏭 IEEE 399 - Industrial Power Systems", | |
"🔧 IEEE C37.010 - Application Guide" | |
], | |
label="📑 Select Standard", | |
value="📜 IEEE C37.2 - Device Function Numbers", | |
elem_classes=["modern-dropdown"] | |
) | |
explain_btn = gr.Button( | |
"📖 Explain Standard", | |
variant="primary", | |
elem_classes=["glow-effect", "interactive-card"] | |
) | |
with gr.Column(scale=2): | |
standard_output = gr.Textbox( | |
label="📋 Standard Explanation", | |
lines=15, | |
placeholder="📚 Detailed standard explanation with practical applications will appear here...", | |
elem_classes=["modern-output"] | |
) | |
# Enhanced Study Resources Tab | |
with gr.TabItem("📚 Study Resources"): | |
gr.HTML(""" | |
<div class="feature-card interactive-card particle-bg"> | |
<div class="feature-title"> | |
<i class="fas fa-graduation-cap"></i> | |
Comprehensive Study Guide | |
</div> | |
<div class="feature-description"> | |
Essential formulas, concepts, quick reference materials, and exam preparation resources for power systems engineering. Your complete study companion for academic and professional success. | |
</div> | |
</div> | |
""") | |
with gr.Accordion("⚡ Fault Analysis Formulas", open=False): | |
gr.HTML(""" | |
<div class="formula-box"> | |
<div class="formula-title"> | |
<i class="fas fa-bolt"></i> | |
Three-Phase Fault Current | |
</div> | |
<code class="formula-code">I_fault = V_nominal / Z_total</code> | |
<div class="formula-title" style="margin-top: 2rem;"> | |
<i class="fas fa-exclamation-triangle"></i> | |
Line-to-Ground Fault | |
</div> | |
<code class="formula-code">I_fault = 3 × V_nominal / (Z1 + Z2 + Z0)</code> | |
<div class="formula-title" style="margin-top: 2rem;"> | |
<i class="fas fa-arrows-alt-h"></i> | |
Line-to-Line Fault | |
</div> | |
<code class="formula-code">I_fault = √3 × V_nominal / (Z1 + Z2)</code> | |
<div class="formula-title" style="margin-top: 2rem;"> | |
<i class="fas fa-network-wired"></i> | |
Double Line-to-Ground Fault | |
</div> | |
<code class="formula-code">I_fault = V_nominal × [Z2 + Z0] / [Z1 × (Z2 + Z0) + Z2 × Z0]</code> | |
<div style="margin-top: 2rem; padding: 1.5rem; background: rgba(34, 197, 94, 0.1); border-radius: 10px; border-left: 4px solid #22c55e;"> | |
<h5 style="color: #22c55e; margin: 0 0 1rem 0;"> | |
<i class="fas fa-lightbulb"></i> Pro Tips | |
</h5> | |
<ul style="color: var(--text-secondary); line-height: 1.6;"> | |
<li>Always check if impedances are in per-unit or actual values</li> | |
<li>Consider pre-fault voltage conditions (typically 1.0 pu)</li> | |
<li>Verify sequence impedance values from equipment data</li> | |
<li>Account for fault resistance when applicable</li> | |
</ul> | |
</div> | |
</div> | |
""") | |
with gr.Accordion("🛡️ Protection System Principles", open=False): | |
gr.HTML(""" | |
<div class="formula-box"> | |
<div class="formula-title"> | |
<i class="fas fa-shield-alt"></i> | |
Overcurrent Protection | |
</div> | |
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; margin: 1.5rem 0;"> | |
<div style="background: rgba(102, 126, 234, 0.1); padding: 1.5rem; border-radius: 10px;"> | |
<h6 style="color: #667eea; margin: 0 0 1rem 0;">Time Characteristics</h6> | |
<ul style="color: var(--text-secondary); line-height: 1.8; margin: 0; padding-left: 1.5rem;"> | |
<li>Inverse Time Curves</li> | |
<li>Coordination Intervals (0.2-0.5s)</li> | |
<li>Pickup Settings (1.25-1.5 × FLA)</li> | |
<li>Time Dial Adjustments</li> | |
</ul> | |
</div> | |
<div style="background: rgba(118, 75, 162, 0.1); padding: 1.5rem; border-radius: 10px;"> | |
<h6 style="color: #764ba2; margin: 0 0 1rem 0;">Curve Types</h6> | |
<ul style="color: var(--text-secondary); line-height: 1.8; margin: 0; padding-left: 1.5rem;"> | |
<li>Standard Inverse (SI)</li> | |
<li>Very Inverse (VI)</li> | |
<li>Extremely Inverse (EI)</li> | |
<li>Definite Time</li> | |
</ul> | |
</div> | |
</div> | |
<div class="formula-title" style="margin-top: 2rem;"> | |
<i class="fas fa-ruler"></i> | |
Distance Protection Zones | |
</div> | |
<div style="background: linear-gradient(135deg, rgba(34, 197, 94, 0.1), rgba(34, 197, 94, 0.05)); padding: 2rem; border-radius: 15px; margin: 1rem 0;"> | |
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; text-align: center;"> | |
<div style="background: rgba(34, 197, 94, 0.2); padding: 1.5rem; border-radius: 10px;"> | |
<h6 style="color: #22c55e; margin: 0 0 0.5rem 0;">Zone 1</h6> | |
<p style="color: var(--text-primary); font-size: 1.1rem; margin: 0;">80-85% of line</p> | |
<p style="color: var(--text-secondary); font-size: 0.9rem; margin: 0.5rem 0 0 0;">Instantaneous</p> | |
</div> | |
<div style="background: rgba(251, 191, 36, 0.2); padding: 1.5rem; border-radius: 10px;"> | |
<h6 style="color: #fbbf24; margin: 0 0 0.5rem 0;">Zone 2</h6> | |
<p style="color: var(--text-primary); font-size: 1.1rem; margin: 0;">120% + 50% next</p> | |
<p style="color: var(--text-secondary); font-size: 0.9rem; margin: 0.5rem 0 0 0;">Time Delayed</p> | |
</div> | |
<div style="background: rgba(239, 68, 68, 0.2); padding: 1.5rem; border-radius: 10px;"> | |
<h6 style="color: #ef4444; margin: 0 0 0.5rem 0;">Zone 3</h6> | |
<p style="color: var(--text-primary); font-size: 1.1rem; margin: 0;">Backup coverage</p> | |
<p style="color: var(--text-secondary); font-size: 0.9rem; margin: 0.5rem 0 0 0;">Extended delay</p> | |
</div> | |
</div> | |
</div> | |
<div class="formula-title" style="margin-top: 2rem;"> | |
<i class="fas fa-balance-scale"></i> | |
Differential Protection | |
</div> | |
<code class="formula-code">Restraint = (|I1| + |I2|) / 2</code> | |
<code class="formula-code">Operating = |I1 + I2|</code> | |
<code class="formula-code">Operate if: Operating > (Slope × Restraint + Pickup)</code> | |
</div> | |
""") | |
with gr.Accordion("📐 Power System Calculations", open=False): | |
gr.HTML(""" | |
<div class="formula-box"> | |
<div class="formula-title"> | |
<i class="fas fa-calculator"></i> | |
Three-Phase Power Formulas | |
</div> | |
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; margin: 2rem 0;"> | |
<div> | |
<h6 style="color: #667eea; margin: 0 0 1rem 0;">Real Power</h6> | |
<code class="formula-code">P = √3 × V × I × cos(φ)</code> | |
<p style="color: var(--text-secondary); font-size: 0.9rem; margin: 0.5rem 0;">Watts (W)</p> | |
</div> | |
<div> | |
<h6 style="color: #667eea; margin: 0 0 1rem 0;">Reactive Power</h6> | |
<code class="formula-code">Q = √3 × V × I × sin(φ)</code> | |
<p style="color: var(--text-secondary); font-size: 0.9rem; margin: 0.5rem 0;">Vars</p> | |
</div> | |
<div> | |
<h6 style="color: #667eea; margin: 0 0 1rem 0;">Apparent Power</h6> | |
<code class="formula-code">S = √3 × V × I</code> | |
<p style="color: var(--text-secondary); font-size: 0.9rem; margin: 0.5rem 0;">VA</p> | |
</div> | |
<div> | |
<h6 style="color: #667eea; margin: 0 0 1rem 0;">Power Factor</h6> | |
<code class="formula-code">PF = P / S = cos(φ)</code> | |
<p style="color: var(--text-secondary); font-size: 0.9rem; margin: 0.5rem 0;">Dimensionless</p> | |
</div> | |
</div> | |
<div class="formula-title" style="margin-top: 2rem;"> | |
<i class="fas fa-arrows-alt"></i> | |
Per-Unit System Conversions | |
</div> | |
<code class="formula-code">Z_pu = Z_actual × (S_base / V_base²)</code> | |
<code class="formula-code">Z_new = Z_old × (S_new/S_old) × (V_old/V_new)²</code> | |
<div style="background: rgba(251, 191, 36, 0.1); padding: 1.5rem; border-radius: 10px; margin: 2rem 0;"> | |
<h6 style="color: #fbbf24; margin: 0 0 1rem 0;"> | |
<i class="fas fa-lightbulb"></i> Base Value Relationships | |
</h6> | |
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;"> | |
<code class="formula-code" style="background: rgba(0,0,0,0.2);">S_base = √3 × V_base × I_base</code> | |
<code class="formula-code" style="background: rgba(0,0,0,0.2);">Z_base = V_base² / S_base</code> | |
</div> | |
</div> | |
<div class="formula-title" style="margin-top: 2rem;"> | |
<i class="fas fa-plug"></i> | |
Transformer Calculations | |
</div> | |
<code class="formula-code">X_pu = (% impedance / 100) × (S_base / S_transformer)</code> | |
<code class="formula-code">Turn_ratio = V_primary / V_secondary</code> | |
<code class="formula-code">I_secondary = I_primary × Turn_ratio</code> | |
</div> | |
""") | |
with gr.Accordion("📊 Standards Quick Reference", open=False): | |
gr.HTML(""" | |
<div class="formula-box"> | |
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 3rem; margin: 2rem 0;"> | |
<div> | |
<div class="formula-title"> | |
<i class="fas fa-flag-usa"></i> | |
IEEE Standards | |
</div> | |
<div style="background: rgba(102, 126, 234, 0.1); padding: 1.5rem; border-radius: 10px; margin: 1rem 0;"> | |
<div style="display: flex; flex-direction: column; gap: 1rem;"> | |
<div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 0; border-bottom: 1px solid rgba(255,255,255,0.1);"> | |
<strong style="color: #667eea;">C37.2</strong> | |
<span style="color: var(--text-secondary); font-size: 0.9rem;">Device Function Numbers</span> | |
</div> | |
<div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 0; border-bottom: 1px solid rgba(255,255,255,0.1);"> | |
<strong style="color: #667eea;">519</strong> | |
<span style="color: var(--text-secondary); font-size: 0.9rem;">Harmonic Control</span> | |
</div> | |
<div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 0; border-bottom: 1px solid rgba(255,255,255,0.1);"> | |
<strong style="color: #667eea;">1547</strong> | |
<span style="color: var(--text-secondary); font-size: 0.9rem;">Distributed Generation</span> | |
</div> | |
<div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 0; border-bottom: 1px solid rgba(255,255,255,0.1);"> | |
<strong style="color: #667eea;">242</strong> | |
<span style="color: var(--text-secondary); font-size: 0.9rem;">Protection & Coordination</span> | |
</div> | |
<div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 0;"> | |
<strong style="color: #667eea;">399</strong> | |
<span style="color: var(--text-secondary); font-size: 0.9rem;">Industrial Power Analysis</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div> | |
<div class="formula-title"> | |
<i class="fas fa-globe"></i> | |
IEC Standards | |
</div> | |
<div style="background: rgba(118, 75, 162, 0.1); padding: 1.5rem; border-radius: 10px; margin: 1rem 0;"> | |
<div style="display: flex; flex-direction: column; gap: 1rem;"> | |
<div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 0; border-bottom: 1px solid rgba(255,255,255,0.1);"> | |
<strong style="color: #764ba2;">61850</strong> | |
<span style="color: var(--text-secondary); font-size: 0.9rem;">Substation Automation</span> | |
</div> | |
<div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 0; border-bottom: 1px solid rgba(255,255,255,0.1);"> | |
<strong style="color: #764ba2;">60909</strong> | |
<span style="color: var(--text-secondary); font-size: 0.9rem;">Short-Circuit Calculations</span> | |
</div> | |
<div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 0; border-bottom: 1px solid rgba(255,255,255,0.1);"> | |
<strong style="color: #764ba2;">60255</strong> | |
<span style="color: var(--text-secondary); font-size: 0.9rem;">Electrical Relays</span> | |
</div> | |
<div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 0; border-bottom: 1px solid rgba(255,255,255,0.1);"> | |
<strong style="color: #764ba2;">61131</strong> | |
<span style="color: var(--text-secondary); font-size: 0.9rem;">PLC Programming</span> | |
</div> | |
<div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 0;"> | |
<strong style="color: #764ba2;">62271</strong> | |
<span style="color: var(--text-secondary); font-size: 0.9rem;">High-Voltage Switchgear</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
""") | |
with gr.Accordion("🎓 Professional Exam Preparation", open=False): | |
gr.HTML(""" | |
<div class="formula-box"> | |
<div class="formula-title"> | |
<i class="fas fa-graduation-cap"></i> | |
PE Exam Power Topics | |
</div> | |
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 2rem; margin: 2rem 0;"> | |
<div style="background: rgba(34, 197, 94, 0.1); padding: 2rem; border-radius: 15px;"> | |
<h6 style="color: #22c55e; margin: 0 0 1.5rem 0; display: flex; align-items: center; gap: 0.5rem;"> | |
<i class="fas fa-check-circle"></i> | |
Core Topics (60-70%) | |
</h6> | |
<ul style="color: var(--text-secondary); line-height: 2; margin: 0; padding-left: 1.5rem;"> | |
<li>Three-phase circuit analysis</li> | |
<li>Symmetrical components</li> | |
<li>Fault analysis calculations</li> | |
<li>Protection system coordination</li> | |
<li>Power flow studies</li> | |
<li>Transformer connections</li> | |
<li>Motor starting analysis</li> | |
</ul> | |
</div> | |
<div style="background: rgba(251, 191, 36, 0.1); padding: 2rem; border-radius: 15px;"> | |
<h6 style="color: #fbbf24; margin: 0 0 1.5rem 0; display: flex; align-items: center; gap: 0.5rem;"> | |
<i class="fas fa-star"></i> | |
Advanced Topics (30-40%) | |
</h6> | |
<ul style="color: var(--text-secondary); line-height: 2; margin: 0; padding-left: 1.5rem;"> | |
<li>Economic dispatch</li> | |
<li>Stability studies</li> | |
<li>Harmonics & power quality</li> | |
<li>Grounding systems</li> | |
<li>Arc flash analysis</li> | |
<li>Renewable integration</li> | |
<li>Load forecasting</li> | |
</ul> | |
</div> | |
</div> | |
<div class="formula-title" style="margin-top: 2rem;"> | |
<i class="fas fa-brain"></i> | |
Essential Formulas to Memorize | |
</div> | |
<div style="background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1)); padding: 2rem; border-radius: 15px; margin: 1rem 0;"> | |
<div style="display: grid; grid-template-columns: 1fr; gap: 1rem;"> | |
<div style="background: rgba(255,255,255,0.05); padding: 1rem; border-radius: 8px; border-left: 4px solid #667eea;"> | |
<strong style="color: #667eea;">Symmetrical Components:</strong> | |
<code style="display: block; margin-top: 0.5rem; color: var(--text-secondary);">a = 1∠120°, a² = 1∠240°</code> | |
</div> | |
<div style="background: rgba(255,255,255,0.05); padding: 1rem; border-radius: 8px; border-left: 4px solid #764ba2;"> | |
<strong style="color: #764ba2;">Power Triangle:</strong> | |
<code style="display: block; margin-top: 0.5rem; color: var(--text-secondary);">S² = P² + Q², PF = P/S</code> | |
</div> | |
<div style="background: rgba(255,255,255,0.05); padding: 1rem; border-radius: 8px; border-left: 4px solid #22c55e;"> | |
<strong style="color: #22c55e;">Transmission Line:</strong> | |
<code style="display: block; margin-top: 0.5rem; color: var(--text-secondary);">Z = √(R + jωL)/(G + jωC)</code> | |
</div> | |
<div style="background: rgba(255,255,255,0.05); padding: 1rem; border-radius: 8px; border-left: 4px solid #fbbf24;"> | |
<strong style="color: #fbbf24;">Regulation:</strong> | |
<code style="display: block; margin-top: 0.5rem; color: var(--text-secondary);">%Reg = (V_no-load - V_full-load)/V_full-load × 100</code> | |
</div> | |
</div> | |
</div> | |
<div style="background: rgba(34, 197, 94, 0.1); padding: 2rem; border-radius: 15px; margin: 2rem 0; border-left: 4px solid #22c55e;"> | |
<h6 style="color: #22c55e; margin: 0 0 1rem 0;"> | |
<i class="fas fa-lightbulb"></i> Exam Success Tips | |
</h6> | |
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem;"> | |
<ul style="color: var(--text-secondary); line-height: 1.8; margin: 0; padding-left: 1.5rem;"> | |
<li>Practice time management</li> | |
<li>Master per-unit conversions</li> | |
<li>Understand phasor diagrams</li> | |
<li>Know protection principles</li> | |
</ul> | |
<ul style="color: var(--text-secondary); line-height: 1.8; margin: 0; padding-left: 1.5rem;"> | |
<li>Memorize key formulas</li> | |
<li>Practice fault calculations</li> | |
<li>Study NESC/NEC codes</li> | |
<li>Review motor starting</li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
""") | |
# Interactive Calculator Section | |
gr.HTML(""" | |
<div class="feature-card interactive-card"> | |
<div class="feature-title"> | |
<i class="fas fa-calculator"></i> | |
Quick Power Systems Calculator | |
</div> | |
<div class="feature-description"> | |
Built-in calculators for common power systems calculations. Perfect for quick validations and study practice. | |
</div> | |
</div> | |
""") | |
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(--glass-bg); padding: 2rem; border-radius: 15px; border: 1px solid var(--border-color); min-height: 200px; display: flex; align-items: center; justify-content: center;"> | |
<div style="text-align: center; color: var(--text-secondary);"> | |
<i class="fas fa-calculator" style="font-size: 3rem; margin-bottom: 1rem; opacity: 0.5;"></i> | |
<p>Enter values and click Calculate to see results</p> | |
</div> | |
</div> | |
""") | |
def calculate_power(voltage, current, pf): | |
if voltage and current and pf: | |
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(--glass-bg); padding: 2rem; border-radius: 15px; border: 1px solid var(--border-color);"> | |
<h4 style="color: var(--text-primary); margin-bottom: 2rem; text-align: center;"> | |
<i class="fas fa-chart-line"></i> Calculation Results | |
</h4> | |
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem;"> | |
<div style="background: rgba(34, 197, 94, 0.1); padding: 1.5rem; border-radius: 10px; text-align: center;"> | |
<h6 style="color: #22c55e; 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(251, 191, 36, 0.1); padding: 1.5rem; border-radius: 10px; text-align: center;"> | |
<h6 style="color: #fbbf24; 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(102, 126, 234, 0.1); padding: 1.5rem; border-radius: 10px; text-align: center;"> | |
<h6 style="color: #667eea; 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: 1.5rem; background: rgba(255,255,255,0.05); border-radius: 10px;"> | |
<h6 style="color: var(--text-primary); margin: 0 0 1rem 0;"> | |
<i class="fas fa-info-circle"></i> System Parameters | |
</h6> | |
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; color: var(--text-secondary);"> | |
<div>Line Voltage: {voltage} V</div> | |
<div>Line Current: {current} A</div> | |
<div>Power Factor: {pf:.3f} {'(Leading)' if pf > 0.95 else '(Lagging)' if pf < 0.95 else '(Unity)'}</div> | |
<div>Phase Angle: {(180/3.14159) * ((1-pf**2)**0.5):.1f}°</div> | |
</div> | |
</div> | |
</div> | |
""" | |
else: | |
return """ | |
<div style="background: var(--glass-bg); padding: 2rem; border-radius: 15px; border: 1px solid var(--border-color); min-height: 200px; display: flex; align-items: center; justify-content: center;"> | |
<div style="text-align: center; color: var(--text-secondary);"> | |
<i class="fas fa-exclamation-triangle" style="font-size: 3rem; margin-bottom: 1rem; color: #fbbf24;"></i> | |
<p>Please enter valid values for voltage, current, and power factor</p> | |
</div> | |
</div> | |
""" | |
calc_btn.click( | |
fn=calculate_power, | |
inputs=[calc_voltage, calc_current, calc_pf], | |
outputs=[calc_results] | |
) | |
# Event handlers for login with enhanced feedback | |
def handle_login(username, password): | |
success, user_data, message, sessions_update, main_visibility = login_user(username, password) | |
if success: | |
welcome_html = f""" | |
<div class="feature-card interactive-card glow-effect" style="text-align: center; background: linear-gradient(135deg, rgba(34, 197, 94, 0.1), rgba(34, 197, 94, 0.05));"> | |
<div style="font-size: 4rem; margin-bottom: 1rem;"> | |
<i class="fas fa-user-check" style="color: #22c55e;"></i> | |
</div> | |
<h2 style="font-size: 2.5rem; font-weight: 800; background: linear-gradient(135deg, #22c55e, #16a34a); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin: 0;"> | |
Welcome back, {user_data['full_name']}! 👋 | |
</h2> | |
<p style="color: var(--text-secondary); font-size: 1.2rem; margin: 1rem 0 2rem 0;"> | |
Ready to tackle complex power systems challenges? Your AI consultant is ready to help! | |
</p> | |
<div style="display: flex; justify-content: center; gap: 2rem; margin-top: 2rem;"> | |
<div style="text-align: center;"> | |
<div style="background: rgba(102, 126, 234, 0.1); border-radius: 50%; width: 60px; height: 60px; display: flex; align-items: center; justify-content: center; margin: 0 auto 0.5rem;"> | |
<i class="fas fa-comments" style="color: #667eea; font-size: 1.5rem;"></i> | |
</div> | |
<p style="color: var(--text-secondary); font-size: 0.9rem; margin: 0;">AI Chat Ready</p> | |
</div> | |
<div style="text-align: center;"> | |
<div style="background: rgba(118, 75, 162, 0.1); border-radius: 50%; width: 60px; height: 60px; display: flex; align-items: center; justify-content: center; margin: 0 auto 0.5rem;"> | |
<i class="fas fa-chart-line" style="color: #764ba2; font-size: 1.5rem;"></i> | |
</div> | |
<p style="color: var(--text-secondary); font-size: 0.9rem; margin: 0;">Diagrams Available</p> | |
</div> | |
<div style="text-align: center;"> | |
<div style="background: rgba(34, 197, 94, 0.1); border-radius: 50%; width: 60px; height: 60px; display: flex; align-items: center; justify-content: center; margin: 0 auto 0.5rem;"> | |
<i class="fas fa-book" style="color: #22c55e; font-size: 1.5rem;"></i> | |
</div> | |
<p style="color: var(--text-secondary); font-size: 0.9rem; margin: 0;">Resources Loaded</p> | |
</div> | |
</div> | |
</div> | |
""" | |
return ( | |
message, | |
user_data, | |
gr.update(visible=False), # Hide login | |
gr.update(visible=True), # Show main | |
welcome_html, | |
sessions_update | |
) | |
else: | |
return ( | |
message, | |
None, | |
gr.update(visible=True), # Keep login visible | |
gr.update(visible=False), # Keep main hidden | |
"", | |
gr.update() | |
) | |
def handle_registration(username, email, password, confirm_password, full_name): | |
success, message = register_user(username, email, password, confirm_password, full_name) | |
return message | |
def handle_new_chat(user_data): | |
if user_data: | |
session_id, session_choices = start_new_chat(user_data) | |
session_choices_formatted = [(f"💬 {s['title']} ({s['created_at'][:10]})", s['session_id']) | |
for s in consultant.user_manager.get_user_sessions(user_data['id'])] | |
return ( | |
session_id, | |
gr.update(choices=session_choices_formatted, value=session_id), | |
[] # Clear chat history | |
) | |
return None, gr.update(), [] | |
def handle_chat_selection(session_id, user_data): | |
if session_id and user_data: | |
history = load_selected_chat(session_id, user_data) | |
return history, session_id | |
return [], None | |
def handle_chat_message(message, history, user_data, session_id): | |
return chat_interface(message, history, user_data, session_id) | |
def clear_chat(): | |
return [], "" | |
def handle_practice_generation(topic, difficulty, num_questions, user_data): | |
return generate_practice_questions(topic, difficulty, num_questions, user_data) | |
def handle_standard_explanation(standard, user_data): | |
return explain_standard_interface(standard, user_data) | |
# Wire up all event handlers with enhanced interactions | |
login_btn.click( | |
fn=handle_login, | |
inputs=[login_username, login_password], | |
outputs=[login_status, user_state, login_section, main_section, user_welcome, chat_sessions_dropdown] | |
) | |
register_btn.click( | |
fn=handle_registration, | |
inputs=[reg_username, reg_email, reg_password, reg_confirm_password, reg_full_name], | |
outputs=[register_status] | |
) | |
new_chat_btn.click( | |
fn=handle_new_chat, | |
inputs=[user_state], | |
outputs=[session_state, chat_sessions_dropdown, chatbot] | |
) | |
chat_sessions_dropdown.change( | |
fn=handle_chat_selection, | |
inputs=[chat_sessions_dropdown, user_state], | |
outputs=[chatbot, session_state] | |
) | |
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] | |
) | |
generate_btn.click( | |
fn=handle_practice_generation, | |
inputs=[topic_input, difficulty_input, num_questions_input, user_state], | |
outputs=[practice_output] | |
) | |
explain_btn.click( | |
fn=handle_standard_explanation, | |
inputs=[standard_input, user_state], | |
outputs=[standard_output] | |
) | |
# Add keyboard shortcuts and enhanced UX | |
gr.HTML(""" | |
<script> | |
// Enhanced keyboard shortcuts | |
document.addEventListener('keydown', function(e) { | |
// Ctrl/Cmd + Enter to send message | |
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { | |
const sendButton = document.querySelector('button[variant="primary"]'); | |
if (sendButton && sendButton.textContent.includes('Send')) { | |
sendButton.click(); | |
} | |
} | |
// Escape to clear chat | |
if (e.key === 'Escape') { | |
const clearButton = document.querySelector('button[variant="secondary"]'); | |
if (clearButton && clearButton.textContent.includes('Clear')) { | |
clearButton.click(); | |
} | |
} | |
}); | |
// Add loading states and animations | |
function addLoadingState(element) { | |
const originalText = element.textContent; | |
element.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Processing...'; | |
element.disabled = true; | |
setTimeout(() => { | |
element.innerHTML = originalText; | |
element.disabled = false; | |
}, 2000); | |
} | |
// Enhanced visual feedback | |
document.addEventListener('click', function(e) { | |
if (e.target.matches('button')) { | |
e.target.style.transform = 'scale(0.95)'; | |
setTimeout(() => { | |
e.target.style.transform = 'scale(1)'; | |
}, 150); | |
} | |
}); | |
// Smooth scroll to new messages | |
const observer = new MutationObserver(function(mutations) { | |
mutations.forEach(function(mutation) { | |
if (mutation.addedNodes.length) { | |
const chatContainer = document.querySelector('.gradio-chatbot'); | |
if (chatContainer) { | |
chatContainer.scrollTop = chatContainer.scrollHeight; | |
} | |
} | |
}); | |
}); | |
// Auto-focus message input | |
setTimeout(() => { | |
const messageInput = document.querySelector('textarea[placeholder*="Ask about"]'); | |
if (messageInput) { | |
messageInput.focus(); | |
} | |
}, 1000); | |
// Add typing indicator simulation | |
function simulateTyping() { | |
const typingIndicator = document.createElement('div'); | |
typingIndicator.innerHTML = ` | |
<div style="display: flex; align-items: center; gap: 0.5rem; padding: 1rem; color: var(--text-secondary); font-style: italic;"> | |
<div class="loading-spinner"></div> | |
AI Consultant is thinking... | |
</div> | |
`; | |
return typingIndicator; | |
} | |
// Enhance form validation | |
function validateEmail(email) { | |
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | |
return re.test(email); | |
} | |
// Add real-time form validation | |
document.addEventListener('input', function(e) { | |
if (e.target.type === 'email') { | |
if (validateEmail(e.target.value)) { | |
e.target.style.borderColor = '#22c55e'; | |
} else { | |
e.target.style.borderColor = '#ef4444'; | |
} | |
} | |
if (e.target.type === 'password') { | |
if (e.target.value.length >= 6) { | |
e.target.style.borderColor = '#22c55e'; | |
} else { | |
e.target.style.borderColor = '#fbbf24'; | |
} | |
} | |
}); | |
// Add tooltips for better UX | |
const tooltips = { | |
'Practice Pack': 'Generate customized practice questions with detailed solutions', | |
'Standards Explorer': 'Get comprehensive explanations of power systems standards', | |
'AI Consultant': 'Chat with AI expert for fault analysis and engineering help', | |
'Study Resources': 'Access formulas, calculations, and exam preparation materials' | |
}; | |
// Progressive loading animation | |
function addProgressiveLoading() { | |
const cards = document.querySelectorAll('.feature-card'); | |
cards.forEach((card, index) => { | |
card.style.opacity = '0'; | |
card.style.transform = 'translateY(30px)'; | |
setTimeout(() => { | |
card.style.transition = 'all 0.6s ease'; | |
card.style.opacity = '1'; | |
card.style.transform = 'translateY(0)'; | |
}, index * 200); | |
}); | |
} | |
// Initialize progressive loading | |
setTimeout(addProgressiveLoading, 500); | |
// Add particle effects for premium feel | |
function createParticle() { | |
const particle = document.createElement('div'); | |
particle.style.position = 'fixed'; | |
particle.style.width = '4px'; | |
particle.style.height = '4px'; | |
particle.style.background = 'linear-gradient(45deg, #667eea, #764ba2)'; | |
particle.style.borderRadius = '50%'; | |
particle.style.pointerEvents = 'none'; | |
particle.style.zIndex = '9999'; | |
particle.style.opacity = '0.6'; | |
const startX = Math.random() * window.innerWidth; | |
const startY = window.innerHeight; | |
particle.style.left = startX + 'px'; | |
particle.style.top = startY + 'px'; | |
document.body.appendChild(particle); | |
const animation = particle.animate([ | |
{ transform: 'translateY(0px) translateX(0px)', opacity: 0.6 }, | |
{ transform: `translateY(-${window.innerHeight + 100}px) translateX(${(Math.random() - 0.5) * 200}px)`, opacity: 0 } | |
], { | |
duration: 3000 + Math.random() * 2000, | |
easing: 'ease-out' | |
}); | |
animation.onfinish = () => particle.remove(); | |
} | |
// Create particles occasionally for ambiance | |
setInterval(() => { | |
if (Math.random() < 0.1) createParticle(); | |
}, 1000); | |
console.log('🚀 Power Systems Consultant UI Enhanced - Ready for Engineering Excellence!'); | |
</script> | |
""") | |
return app | |
# Enhanced launch configuration | |
if __name__ == "__main__": | |
app = create_app() | |
# Enhanced startup information with modern styling | |
print("⚡" * 50) | |
print("🚀 POWER SYSTEMS MINI-CONSULTANT v2.0") | |
print("⚡" * 50) | |
print() | |
print("🎨 MODERN UI/UX FEATURES:") | |
print(" ✨ Glassmorphism design with dark theme") | |
print(" 🌈 Dynamic gradients and animations") | |
print(" 📱 Fully responsive interface") | |
print(" 🎯 Interactive elements with hover effects") | |
print(" 💫 Particle effects and smooth transitions") | |
print(" 🔥 Real-time form validation") | |
print(" ⌨️ Keyboard shortcuts (Ctrl+Enter to send)") | |
print() | |
print("🔧 CORE FEATURES:") | |
print(" 🤖 AI-powered chat consultant") | |
print(" 📊 Professional diagram generation") | |
print(" 📚 Interactive practice question generator") | |
print(" 📋 Comprehensive standards explanations") | |
print(" 🧮 Built-in power calculations") | |
print(" 💾 User authentication & session management") | |
print(" 📜 Persistent chat history") | |
print(" 🎓 Exam preparation resources") | |
print() | |
if consultant is None: | |
print("⚠️ WARNING: System not fully initialized") | |
print(" Please ensure GROQ_API_KEY is set in environment") | |
print(" Some features may not work properly") | |
print() | |
else: | |
print("✅ SYSTEM STATUS: All systems operational!") | |
print() | |
print("🌐 ACCESS INFORMATION:") | |
print(" 📍 Local URL: http://localhost:7860") | |
print(" 🔒 Secure authentication required") | |
print(" 💡 Create account or use existing credentials") | |
print() | |
print("🎯 GETTING STARTED:") | |
print(" 1️⃣ Register/Login to access full features") | |
print(" 2️⃣ Start chatting with AI consultant") | |
print(" 3️⃣ Try: 'Explain three-phase fault analysis'") | |
print(" 4️⃣ Generate diagrams: 'Create a single line diagram'") | |
print(" 5️⃣ Explore practice packs and standards") | |
print() | |
print("💫 Experience the future of power systems engineering!") | |
print("⚡" * 50) | |
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, | |
app_kwargs={ | |
"docs_url": None, | |
"redoc_url": None, | |
} | |
) |