|
|
import streamlit as st |
|
|
import json |
|
|
from datetime import datetime |
|
|
import time |
|
|
import requests |
|
|
import boto3 |
|
|
import uuid |
|
|
|
|
|
|
|
|
API_ENDPOINT = "https://oau6sljd4l.execute-api.ap-south-1.amazonaws.com/production/process" |
|
|
|
|
|
|
|
|
DYNAMODB_REGION = "ap-south-1" |
|
|
SESSION_TABLE = "SessionTracking" |
|
|
|
|
|
|
|
|
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 |
|
|
} |
|
|
) |
|
|
|
|
|
|
|
|
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; |
|
|
} |
|
|
|
|
|
/* 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; |
|
|
} |
|
|
|
|
|
.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: 1px solid var(--border-color); |
|
|
} |
|
|
|
|
|
/* Form container */ |
|
|
div[data-testid="stForm"] { |
|
|
background-color: var(--card-background); |
|
|
padding: 1.5rem; |
|
|
border-radius: 8px; |
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.3); |
|
|
} |
|
|
|
|
|
/* Button styling */ |
|
|
.stButton>button { |
|
|
background-color: var(--accent-color); |
|
|
color: white; |
|
|
border-radius: 4px; |
|
|
padding: 0.5rem 2rem; |
|
|
font-weight: 500; |
|
|
border: none; |
|
|
} |
|
|
|
|
|
/* Input fields */ |
|
|
div[data-baseweb="select"] > div { |
|
|
background-color: var(--background-color); |
|
|
border-color: var(--border-color); |
|
|
} |
|
|
|
|
|
.stTextInput > div > div > input, |
|
|
.stNumberInput > div > div > input, |
|
|
.stTextArea > div > div > textarea { |
|
|
background-color: var(--background-color); |
|
|
color: var(--text-color); |
|
|
border-color: var(--border-color); |
|
|
} |
|
|
|
|
|
/* Radio buttons */ |
|
|
.stRadio > div { |
|
|
background-color: transparent; |
|
|
} |
|
|
|
|
|
/* Spacing */ |
|
|
div.block-container { |
|
|
padding-top: 2rem; |
|
|
} |
|
|
|
|
|
hr { |
|
|
margin-top: 1rem; |
|
|
margin-bottom: 1rem; |
|
|
border-color: var(--border-color); |
|
|
} |
|
|
|
|
|
/* API Response Container */ |
|
|
.api-response { |
|
|
background-color: #1A1A1A; |
|
|
border-radius: 8px; |
|
|
padding: 1rem; |
|
|
border-left: 4px solid var(--accent-color); |
|
|
} |
|
|
|
|
|
.session-info { |
|
|
background-color: var(--card-background); |
|
|
padding: 1rem; |
|
|
border-radius: 8px; |
|
|
margin: 1rem 0; |
|
|
border: 1px solid var(--border-color); |
|
|
} |
|
|
</style> |
|
|
""", unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
if 'session_ids' not in st.session_state: |
|
|
st.session_state.session_ids = [] |
|
|
|
|
|
|
|
|
st.markdown('<h1 class="main-header">π Base Course Personalization</h1>', unsafe_allow_html=True) |
|
|
st.markdown(""" |
|
|
<div style="text-align: center; margin-bottom: 2rem;"> |
|
|
Complete this form to tailor your learning experience to your preferences.<br> |
|
|
Your personalized course will be generated based on the information you provide below. |
|
|
</div> |
|
|
""", unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
with st.form("personalization_form", clear_on_submit=False): |
|
|
|
|
|
st.markdown('<div class="section-header">π Course Information</div>', unsafe_allow_html=True) |
|
|
|
|
|
col1, col2 = st.columns(2) |
|
|
with col1: |
|
|
course_id = st.number_input("Course ID", min_value=1, value=47, help="Enter the course ID") |
|
|
user_id = st.number_input("User ID", min_value=1, value=30, help="Enter your user ID") |
|
|
|
|
|
with col2: |
|
|
personalization_id = st.number_input("Personalization ID", min_value=1, value=100, help="Enter personalization ID") |
|
|
|
|
|
|
|
|
st.markdown('<div class="section-header">π Topics Configuration</div>', unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
topic_mode = st.radio( |
|
|
"Topic Mode", |
|
|
["Single Topic", "Multiple Topics"], |
|
|
horizontal=True, |
|
|
help="Choose whether to process one topic or multiple topics" |
|
|
) |
|
|
|
|
|
if topic_mode == "Single Topic": |
|
|
st.subheader("Single Topic Details") |
|
|
col1, col2 = st.columns(2) |
|
|
with col1: |
|
|
single_topic_id = st.number_input("Topic ID", min_value=1, value=10834) |
|
|
single_topic_title = st.text_input("Topic Title", value="Setting Up Flask Environment") |
|
|
with col2: |
|
|
single_chapter_id = st.number_input("Chapter ID", min_value=1, value=647) |
|
|
single_chapter_title = st.text_input("Chapter Title", value="Introduction to Flask") |
|
|
|
|
|
single_video_url = st.text_input( |
|
|
"Video URL", |
|
|
value="https://techlearn-dev.s3.ap-south-1.amazonaws.com/course_videos/47/647/1729064365.mp4" |
|
|
) |
|
|
col1, col2 = st.columns(2) |
|
|
with col1: |
|
|
single_video_duration = st.number_input("Video Duration (seconds)", min_value=1, value=462) |
|
|
with col2: |
|
|
single_sequence_number = st.number_input("Sequence Number", min_value=1, value=1) |
|
|
|
|
|
else: |
|
|
st.subheader("Multiple Topics") |
|
|
num_topics = st.number_input("Number of Topics", min_value=1, max_value=10, value=3) |
|
|
|
|
|
topics_data = [] |
|
|
for i in range(num_topics): |
|
|
with st.expander(f"Topic {i+1}"): |
|
|
col1, col2 = st.columns(2) |
|
|
with col1: |
|
|
topic_id = st.number_input(f"Topic ID {i+1}", min_value=1, value=10833+i, key=f"topic_id_{i}") |
|
|
topic_title = st.text_input(f"Topic Title {i+1}", |
|
|
value=["What is Flask", "Setting Up Flask Environment", "Understanding Flask Project Structure"][i] if i < 3 else f"Topic {i+1}", |
|
|
key=f"topic_title_{i}") |
|
|
with col2: |
|
|
chapter_id = st.number_input(f"Chapter ID {i+1}", min_value=1, value=647, key=f"chapter_id_{i}") |
|
|
chapter_title = st.text_input(f"Chapter Title {i+1}", value="Introduction to Flask", key=f"chapter_title_{i}") |
|
|
|
|
|
video_url = st.text_input(f"Video URL {i+1}", |
|
|
value=f"https://techlearn-dev.s3.ap-south-1.amazonaws.com/course_videos/47/647/172906{4322+i*50}.mp4", |
|
|
key=f"video_url_{i}") |
|
|
col1, col2 = st.columns(2) |
|
|
with col1: |
|
|
video_duration = st.number_input(f"Video Duration {i+1} (seconds)", min_value=1, value=410+i*20, key=f"video_duration_{i}") |
|
|
with col2: |
|
|
sequence_number = st.number_input(f"Sequence Number {i+1}", min_value=1, value=i+1, key=f"sequence_number_{i}") |
|
|
|
|
|
topics_data.append({ |
|
|
"topic_id": topic_id, |
|
|
"topic_title": topic_title, |
|
|
"chapter_id": chapter_id, |
|
|
"chapter_title": chapter_title, |
|
|
"video_url": video_url, |
|
|
"video_duration": video_duration, |
|
|
"sequence_number": sequence_number, |
|
|
"course_id": course_id |
|
|
}) |
|
|
|
|
|
|
|
|
st.markdown('<div class="section-header">π€ User Profile</div>', unsafe_allow_html=True) |
|
|
|
|
|
col1, col2 = st.columns(2) |
|
|
with col1: |
|
|
user_name = st.text_input("Full Name", placeholder="Enter your full name", value="John Doe") |
|
|
user_age = st.number_input("Age", min_value=10, max_value=100, value=22) |
|
|
user_gender = st.selectbox( |
|
|
"Gender", |
|
|
["male", "female", "other"], |
|
|
index=0, |
|
|
format_func=lambda x: x.capitalize() |
|
|
) |
|
|
|
|
|
with col2: |
|
|
tech_knowledge = st.select_slider( |
|
|
"Technical Knowledge", |
|
|
options=["beginner", "intermediate", "advanced"], |
|
|
value="beginner" |
|
|
) |
|
|
learning_style = st.radio( |
|
|
"Learning Style", |
|
|
["visual", "auditory", "kinesthetic"], |
|
|
index=0, |
|
|
horizontal=True, |
|
|
format_func=lambda x: x.capitalize() |
|
|
) |
|
|
|
|
|
|
|
|
st.markdown('<div class="section-header">π― Preferences</div>', unsafe_allow_html=True) |
|
|
col1, col2 = st.columns(2) |
|
|
|
|
|
with col1: |
|
|
preferred_activity = st.text_area( |
|
|
"Preferred Activities", |
|
|
placeholder="e.g., movies, painting, shopping", |
|
|
value="movies, painting, shopping", |
|
|
help="Enter activities separated by commas" |
|
|
) |
|
|
food = st.text_area( |
|
|
"Food Preferences", |
|
|
placeholder="e.g., vegetarian, paneer, pizza", |
|
|
value="vegetarian, paneer, pizza", |
|
|
help="Enter food items separated by commas" |
|
|
) |
|
|
|
|
|
with col2: |
|
|
physical_activities = st.text_area( |
|
|
"Physical Activities", |
|
|
placeholder="e.g., badminton, cricket, yoga", |
|
|
value="badminton, cricket, yoga", |
|
|
help="Enter activities separated by commas" |
|
|
) |
|
|
|
|
|
|
|
|
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", "telugu", "tamil", "other"], |
|
|
index=0, |
|
|
format_func=lambda x: x.capitalize() |
|
|
) |
|
|
|
|
|
with col2: |
|
|
tts_gender = st.selectbox( |
|
|
"Voice Gender", |
|
|
["male", "female"], |
|
|
index=0, |
|
|
format_func=lambda x: x.capitalize() |
|
|
) |
|
|
|
|
|
with col3: |
|
|
tts_voice = st.selectbox( |
|
|
"Voice Style", |
|
|
["echo", "soprano", "alto", "robotic", "onyx"], |
|
|
index=4, |
|
|
format_func=lambda x: x.capitalize() |
|
|
) |
|
|
|
|
|
col1, col2 = st.columns(2) |
|
|
with col1: |
|
|
toggle_hinglish = st.toggle("Enable Hinglish", value=True, help="Mix of Hindi and English") |
|
|
with col2: |
|
|
run_visualization = st.toggle("Run Visualization", value=False, help="Enable visualization features") |
|
|
|
|
|
|
|
|
st.markdown('<div class="section-header">π» Technical Settings</div>', unsafe_allow_html=True) |
|
|
|
|
|
col1, col2, col3 = st.columns(3) |
|
|
with col1: |
|
|
programming_language = st.selectbox( |
|
|
"Programming Language", |
|
|
["python", "java", "javascript", "c++", "go"], |
|
|
index=0, |
|
|
format_func=lambda x: x.capitalize() |
|
|
) |
|
|
with col2: |
|
|
slide_colour = st.selectbox( |
|
|
"Slide Color", |
|
|
["blue", "red", "green", "purple", "orange"], |
|
|
index=0, |
|
|
format_func=lambda x: x.capitalize() |
|
|
) |
|
|
with col3: |
|
|
video_type = st.selectbox( |
|
|
"Video Type", |
|
|
["personalised_video", "standard_video"], |
|
|
index=0, |
|
|
format_func=lambda x: x.replace('_', ' ').title() |
|
|
) |
|
|
|
|
|
subtitle = st.text_input("Subtitle", placeholder="Optional subtitle for the video", value="") |
|
|
|
|
|
|
|
|
st.markdown("<br>", unsafe_allow_html=True) |
|
|
col1, col2, col3 = st.columns([1, 2, 1]) |
|
|
with col2: |
|
|
submitted = st.form_submit_button("Generate Personalized Course", use_container_width=True) |
|
|
|
|
|
|
|
|
if submitted: |
|
|
|
|
|
if not user_name: |
|
|
st.error("Please enter your name") |
|
|
elif topic_mode == "Single Topic" and not single_topic_title: |
|
|
st.error("Please enter a topic title") |
|
|
else: |
|
|
|
|
|
with st.spinner("Generating your personalized course..."): |
|
|
time.sleep(1.5) |
|
|
|
|
|
|
|
|
user_profile = { |
|
|
"personalized": True, |
|
|
"user_name": user_name, |
|
|
"user_age": user_age, |
|
|
"user_gender": user_gender, |
|
|
"user_tech_knowledge": tech_knowledge, |
|
|
"user_preferred_activity": preferred_activity, |
|
|
"user_food": food, |
|
|
"user_physical_activities": physical_activities, |
|
|
"learning_style": learning_style, |
|
|
"target_language": target_language, |
|
|
"tts_gender": tts_gender, |
|
|
"tts_voice": tts_voice, |
|
|
"toggle_hinglish": toggle_hinglish, |
|
|
"run_visualization": run_visualization, |
|
|
"subtitle": subtitle, |
|
|
"age_group": "18-25" if user_age <= 25 else "26-35" if user_age <= 35 else "35+" |
|
|
} |
|
|
|
|
|
|
|
|
settings = { |
|
|
"target_language": target_language, |
|
|
"tts_gender": tts_gender, |
|
|
"tts_voice": tts_voice, |
|
|
"toggle_hinglish": toggle_hinglish, |
|
|
"run_visualization": run_visualization, |
|
|
"subtitle": subtitle, |
|
|
"programming_language": programming_language, |
|
|
"slide_colour": slide_colour, |
|
|
"video_type": video_type |
|
|
} |
|
|
|
|
|
|
|
|
if topic_mode == "Single Topic": |
|
|
payload = { |
|
|
"personalization_id": personalization_id, |
|
|
"user_profile": user_profile, |
|
|
"settings": settings, |
|
|
"topic_data": { |
|
|
"topic_id": single_topic_id, |
|
|
"topic_title": single_topic_title, |
|
|
"chapter_id": single_chapter_id, |
|
|
"chapter_title": single_chapter_title, |
|
|
"course_id": course_id, |
|
|
"video_url": single_video_url, |
|
|
"video_duration": single_video_duration, |
|
|
"sequence_number": single_sequence_number |
|
|
} |
|
|
} |
|
|
else: |
|
|
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 |
|
|
} |
|
|
|
|
|
|
|
|
try: |
|
|
headers = { |
|
|
'Content-Type': 'application/json' |
|
|
} |
|
|
|
|
|
response = requests.post(API_ENDPOINT, json=payload, headers=headers, timeout=30) |
|
|
|
|
|
if response.status_code == 200: |
|
|
response_data = response.json() |
|
|
session_ids = response_data.get("session_ids", []) |
|
|
|
|
|
st.success(f"Course personalization started successfully for {user_name}!") |
|
|
|
|
|
|
|
|
st.session_state.session_ids.extend(session_ids) |
|
|
|
|
|
|
|
|
st.markdown("### π Your personalized course generation has started!") |
|
|
st.markdown(f""" |
|
|
**User**: {user_name} |
|
|
**Course ID**: {course_id} |
|
|
**Programming Language**: {programming_language.capitalize()} |
|
|
**Learning Style**: {learning_style.capitalize()} |
|
|
**Voice**: {tts_voice.capitalize()} ({tts_gender.capitalize()}) |
|
|
**Topics**: {len(topics_data) if topic_mode == "Multiple Topics" else 1} |
|
|
""") |
|
|
|
|
|
|
|
|
if session_ids: |
|
|
st.markdown("### π Session IDs for Tracking") |
|
|
for i, session_id in enumerate(session_ids, 1): |
|
|
st.markdown(f'<div class="session-info"><strong>Session {i}:</strong> <code>{session_id}</code></div>', unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
st.markdown('<div class="section-header">π API Response</div>', unsafe_allow_html=True) |
|
|
st.markdown('<div class="api-response">', unsafe_allow_html=True) |
|
|
st.json(response_data) |
|
|
st.markdown('</div>', unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
st.info(f""" |
|
|
π‘ **Tracking Information** |
|
|
You can track the progress of your video generation using the session IDs above. |
|
|
The processing status will be updated in DynamoDB table: `{SESSION_TABLE}` |
|
|
Region: `{DYNAMODB_REGION}` |
|
|
""") |
|
|
|
|
|
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)}") |
|
|
|
|
|
|
|
|
st.warning("Request payload for debugging:") |
|
|
st.json(payload) |
|
|
|
|
|
|
|
|
if st.session_state.session_ids: |
|
|
st.markdown("---") |
|
|
st.markdown("### π Previous Session IDs") |
|
|
for i, session_id in enumerate(st.session_state.session_ids, 1): |
|
|
st.markdown(f'<div class="session-info"><strong>Session {i}:</strong> <code>{session_id}</code></div>', unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
if st.button("Clear Session History"): |
|
|
st.session_state.session_ids = [] |
|
|
st.rerun() |
|
|
|
|
|
|
|
|
st.markdown("---") |
|
|
st.markdown(f""" |
|
|
<div style="text-align: center; color: #888888;"> |
|
|
Β© 2025 Base Course Personalization | All Rights Reserved<br> |
|
|
<small>API Endpoint: {API_ENDPOINT}</small><br> |
|
|
<small>Session Tracking: {SESSION_TABLE} ({DYNAMODB_REGION})</small> |
|
|
</div> |
|
|
""", unsafe_allow_html=True) |