banao-tech's picture
Update app.py
6a8ccf5 verified
raw
history blame
21.2 kB
import streamlit as st
import json
from datetime import datetime
import time
import requests
import boto3
import uuid
import os
# Load configuration from environment variables (Hugging Face Spaces)
API_ENDPOINT = os.getenv("API_ENDPOINT", "https://oau6sljd4l.execute-api.ap-south-1.amazonaws.com/production/process")
# DynamoDB configuration for session tracking
DYNAMODB_REGION = os.getenv("DYNAMODB_REGION", "ap-south-1")
SESSION_TABLE = os.getenv("SESSION_TABLE", "SessionTracking")
# AWS Configuration
AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID")
AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY")
AWS_DEFAULT_REGION = os.getenv("AWS_DEFAULT_REGION", "ap-south-1")
# Default values
DEFAULT_COURSE_ID = int(os.getenv("DEFAULT_COURSE_ID", "47"))
DEFAULT_USER_ID = int(os.getenv("DEFAULT_USER_ID", "30"))
DEFAULT_PERSONALIZATION_ID = int(os.getenv("DEFAULT_PERSONALIZATION_ID", "100"))
API_TIMEOUT = int(os.getenv("API_TIMEOUT", "30"))
# Authentication credentials from environment variables
VALID_USERNAME = os.getenv("APP_USERNAME")
VALID_PASSWORD = os.getenv("APP_PASSWORD")
# Set page configuration
st.set_page_config(
page_title="Base Course Personalization",
layout="wide",
initial_sidebar_state="collapsed",
page_icon="πŸ“š",
menu_items={
'Get Help': None,
'Report a bug': None,
'About': None
}
)
# Custom CSS for dark theme styling
st.markdown("""
<style>
/* Dark theme colors */
:root {
--background-color: #1E1E1E;
--card-background: #2D2D2D;
--text-color: #E0E0E0;
--accent-color: #4F97FF;
--border-color: #444444;
--header-color: #4F97FF;
--subheader-color: #FFFFFF;
--success-color: #10B981;
--warning-color: #F59E0B;
--error-color: #EF4444;
}
/* Main container styling */
.stApp {
background-color: var(--background-color);
color: var(--text-color);
}
/* Headers */
.main-header {
font-size: 2.5rem;
font-weight: 600;
color: var(--header-color);
margin-bottom: 1rem;
text-align: center;
text-shadow: 0 2px 4px rgba(79, 151, 255, 0.3);
}
.section-header {
font-size: 1.5rem;
font-weight: 500;
color: var(--subheader-color);
margin-top: 2rem;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid var(--border-color);
display: flex;
align-items: center;
gap: 0.5rem;
}
/* Form container */
div[data-testid="stForm"] {
background-color: var(--card-background);
padding: 2rem;
border-radius: 12px;
box-shadow: 0 8px 25px rgba(0,0,0,0.4);
border: 1px solid var(--border-color);
margin-bottom: 1rem;
}
/* Button styling */
.stButton>button {
background: linear-gradient(135deg, var(--accent-color) 0%, #3B82F6 100%);
color: white;
border-radius: 8px;
padding: 0.75rem 2rem;
font-weight: 600;
border: none;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(79, 151, 255, 0.3);
}
.stButton>button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(79, 151, 255, 0.4);
}
/* Input fields styling */
div[data-baseweb="select"] > div {
background-color: var(--background-color);
border: 2px solid var(--border-color);
border-radius: 8px;
transition: border-color 0.3s ease;
}
div[data-baseweb="select"] > div:focus-within {
border-color: var(--accent-color);
box-shadow: 0 0 0 3px rgba(79, 151, 255, 0.1);
}
.stTextInput > div > div > input,
.stNumberInput > div > div > input,
.stTextArea > div > div > textarea {
background-color: var(--background-color);
color: var(--text-color);
border: 2px solid var(--border-color);
border-radius: 8px;
padding: 0.75rem;
transition: border-color 0.3s ease;
}
.stTextInput > div > div > input:focus,
.stNumberInput > div > div > input:focus,
.stTextArea > div > div > textarea:focus {
border-color: var(--accent-color);
box-shadow: 0 0 0 3px rgba(79, 151, 255, 0.1);
}
/* Topic container styling */
.topic-container {
background-color: var(--card-background);
padding: 1.5rem;
border-radius: 10px;
margin-bottom: 1rem;
border: 1px solid var(--border-color);
position: relative;
}
.topic-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
font-weight: 600;
color: var(--accent-color);
}
/* Radio buttons */
.stRadio > div {
background-color: transparent;
gap: 1rem;
}
.stRadio > div > label > div {
color: var(--text-color) !important;
font-weight: 500;
}
/* Labels */
.stSelectbox > label,
.stTextInput > label,
.stNumberInput > label,
.stTextArea > label,
.stRadio > label {
color: var(--text-color) !important;
font-weight: 500;
margin-bottom: 0.5rem;
}
/* Toggle switch */
.stToggle > label {
color: var(--text-color) !important;
font-weight: 500;
}
/* Spacing */
div.block-container {
padding-top: 2rem;
max-width: 1200px;
}
hr {
margin: 2rem 0;
border: none;
border-top: 1px solid var(--border-color);
}
/* API Response Container */
.api-response {
background-color: #1A1A1A;
border-radius: 10px;
padding: 1.5rem;
border-left: 4px solid var(--accent-color);
margin: 1rem 0;
}
.session-info {
background-color: var(--card-background);
padding: 1.5rem;
border-radius: 10px;
margin: 1rem 0;
border: 1px solid var(--border-color);
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
}
/* Action buttons container */
.action-buttons {
display: flex;
gap: 1rem;
margin: 1rem 0;
justify-content: flex-start;
}
/* Small action buttons */
.small-button {
padding: 0.5rem 1rem;
font-size: 0.875rem;
}
/* Success/Error styling */
.stSuccess, .stError, .stWarning, .stInfo {
border-radius: 8px;
padding: 1rem;
margin: 1rem 0;
}
/* Login container */
.login-container {
background-color: var(--card-background);
padding: 3rem;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
border: 1px solid var(--border-color);
}
</style>
""", unsafe_allow_html=True)
# Initialize session state for topics and authentication
if 'topics_list' not in st.session_state:
st.session_state.topics_list = [{"topic_title": "What is Flask", "chapter_title": "Introduction to Flask"}]
if 'session_ids' not in st.session_state:
st.session_state.session_ids = []
if 'authenticated' not in st.session_state:
st.session_state.authenticated = False
# Authentication check
if not st.session_state.authenticated:
st.markdown('<h1 class="main-header">πŸ” Login to Course Personalization</h1>', unsafe_allow_html=True)
col1, col2, col3 = st.columns([1, 2, 1])
with col2:
with st.container():
st.markdown('<div class="login-container">', unsafe_allow_html=True)
with st.form("login_form"):
st.markdown('<div class="section-header">πŸ”‘ Authentication Required</div>', unsafe_allow_html=True)
username = st.text_input("Username", placeholder="Enter username", key="login_username")
password = st.text_input("Password", placeholder="Enter password", type="password", key="login_password")
login_submitted = st.form_submit_button("πŸš€ Login", use_container_width=True)
if login_submitted:
if username == VALID_USERNAME and password == VALID_PASSWORD:
st.session_state.authenticated = True
st.success("Login successful! Redirecting...")
time.sleep(1)
st.rerun()
else:
st.error("❌ Invalid username or password")
st.markdown('</div>', unsafe_allow_html=True)
st.markdown("---")
st.markdown(f"""
<div style="text-align: center; color: #888888;">
<small>Demo Credentials: {VALID_USERNAME} / {VALID_PASSWORD if len(VALID_PASSWORD) <= 10 else VALID_PASSWORD[:3] + '...'}</small>
</div>
""", unsafe_allow_html=True)
st.stop()
# Main app header
st.markdown('<h1 class="main-header">πŸ“š Base Course Personalization</h1>', unsafe_allow_html=True)
# Logout button in top right
col1, col2, col3 = st.columns([2, 1, 1])
with col3:
if st.button("πŸ”“ Logout", key="logout_btn"):
st.session_state.authenticated = False
st.session_state.topics_list = [{"topic_title": "What is Flask", "chapter_title": "Introduction to Flask"}]
st.session_state.session_ids = []
st.rerun()
# Main Form
with st.form("personalization_form", clear_on_submit=False):
# Topics Section
st.markdown('<div class="section-header">πŸ“‹ Topics Configuration</div>', unsafe_allow_html=True)
# Option to choose single or multiple topics
topic_mode = st.radio(
"Select Topic Mode",
["Single Topic", "Multiple Topics"],
horizontal=True,
help="Choose whether to process one topic or multiple topics"
)
if topic_mode == "Single Topic":
st.markdown('<div class="topic-container">', unsafe_allow_html=True)
st.markdown('<div class="topic-header">πŸ“ Single Topic Configuration</div>', unsafe_allow_html=True)
col1, col2 = st.columns(2)
with col1:
single_topic_title = st.text_input("Topic Title", value="What is Flask", help="Enter the topic title", key="single_topic")
with col2:
single_chapter_title = st.text_input("Chapter Title", value="Introduction to Flask", help="Enter the chapter title", key="single_chapter")
st.markdown('</div>', unsafe_allow_html=True)
else:
st.markdown("### πŸ“š Multiple Topics Configuration")
# Display existing topics with better styling
for i, topic in enumerate(st.session_state.topics_list):
st.markdown(f'<div class="topic-container">', unsafe_allow_html=True)
st.markdown(f'<div class="topic-header">πŸ“– Topic {i+1}</div>', unsafe_allow_html=True)
col1, col2, col3 = st.columns([3, 3, 1])
with col1:
topic_title = st.text_input(
"Topic Title",
value=topic["topic_title"],
key=f"topic_title_{i}",
help="Enter the topic title"
)
st.session_state.topics_list[i]["topic_title"] = topic_title
with col2:
chapter_title = st.text_input(
"Chapter Title",
value=topic["chapter_title"],
key=f"chapter_title_{i}",
help="Enter the chapter title"
)
st.session_state.topics_list[i]["chapter_title"] = chapter_title
with col3:
if len(st.session_state.topics_list) > 1:
st.markdown("<br>", unsafe_allow_html=True) # Add spacing
if st.button("πŸ—‘οΈ", key=f"remove_{i}", help="Remove this topic"):
st.session_state.topics_list.pop(i)
st.rerun()
st.markdown('</div>', unsafe_allow_html=True)
# Language & Voice Settings Section
st.markdown('<div class="section-header">πŸ—£οΈ Language & Voice Settings</div>', unsafe_allow_html=True)
col1, col2, col3 = st.columns(3)
with col1:
target_language = st.selectbox(
"Target Language",
["english", "hindi", "marathi", "kannada", "punjabi","gujarati"],
index=0,
format_func=lambda x: x.capitalize(),
help="Select the target language for content generation"
)
with col2:
tts_gender = st.selectbox(
"Voice Gender",
["male", "female"],
index=0,
format_func=lambda x: x.capitalize(),
help="Select the voice gender for text-to-speech"
)
with col3:
tts_voice = st.selectbox(
"Voice Style",
["onyx", "echo", "soprano", "alto", "robotic"],
index=0,
format_func=lambda x: x.capitalize(),
help="Select the voice style for text-to-speech"
)
# Technical Settings Section
st.markdown('<div class="section-header">πŸ’» Technical Settings</div>', unsafe_allow_html=True)
col1, col2 = st.columns(2)
with col1:
programming_language = st.selectbox(
"Programming Language",
["python", "java", "javascript", "c++", "go"],
index=0,
format_func=lambda x: x.capitalize(),
help="Select the primary programming language for examples"
)
with col2:
st.markdown("<br>", unsafe_allow_html=True) # Add spacing
toggle_hinglish = st.toggle("Enable Hinglish", value=True, help="Enable mixing of Hindi and English")
# Submit button
st.markdown("<br>", unsafe_allow_html=True)
col1, col2, col3 = st.columns([1, 2, 1])
with col2:
submitted = st.form_submit_button("πŸš€ Generate Base Course", use_container_width=True)
# Add/Remove topic buttons outside the form (for multiple topics)
if topic_mode == "Multiple Topics":
st.markdown('<div class="action-buttons">', unsafe_allow_html=True)
col1, col2, col3, col4 = st.columns([2, 2, 2, 4])
with col1:
if st.button(" Add Topic", key="add_topic", help="Add a new topic"):
st.session_state.topics_list.append({
"topic_title": f"Topic {len(st.session_state.topics_list) + 1}",
"chapter_title": f"Chapter {len(st.session_state.topics_list) + 1}"
})
st.rerun()
with col2:
if st.button(" Reset All", key="reset_topics", help="Reset to default topics"):
st.session_state.topics_list = [{"topic_title": "What is Flask", "chapter_title": "Introduction to Flask"}]
st.rerun()
st.markdown('</div>', unsafe_allow_html=True)
# Handle submission
if submitted:
# Determine which topics to use based on mode
if topic_mode == "Single Topic":
topics_to_process = [{"topic_title": single_topic_title, "chapter_title": single_chapter_title}]
else:
topics_to_process = st.session_state.topics_list
# Validate inputs
valid_topics = []
for topic in topics_to_process:
if topic["topic_title"].strip() and topic["chapter_title"].strip():
valid_topics.append(topic)
if not valid_topics:
st.error("❌ Please enter at least one topic with both topic title and chapter title")
else:
# Show loading state with better animation
with st.spinner("🎬 Generating your personalized course... This may take a few moments."):
progress_bar = st.progress(0)
for i in range(100):
time.sleep(0.02) # Simulating progress
progress_bar.progress(i + 1)
# Hardcoded values
course_id = DEFAULT_COURSE_ID
user_id = DEFAULT_USER_ID
personalization_id = DEFAULT_PERSONALIZATION_ID
# Create user profile (hardcoded)
user_profile = {
"personalized": True,
"user_name": "System User",
"user_age": 25,
"user_gender": "male",
"user_tech_knowledge": "beginner",
"user_preferred_activity": "coding, learning, technology",
"user_food": "healthy food, vegetarian",
"user_physical_activities": "walking, yoga",
"learning_style": "visual",
"target_language": target_language,
"tts_gender": tts_gender,
"tts_voice": tts_voice,
"toggle_hinglish": toggle_hinglish,
"run_visualization": False,
"subtitle": "",
"age_group": "18-25"
}
# Create settings
settings = {
"target_language": target_language,
"tts_gender": tts_gender,
"tts_voice": tts_voice,
"toggle_hinglish": toggle_hinglish,
"run_visualization": False,
"subtitle": "",
"programming_language": programming_language,
"slide_colour": "blue",
"video_type": "base_video"
}
# Generate topics data with user input for titles and hardcoded values for others
topics_data = []
for i, topic in enumerate(valid_topics):
topics_data.append({
"topic_id": 10834 + i, # Hardcoded with increment
"topic_title": topic["topic_title"].strip(),
"chapter_id": 647, # Hardcoded
"chapter_title": topic["chapter_title"].strip(), # User input
"course_id": course_id,
"video_url": f"https://techlearn-dev.s3.ap-south-1.amazonaws.com/course_videos/47/647/172906{4365+i*50}.mp4", # Hardcoded with variation
"video_duration": 462 + i*20, # Hardcoded with variation
"sequence_number": i + 1,
})
# Create payload (always multiple topics structure)
payload = {
"personalization_id": personalization_id,
"user_id": user_id,
"course_id": course_id,
"total_videos": len(topics_data),
"created_at": datetime.utcnow().isoformat(),
"user_profile": user_profile,
"topics": topics_data,
"settings": settings
}
# Make API call
try:
headers = {
'Content-Type': 'application/json'
}
response = requests.post(API_ENDPOINT, json=payload, headers=headers, timeout=API_TIMEOUT)
if response.status_code == 200:
response_data = response.json()
session_ids = response_data.get("session_ids", [])
st.success(" Base Course started successfully!")
# Store session IDs in session state
st.session_state.session_ids.extend(session_ids)
# Display results in a clean format
st.markdown("### Generation Summary")
col1, col2 = st.columns(2)
with col1:
st.markdown(f"""
**Course ID**: `{course_id}`
**Programming Language**: {programming_language.capitalize()}
**Target Language**: {target_language.capitalize()}
""")
with col2:
st.markdown(f"""
**Voice**: {tts_voice.capitalize()} ({tts_gender.capitalize()})
**Topics Count**: {len(valid_topics)}
**Hinglish**: {"Enabled" if toggle_hinglish else "Disabled"}
""")
else:
st.error(f" API Error: {response.status_code}")
if response.text:
st.error(f"**Error Details**: {response.text}")
except requests.exceptions.Timeout:
st.error(" Request timed out. Please try again later.")
except requests.exceptions.ConnectionError:
st.error("🌐 Connection error. Please check your internet connection.")
except Exception as e:
st.error(f" API call failed: {str(e)}")
# Show payload for debugging
with st.expander("πŸ› Debug Information", expanded=False):
st.warning("Request payload for debugging:")
st.json(payload)