Update app.py
Browse files
app.py
CHANGED
|
@@ -25,6 +25,10 @@ DEFAULT_USER_ID = int(os.getenv("DEFAULT_USER_ID", "30"))
|
|
| 25 |
DEFAULT_PERSONALIZATION_ID = int(os.getenv("DEFAULT_PERSONALIZATION_ID", "100"))
|
| 26 |
API_TIMEOUT = int(os.getenv("API_TIMEOUT", "30"))
|
| 27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
# Set page configuration
|
| 29 |
st.set_page_config(
|
| 30 |
page_title="Base Course Personalization",
|
|
@@ -50,6 +54,9 @@ st.markdown("""
|
|
| 50 |
--border-color: #444444;
|
| 51 |
--header-color: #4F97FF;
|
| 52 |
--subheader-color: #FFFFFF;
|
|
|
|
|
|
|
|
|
|
| 53 |
}
|
| 54 |
|
| 55 |
/* Main container styling */
|
|
@@ -65,6 +72,7 @@ st.markdown("""
|
|
| 65 |
color: var(--header-color);
|
| 66 |
margin-bottom: 1rem;
|
| 67 |
text-align: center;
|
|
|
|
| 68 |
}
|
| 69 |
|
| 70 |
.section-header {
|
|
@@ -74,31 +82,50 @@ st.markdown("""
|
|
| 74 |
margin-top: 2rem;
|
| 75 |
margin-bottom: 1rem;
|
| 76 |
padding-bottom: 0.5rem;
|
| 77 |
-
border-bottom:
|
|
|
|
|
|
|
|
|
|
| 78 |
}
|
| 79 |
|
| 80 |
/* Form container */
|
| 81 |
div[data-testid="stForm"] {
|
| 82 |
background-color: var(--card-background);
|
| 83 |
-
padding:
|
| 84 |
-
border-radius:
|
| 85 |
-
box-shadow: 0
|
|
|
|
|
|
|
| 86 |
}
|
| 87 |
|
| 88 |
/* Button styling */
|
| 89 |
.stButton>button {
|
| 90 |
-
background
|
| 91 |
color: white;
|
| 92 |
-
border-radius:
|
| 93 |
-
padding: 0.
|
| 94 |
-
font-weight:
|
| 95 |
border: none;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
}
|
| 97 |
|
| 98 |
-
/* Input fields */
|
| 99 |
div[data-baseweb="select"] > div {
|
| 100 |
background-color: var(--background-color);
|
| 101 |
-
border
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
}
|
| 103 |
|
| 104 |
.stTextInput > div > div > input,
|
|
@@ -106,64 +133,123 @@ st.markdown("""
|
|
| 106 |
.stTextArea > div > div > textarea {
|
| 107 |
background-color: var(--background-color);
|
| 108 |
color: var(--text-color);
|
| 109 |
-
border
|
|
|
|
|
|
|
|
|
|
| 110 |
}
|
| 111 |
|
| 112 |
-
|
| 113 |
-
.
|
| 114 |
-
|
|
|
|
|
|
|
| 115 |
}
|
| 116 |
|
| 117 |
-
|
| 118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
}
|
| 120 |
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
}
|
| 125 |
|
| 126 |
-
/*
|
| 127 |
-
div
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
color: var(--text-color) !important;
|
|
|
|
| 129 |
}
|
| 130 |
|
| 131 |
-
/*
|
| 132 |
-
.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
color: var(--text-color) !important;
|
|
|
|
|
|
|
| 134 |
}
|
| 135 |
|
| 136 |
-
/*
|
| 137 |
-
.
|
| 138 |
-
.st-emotion-cache-1aumxhf,
|
| 139 |
-
.st-emotion-cache-16txtl3 {
|
| 140 |
color: var(--text-color) !important;
|
|
|
|
| 141 |
}
|
| 142 |
|
| 143 |
/* Spacing */
|
| 144 |
div.block-container {
|
| 145 |
padding-top: 2rem;
|
|
|
|
| 146 |
}
|
| 147 |
|
| 148 |
hr {
|
| 149 |
-
margin
|
| 150 |
-
|
| 151 |
-
border-
|
| 152 |
}
|
| 153 |
|
| 154 |
/* API Response Container */
|
| 155 |
.api-response {
|
| 156 |
background-color: #1A1A1A;
|
| 157 |
-
border-radius:
|
| 158 |
-
padding:
|
| 159 |
border-left: 4px solid var(--accent-color);
|
|
|
|
| 160 |
}
|
| 161 |
|
| 162 |
.session-info {
|
| 163 |
background-color: var(--card-background);
|
| 164 |
-
padding:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
border-radius: 8px;
|
|
|
|
| 166 |
margin: 1rem 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
border: 1px solid var(--border-color);
|
| 168 |
}
|
| 169 |
</style>
|
|
@@ -177,54 +263,54 @@ if 'session_ids' not in st.session_state:
|
|
| 177 |
if 'authenticated' not in st.session_state:
|
| 178 |
st.session_state.authenticated = False
|
| 179 |
|
| 180 |
-
# Hardcoded credentials (you can change these)
|
| 181 |
-
VALID_USERNAME = "admin"
|
| 182 |
-
VALID_PASSWORD = "techlearn2024"
|
| 183 |
-
|
| 184 |
# Authentication check
|
| 185 |
if not st.session_state.authenticated:
|
| 186 |
st.markdown('<h1 class="main-header">π Login to Course Personalization</h1>', unsafe_allow_html=True)
|
| 187 |
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
with col2:
|
| 193 |
-
username = st.text_input("Username", placeholder="Enter username")
|
| 194 |
-
password = st.text_input("Password", placeholder="Enter password", type="password")
|
| 195 |
|
| 196 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
|
| 198 |
-
|
| 199 |
-
if username == VALID_USERNAME and password == VALID_PASSWORD:
|
| 200 |
-
st.session_state.authenticated = True
|
| 201 |
-
st.success("Login successful! Redirecting...")
|
| 202 |
-
time.sleep(1)
|
| 203 |
-
st.rerun()
|
| 204 |
-
else:
|
| 205 |
-
st.error(" Invalid username or password")
|
| 206 |
|
| 207 |
st.markdown("---")
|
| 208 |
-
st.markdown("""
|
| 209 |
<div style="text-align: center; color: #888888;">
|
| 210 |
-
<small>Demo Credentials:
|
| 211 |
</div>
|
| 212 |
""", unsafe_allow_html=True)
|
| 213 |
st.stop()
|
| 214 |
|
| 215 |
-
# Main app
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
with col3:
|
| 221 |
-
if st.button(" Logout"):
|
| 222 |
st.session_state.authenticated = False
|
| 223 |
st.session_state.topics_list = [{"topic_title": "What is Flask", "chapter_title": "Introduction to Flask"}]
|
| 224 |
st.session_state.session_ids = []
|
| 225 |
st.rerun()
|
| 226 |
|
| 227 |
-
|
| 228 |
# Main Form
|
| 229 |
with st.form("personalization_form", clear_on_submit=False):
|
| 230 |
# Topics Section
|
|
@@ -232,30 +318,59 @@ with st.form("personalization_form", clear_on_submit=False):
|
|
| 232 |
|
| 233 |
# Option to choose single or multiple topics
|
| 234 |
topic_mode = st.radio(
|
| 235 |
-
"Topic Mode",
|
| 236 |
["Single Topic", "Multiple Topics"],
|
| 237 |
horizontal=True,
|
| 238 |
help="Choose whether to process one topic or multiple topics"
|
| 239 |
)
|
| 240 |
|
| 241 |
if topic_mode == "Single Topic":
|
| 242 |
-
st.
|
| 243 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
|
| 245 |
else:
|
| 246 |
-
st.
|
| 247 |
|
| 248 |
-
# Display existing topics
|
| 249 |
for i, topic in enumerate(st.session_state.topics_list):
|
| 250 |
-
|
|
|
|
|
|
|
|
|
|
| 251 |
with col1:
|
| 252 |
-
topic_title = st.text_input(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
st.session_state.topics_list[i]["topic_title"] = topic_title
|
|
|
|
| 254 |
with col2:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
if len(st.session_state.topics_list) > 1:
|
| 256 |
-
|
|
|
|
| 257 |
st.session_state.topics_list.pop(i)
|
| 258 |
st.rerun()
|
|
|
|
|
|
|
| 259 |
|
| 260 |
# Language & Voice Settings Section
|
| 261 |
st.markdown('<div class="section-header">π£οΈ Language & Voice Settings</div>', unsafe_allow_html=True)
|
|
@@ -264,9 +379,10 @@ with st.form("personalization_form", clear_on_submit=False):
|
|
| 264 |
with col1:
|
| 265 |
target_language = st.selectbox(
|
| 266 |
"Target Language",
|
| 267 |
-
["english", "hindi", "
|
| 268 |
index=0,
|
| 269 |
-
format_func=lambda x: x.capitalize()
|
|
|
|
| 270 |
)
|
| 271 |
|
| 272 |
with col2:
|
|
@@ -274,15 +390,17 @@ with st.form("personalization_form", clear_on_submit=False):
|
|
| 274 |
"Voice Gender",
|
| 275 |
["male", "female"],
|
| 276 |
index=0,
|
| 277 |
-
format_func=lambda x: x.capitalize()
|
|
|
|
| 278 |
)
|
| 279 |
|
| 280 |
with col3:
|
| 281 |
tts_voice = st.selectbox(
|
| 282 |
"Voice Style",
|
| 283 |
["onyx", "echo", "soprano", "alto", "robotic"],
|
| 284 |
-
index=0,
|
| 285 |
-
format_func=lambda x: x.capitalize()
|
|
|
|
| 286 |
)
|
| 287 |
|
| 288 |
# Technical Settings Section
|
|
@@ -294,40 +412,59 @@ with st.form("personalization_form", clear_on_submit=False):
|
|
| 294 |
"Programming Language",
|
| 295 |
["python", "java", "javascript", "c++", "go"],
|
| 296 |
index=0,
|
| 297 |
-
format_func=lambda x: x.capitalize()
|
|
|
|
| 298 |
)
|
| 299 |
with col2:
|
| 300 |
-
|
|
|
|
| 301 |
|
| 302 |
# Submit button
|
| 303 |
st.markdown("<br>", unsafe_allow_html=True)
|
| 304 |
col1, col2, col3 = st.columns([1, 2, 1])
|
| 305 |
with col2:
|
| 306 |
-
submitted = st.form_submit_button("Generate
|
| 307 |
|
| 308 |
# Add/Remove topic buttons outside the form (for multiple topics)
|
| 309 |
if topic_mode == "Multiple Topics":
|
| 310 |
-
|
|
|
|
| 311 |
with col1:
|
| 312 |
-
if st.button(" Add
|
| 313 |
-
st.session_state.topics_list.append({
|
|
|
|
|
|
|
|
|
|
| 314 |
st.rerun()
|
| 315 |
with col2:
|
| 316 |
-
if st.button(" Reset
|
| 317 |
-
st.session_state.topics_list = [{"topic_title": "What is Flask"}]
|
| 318 |
st.rerun()
|
|
|
|
| 319 |
|
| 320 |
# Handle submission
|
| 321 |
if submitted:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 322 |
# Validate inputs
|
| 323 |
-
valid_topics = [
|
|
|
|
|
|
|
|
|
|
| 324 |
|
| 325 |
if not valid_topics:
|
| 326 |
-
st.error("Please enter at least one topic with both topic title and chapter title")
|
| 327 |
else:
|
| 328 |
-
# Show loading state
|
| 329 |
-
with st.spinner("Generating your personalized course..."):
|
| 330 |
-
|
|
|
|
|
|
|
|
|
|
| 331 |
|
| 332 |
# Hardcoded values
|
| 333 |
course_id = DEFAULT_COURSE_ID
|
|
@@ -405,53 +542,44 @@ if submitted:
|
|
| 405 |
response_data = response.json()
|
| 406 |
session_ids = response_data.get("session_ids", [])
|
| 407 |
|
| 408 |
-
st.success(
|
| 409 |
|
| 410 |
# Store session IDs in session state
|
| 411 |
st.session_state.session_ids.extend(session_ids)
|
| 412 |
|
| 413 |
# Display results in a clean format
|
| 414 |
-
st.markdown("###
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
|
| 423 |
-
# Display session IDs
|
| 424 |
-
if session_ids:
|
| 425 |
-
st.markdown("### π Session IDs for Tracking")
|
| 426 |
-
for i, session_id in enumerate(session_ids, 1):
|
| 427 |
-
st.markdown(f'<div class="session-info"><strong>Session {i}:</strong> <code>{session_id}</code></div>', unsafe_allow_html=True)
|
| 428 |
|
| 429 |
-
# Display API response
|
| 430 |
-
st.markdown('<div class="section-header">π API Response</div>', unsafe_allow_html=True)
|
| 431 |
-
st.markdown('<div class="api-response">', unsafe_allow_html=True)
|
| 432 |
-
st.json(response_data)
|
| 433 |
-
st.markdown('</div>', unsafe_allow_html=True)
|
| 434 |
|
| 435 |
-
# Information about tracking
|
| 436 |
-
st.info(f"""
|
| 437 |
-
π‘ **Tracking Information**
|
| 438 |
-
You can track the progress of your video generation using the session IDs above.
|
| 439 |
-
The processing status will be updated in DynamoDB table: `{SESSION_TABLE}`
|
| 440 |
-
Region: `{DYNAMODB_REGION}`
|
| 441 |
-
""")
|
| 442 |
|
| 443 |
else:
|
| 444 |
-
st.error(f"API Error: {response.status_code}")
|
| 445 |
if response.text:
|
| 446 |
-
st.error(f"Error Details
|
| 447 |
|
| 448 |
except requests.exceptions.Timeout:
|
| 449 |
-
st.error("Request timed out. Please try again later.")
|
| 450 |
except requests.exceptions.ConnectionError:
|
| 451 |
-
st.error("Connection error. Please check your internet connection.")
|
| 452 |
except Exception as e:
|
| 453 |
-
st.error(f"API call failed: {str(e)}")
|
| 454 |
|
| 455 |
# Show payload for debugging
|
| 456 |
-
st.
|
| 457 |
-
|
|
|
|
|
|
| 25 |
DEFAULT_PERSONALIZATION_ID = int(os.getenv("DEFAULT_PERSONALIZATION_ID", "100"))
|
| 26 |
API_TIMEOUT = int(os.getenv("API_TIMEOUT", "30"))
|
| 27 |
|
| 28 |
+
# Authentication credentials from environment variables
|
| 29 |
+
VALID_USERNAME = os.getenv("APP_USERNAME")
|
| 30 |
+
VALID_PASSWORD = os.getenv("APP_PASSWORD")
|
| 31 |
+
|
| 32 |
# Set page configuration
|
| 33 |
st.set_page_config(
|
| 34 |
page_title="Base Course Personalization",
|
|
|
|
| 54 |
--border-color: #444444;
|
| 55 |
--header-color: #4F97FF;
|
| 56 |
--subheader-color: #FFFFFF;
|
| 57 |
+
--success-color: #10B981;
|
| 58 |
+
--warning-color: #F59E0B;
|
| 59 |
+
--error-color: #EF4444;
|
| 60 |
}
|
| 61 |
|
| 62 |
/* Main container styling */
|
|
|
|
| 72 |
color: var(--header-color);
|
| 73 |
margin-bottom: 1rem;
|
| 74 |
text-align: center;
|
| 75 |
+
text-shadow: 0 2px 4px rgba(79, 151, 255, 0.3);
|
| 76 |
}
|
| 77 |
|
| 78 |
.section-header {
|
|
|
|
| 82 |
margin-top: 2rem;
|
| 83 |
margin-bottom: 1rem;
|
| 84 |
padding-bottom: 0.5rem;
|
| 85 |
+
border-bottom: 2px solid var(--border-color);
|
| 86 |
+
display: flex;
|
| 87 |
+
align-items: center;
|
| 88 |
+
gap: 0.5rem;
|
| 89 |
}
|
| 90 |
|
| 91 |
/* Form container */
|
| 92 |
div[data-testid="stForm"] {
|
| 93 |
background-color: var(--card-background);
|
| 94 |
+
padding: 2rem;
|
| 95 |
+
border-radius: 12px;
|
| 96 |
+
box-shadow: 0 8px 25px rgba(0,0,0,0.4);
|
| 97 |
+
border: 1px solid var(--border-color);
|
| 98 |
+
margin-bottom: 1rem;
|
| 99 |
}
|
| 100 |
|
| 101 |
/* Button styling */
|
| 102 |
.stButton>button {
|
| 103 |
+
background: linear-gradient(135deg, var(--accent-color) 0%, #3B82F6 100%);
|
| 104 |
color: white;
|
| 105 |
+
border-radius: 8px;
|
| 106 |
+
padding: 0.75rem 2rem;
|
| 107 |
+
font-weight: 600;
|
| 108 |
border: none;
|
| 109 |
+
transition: all 0.3s ease;
|
| 110 |
+
box-shadow: 0 4px 15px rgba(79, 151, 255, 0.3);
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
.stButton>button:hover {
|
| 114 |
+
transform: translateY(-2px);
|
| 115 |
+
box-shadow: 0 6px 20px rgba(79, 151, 255, 0.4);
|
| 116 |
}
|
| 117 |
|
| 118 |
+
/* Input fields styling */
|
| 119 |
div[data-baseweb="select"] > div {
|
| 120 |
background-color: var(--background-color);
|
| 121 |
+
border: 2px solid var(--border-color);
|
| 122 |
+
border-radius: 8px;
|
| 123 |
+
transition: border-color 0.3s ease;
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
div[data-baseweb="select"] > div:focus-within {
|
| 127 |
+
border-color: var(--accent-color);
|
| 128 |
+
box-shadow: 0 0 0 3px rgba(79, 151, 255, 0.1);
|
| 129 |
}
|
| 130 |
|
| 131 |
.stTextInput > div > div > input,
|
|
|
|
| 133 |
.stTextArea > div > div > textarea {
|
| 134 |
background-color: var(--background-color);
|
| 135 |
color: var(--text-color);
|
| 136 |
+
border: 2px solid var(--border-color);
|
| 137 |
+
border-radius: 8px;
|
| 138 |
+
padding: 0.75rem;
|
| 139 |
+
transition: border-color 0.3s ease;
|
| 140 |
}
|
| 141 |
|
| 142 |
+
.stTextInput > div > div > input:focus,
|
| 143 |
+
.stNumberInput > div > div > input:focus,
|
| 144 |
+
.stTextArea > div > div > textarea:focus {
|
| 145 |
+
border-color: var(--accent-color);
|
| 146 |
+
box-shadow: 0 0 0 3px rgba(79, 151, 255, 0.1);
|
| 147 |
}
|
| 148 |
|
| 149 |
+
/* Topic container styling */
|
| 150 |
+
.topic-container {
|
| 151 |
+
background-color: var(--card-background);
|
| 152 |
+
padding: 1.5rem;
|
| 153 |
+
border-radius: 10px;
|
| 154 |
+
margin-bottom: 1rem;
|
| 155 |
+
border: 1px solid var(--border-color);
|
| 156 |
+
position: relative;
|
| 157 |
}
|
| 158 |
|
| 159 |
+
.topic-header {
|
| 160 |
+
display: flex;
|
| 161 |
+
align-items: center;
|
| 162 |
+
gap: 0.5rem;
|
| 163 |
+
margin-bottom: 1rem;
|
| 164 |
+
font-weight: 600;
|
| 165 |
+
color: var(--accent-color);
|
| 166 |
}
|
| 167 |
|
| 168 |
+
/* Radio buttons */
|
| 169 |
+
.stRadio > div {
|
| 170 |
+
background-color: transparent;
|
| 171 |
+
gap: 1rem;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
.stRadio > div > label > div {
|
| 175 |
color: var(--text-color) !important;
|
| 176 |
+
font-weight: 500;
|
| 177 |
}
|
| 178 |
|
| 179 |
+
/* Labels */
|
| 180 |
+
.stSelectbox > label,
|
| 181 |
+
.stTextInput > label,
|
| 182 |
+
.stNumberInput > label,
|
| 183 |
+
.stTextArea > label,
|
| 184 |
+
.stRadio > label {
|
| 185 |
color: var(--text-color) !important;
|
| 186 |
+
font-weight: 500;
|
| 187 |
+
margin-bottom: 0.5rem;
|
| 188 |
}
|
| 189 |
|
| 190 |
+
/* Toggle switch */
|
| 191 |
+
.stToggle > label {
|
|
|
|
|
|
|
| 192 |
color: var(--text-color) !important;
|
| 193 |
+
font-weight: 500;
|
| 194 |
}
|
| 195 |
|
| 196 |
/* Spacing */
|
| 197 |
div.block-container {
|
| 198 |
padding-top: 2rem;
|
| 199 |
+
max-width: 1200px;
|
| 200 |
}
|
| 201 |
|
| 202 |
hr {
|
| 203 |
+
margin: 2rem 0;
|
| 204 |
+
border: none;
|
| 205 |
+
border-top: 1px solid var(--border-color);
|
| 206 |
}
|
| 207 |
|
| 208 |
/* API Response Container */
|
| 209 |
.api-response {
|
| 210 |
background-color: #1A1A1A;
|
| 211 |
+
border-radius: 10px;
|
| 212 |
+
padding: 1.5rem;
|
| 213 |
border-left: 4px solid var(--accent-color);
|
| 214 |
+
margin: 1rem 0;
|
| 215 |
}
|
| 216 |
|
| 217 |
.session-info {
|
| 218 |
background-color: var(--card-background);
|
| 219 |
+
padding: 1.5rem;
|
| 220 |
+
border-radius: 10px;
|
| 221 |
+
margin: 1rem 0;
|
| 222 |
+
border: 1px solid var(--border-color);
|
| 223 |
+
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
/* Action buttons container */
|
| 227 |
+
.action-buttons {
|
| 228 |
+
display: flex;
|
| 229 |
+
gap: 1rem;
|
| 230 |
+
margin: 1rem 0;
|
| 231 |
+
justify-content: flex-start;
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
/* Small action buttons */
|
| 235 |
+
.small-button {
|
| 236 |
+
padding: 0.5rem 1rem;
|
| 237 |
+
font-size: 0.875rem;
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
/* Success/Error styling */
|
| 241 |
+
.stSuccess, .stError, .stWarning, .stInfo {
|
| 242 |
border-radius: 8px;
|
| 243 |
+
padding: 1rem;
|
| 244 |
margin: 1rem 0;
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
/* Login container */
|
| 248 |
+
.login-container {
|
| 249 |
+
background-color: var(--card-background);
|
| 250 |
+
padding: 3rem;
|
| 251 |
+
border-radius: 15px;
|
| 252 |
+
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
|
| 253 |
border: 1px solid var(--border-color);
|
| 254 |
}
|
| 255 |
</style>
|
|
|
|
| 263 |
if 'authenticated' not in st.session_state:
|
| 264 |
st.session_state.authenticated = False
|
| 265 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
# Authentication check
|
| 267 |
if not st.session_state.authenticated:
|
| 268 |
st.markdown('<h1 class="main-header">π Login to Course Personalization</h1>', unsafe_allow_html=True)
|
| 269 |
|
| 270 |
+
col1, col2, col3 = st.columns([1, 2, 1])
|
| 271 |
+
with col2:
|
| 272 |
+
with st.container():
|
| 273 |
+
st.markdown('<div class="login-container">', unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
| 274 |
|
| 275 |
+
with st.form("login_form"):
|
| 276 |
+
st.markdown('<div class="section-header">π Authentication Required</div>', unsafe_allow_html=True)
|
| 277 |
+
|
| 278 |
+
username = st.text_input("Username", placeholder="Enter username", key="login_username")
|
| 279 |
+
password = st.text_input("Password", placeholder="Enter password", type="password", key="login_password")
|
| 280 |
+
|
| 281 |
+
login_submitted = st.form_submit_button("π Login", use_container_width=True)
|
| 282 |
+
|
| 283 |
+
if login_submitted:
|
| 284 |
+
if username == VALID_USERNAME and password == VALID_PASSWORD:
|
| 285 |
+
st.session_state.authenticated = True
|
| 286 |
+
st.success("Login successful! Redirecting...")
|
| 287 |
+
time.sleep(1)
|
| 288 |
+
st.rerun()
|
| 289 |
+
else:
|
| 290 |
+
st.error("β Invalid username or password")
|
| 291 |
|
| 292 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 293 |
|
| 294 |
st.markdown("---")
|
| 295 |
+
st.markdown(f"""
|
| 296 |
<div style="text-align: center; color: #888888;">
|
| 297 |
+
<small>Demo Credentials: {VALID_USERNAME} / {VALID_PASSWORD if len(VALID_PASSWORD) <= 10 else VALID_PASSWORD[:3] + '...'}</small>
|
| 298 |
</div>
|
| 299 |
""", unsafe_allow_html=True)
|
| 300 |
st.stop()
|
| 301 |
|
| 302 |
+
# Main app header
|
| 303 |
+
st.markdown('<h1 class="main-header">π Base Course Personalization</h1>', unsafe_allow_html=True)
|
| 304 |
+
|
| 305 |
+
# Logout button in top right
|
| 306 |
+
col1, col2, col3 = st.columns([2, 1, 1])
|
| 307 |
with col3:
|
| 308 |
+
if st.button("π Logout", key="logout_btn"):
|
| 309 |
st.session_state.authenticated = False
|
| 310 |
st.session_state.topics_list = [{"topic_title": "What is Flask", "chapter_title": "Introduction to Flask"}]
|
| 311 |
st.session_state.session_ids = []
|
| 312 |
st.rerun()
|
| 313 |
|
|
|
|
| 314 |
# Main Form
|
| 315 |
with st.form("personalization_form", clear_on_submit=False):
|
| 316 |
# Topics Section
|
|
|
|
| 318 |
|
| 319 |
# Option to choose single or multiple topics
|
| 320 |
topic_mode = st.radio(
|
| 321 |
+
"Select Topic Mode",
|
| 322 |
["Single Topic", "Multiple Topics"],
|
| 323 |
horizontal=True,
|
| 324 |
help="Choose whether to process one topic or multiple topics"
|
| 325 |
)
|
| 326 |
|
| 327 |
if topic_mode == "Single Topic":
|
| 328 |
+
st.markdown('<div class="topic-container">', unsafe_allow_html=True)
|
| 329 |
+
st.markdown('<div class="topic-header">π Single Topic Configuration</div>', unsafe_allow_html=True)
|
| 330 |
+
|
| 331 |
+
col1, col2 = st.columns(2)
|
| 332 |
+
with col1:
|
| 333 |
+
single_topic_title = st.text_input("Topic Title", value="What is Flask", help="Enter the topic title", key="single_topic")
|
| 334 |
+
with col2:
|
| 335 |
+
single_chapter_title = st.text_input("Chapter Title", value="Introduction to Flask", help="Enter the chapter title", key="single_chapter")
|
| 336 |
+
|
| 337 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 338 |
|
| 339 |
else:
|
| 340 |
+
st.markdown("### π Multiple Topics Configuration")
|
| 341 |
|
| 342 |
+
# Display existing topics with better styling
|
| 343 |
for i, topic in enumerate(st.session_state.topics_list):
|
| 344 |
+
st.markdown(f'<div class="topic-container">', unsafe_allow_html=True)
|
| 345 |
+
st.markdown(f'<div class="topic-header">π Topic {i+1}</div>', unsafe_allow_html=True)
|
| 346 |
+
|
| 347 |
+
col1, col2, col3 = st.columns([3, 3, 1])
|
| 348 |
with col1:
|
| 349 |
+
topic_title = st.text_input(
|
| 350 |
+
"Topic Title",
|
| 351 |
+
value=topic["topic_title"],
|
| 352 |
+
key=f"topic_title_{i}",
|
| 353 |
+
help="Enter the topic title"
|
| 354 |
+
)
|
| 355 |
st.session_state.topics_list[i]["topic_title"] = topic_title
|
| 356 |
+
|
| 357 |
with col2:
|
| 358 |
+
chapter_title = st.text_input(
|
| 359 |
+
"Chapter Title",
|
| 360 |
+
value=topic["chapter_title"],
|
| 361 |
+
key=f"chapter_title_{i}",
|
| 362 |
+
help="Enter the chapter title"
|
| 363 |
+
)
|
| 364 |
+
st.session_state.topics_list[i]["chapter_title"] = chapter_title
|
| 365 |
+
|
| 366 |
+
with col3:
|
| 367 |
if len(st.session_state.topics_list) > 1:
|
| 368 |
+
st.markdown("<br>", unsafe_allow_html=True) # Add spacing
|
| 369 |
+
if st.button("ποΈ", key=f"remove_{i}", help="Remove this topic"):
|
| 370 |
st.session_state.topics_list.pop(i)
|
| 371 |
st.rerun()
|
| 372 |
+
|
| 373 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 374 |
|
| 375 |
# Language & Voice Settings Section
|
| 376 |
st.markdown('<div class="section-header">π£οΈ Language & Voice Settings</div>', unsafe_allow_html=True)
|
|
|
|
| 379 |
with col1:
|
| 380 |
target_language = st.selectbox(
|
| 381 |
"Target Language",
|
| 382 |
+
["english", "hindi", "marathi", "kannada", "punjabi","gujarati"],
|
| 383 |
index=0,
|
| 384 |
+
format_func=lambda x: x.capitalize(),
|
| 385 |
+
help="Select the target language for content generation"
|
| 386 |
)
|
| 387 |
|
| 388 |
with col2:
|
|
|
|
| 390 |
"Voice Gender",
|
| 391 |
["male", "female"],
|
| 392 |
index=0,
|
| 393 |
+
format_func=lambda x: x.capitalize(),
|
| 394 |
+
help="Select the voice gender for text-to-speech"
|
| 395 |
)
|
| 396 |
|
| 397 |
with col3:
|
| 398 |
tts_voice = st.selectbox(
|
| 399 |
"Voice Style",
|
| 400 |
["onyx", "echo", "soprano", "alto", "robotic"],
|
| 401 |
+
index=0,
|
| 402 |
+
format_func=lambda x: x.capitalize(),
|
| 403 |
+
help="Select the voice style for text-to-speech"
|
| 404 |
)
|
| 405 |
|
| 406 |
# Technical Settings Section
|
|
|
|
| 412 |
"Programming Language",
|
| 413 |
["python", "java", "javascript", "c++", "go"],
|
| 414 |
index=0,
|
| 415 |
+
format_func=lambda x: x.capitalize(),
|
| 416 |
+
help="Select the primary programming language for examples"
|
| 417 |
)
|
| 418 |
with col2:
|
| 419 |
+
st.markdown("<br>", unsafe_allow_html=True) # Add spacing
|
| 420 |
+
toggle_hinglish = st.toggle("Enable Hinglish", value=True, help="Enable mixing of Hindi and English")
|
| 421 |
|
| 422 |
# Submit button
|
| 423 |
st.markdown("<br>", unsafe_allow_html=True)
|
| 424 |
col1, col2, col3 = st.columns([1, 2, 1])
|
| 425 |
with col2:
|
| 426 |
+
submitted = st.form_submit_button("π Generate Base Course", use_container_width=True)
|
| 427 |
|
| 428 |
# Add/Remove topic buttons outside the form (for multiple topics)
|
| 429 |
if topic_mode == "Multiple Topics":
|
| 430 |
+
st.markdown('<div class="action-buttons">', unsafe_allow_html=True)
|
| 431 |
+
col1, col2, col3, col4 = st.columns([2, 2, 2, 4])
|
| 432 |
with col1:
|
| 433 |
+
if st.button(" Add Topic", key="add_topic", help="Add a new topic"):
|
| 434 |
+
st.session_state.topics_list.append({
|
| 435 |
+
"topic_title": f"Topic {len(st.session_state.topics_list) + 1}",
|
| 436 |
+
"chapter_title": f"Chapter {len(st.session_state.topics_list) + 1}"
|
| 437 |
+
})
|
| 438 |
st.rerun()
|
| 439 |
with col2:
|
| 440 |
+
if st.button(" Reset All", key="reset_topics", help="Reset to default topics"):
|
| 441 |
+
st.session_state.topics_list = [{"topic_title": "What is Flask", "chapter_title": "Introduction to Flask"}]
|
| 442 |
st.rerun()
|
| 443 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 444 |
|
| 445 |
# Handle submission
|
| 446 |
if submitted:
|
| 447 |
+
# Determine which topics to use based on mode
|
| 448 |
+
if topic_mode == "Single Topic":
|
| 449 |
+
topics_to_process = [{"topic_title": single_topic_title, "chapter_title": single_chapter_title}]
|
| 450 |
+
else:
|
| 451 |
+
topics_to_process = st.session_state.topics_list
|
| 452 |
+
|
| 453 |
# Validate inputs
|
| 454 |
+
valid_topics = []
|
| 455 |
+
for topic in topics_to_process:
|
| 456 |
+
if topic["topic_title"].strip() and topic["chapter_title"].strip():
|
| 457 |
+
valid_topics.append(topic)
|
| 458 |
|
| 459 |
if not valid_topics:
|
| 460 |
+
st.error("β Please enter at least one topic with both topic title and chapter title")
|
| 461 |
else:
|
| 462 |
+
# Show loading state with better animation
|
| 463 |
+
with st.spinner("π¬ Generating your personalized course... This may take a few moments."):
|
| 464 |
+
progress_bar = st.progress(0)
|
| 465 |
+
for i in range(100):
|
| 466 |
+
time.sleep(0.02) # Simulating progress
|
| 467 |
+
progress_bar.progress(i + 1)
|
| 468 |
|
| 469 |
# Hardcoded values
|
| 470 |
course_id = DEFAULT_COURSE_ID
|
|
|
|
| 542 |
response_data = response.json()
|
| 543 |
session_ids = response_data.get("session_ids", [])
|
| 544 |
|
| 545 |
+
st.success(" Base Course started successfully!")
|
| 546 |
|
| 547 |
# Store session IDs in session state
|
| 548 |
st.session_state.session_ids.extend(session_ids)
|
| 549 |
|
| 550 |
# Display results in a clean format
|
| 551 |
+
st.markdown("### Generation Summary")
|
| 552 |
+
|
| 553 |
+
col1, col2 = st.columns(2)
|
| 554 |
+
with col1:
|
| 555 |
+
st.markdown(f"""
|
| 556 |
+
**Course ID**: `{course_id}`
|
| 557 |
+
**Programming Language**: {programming_language.capitalize()}
|
| 558 |
+
**Target Language**: {target_language.capitalize()}
|
| 559 |
+
""")
|
| 560 |
+
with col2:
|
| 561 |
+
st.markdown(f"""
|
| 562 |
+
**Voice**: {tts_voice.capitalize()} ({tts_gender.capitalize()})
|
| 563 |
+
**Topics Count**: {len(valid_topics)}
|
| 564 |
+
**Hinglish**: {"Enabled" if toggle_hinglish else "Disabled"}
|
| 565 |
+
""")
|
| 566 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 567 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 568 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 569 |
|
| 570 |
else:
|
| 571 |
+
st.error(f" API Error: {response.status_code}")
|
| 572 |
if response.text:
|
| 573 |
+
st.error(f"**Error Details**: {response.text}")
|
| 574 |
|
| 575 |
except requests.exceptions.Timeout:
|
| 576 |
+
st.error(" Request timed out. Please try again later.")
|
| 577 |
except requests.exceptions.ConnectionError:
|
| 578 |
+
st.error("π Connection error. Please check your internet connection.")
|
| 579 |
except Exception as e:
|
| 580 |
+
st.error(f" API call failed: {str(e)}")
|
| 581 |
|
| 582 |
# Show payload for debugging
|
| 583 |
+
with st.expander("π Debug Information", expanded=False):
|
| 584 |
+
st.warning("Request payload for debugging:")
|
| 585 |
+
st.json(payload)
|