rivapereira123's picture
Update app.py
5ff32a3 verified
import random
import streamlit as st
import streamlit.components.v1 as components
from sentence_transformers import SentenceTransformer, util
from ensemble import get_ensembler
from quiz_engine import run_quiz_step, generate_tarot_card_summary, generate_reflection_and_prompt, QUESTION_BANK
from vibe_mapper import get_emojis_from_emotions
from utils import update_memory, log_interaction
# Initialize session state
if "quiz_started" not in st.session_state:
st.session_state.quiz_started = False
if "question_index" not in st.session_state:
st.session_state.question_index = 0
if "answers" not in st.session_state:
st.session_state.answers = []
if "reflections" not in st.session_state:
st.session_state.reflections = []
if "show_final" not in st.session_state:
st.session_state.show_final = False
if "memory" not in st.session_state:
st.session_state.memory = {
"key_phrases": [],
"emotional_patterns": [],
"attachment_cues": []
}
# ๐ŸŽจ Styling
st.markdown("""
<style>
body {
background: linear-gradient(to right, #ffe6f0, #f0f9ff);
font-family: 'Courier New', Courier, monospace;
}
.big-title { font-size: 2.5em; font-weight: bold; text-align: center; color: #d63384; }
.typing { border-right: .15em solid pink; white-space: nowrap; overflow: hidden; }
.question-text { font-size: 1.4em; font-weight: 500; color: #ff75a0; padding: 10px 0; display: inline-block; animation: typing 3s steps(40, end), blink-caret 0.75s step-end infinite; white-space: nowrap; overflow: hidden; border-right: .15em solid #ff75a0; }
@keyframes typing {
from { width: 0 }
to { width: 100% }
}
@keyframes blink-caret {
from, to { border-color: transparent }
50% { border-color: #ff75a0; }
}
audio { display: none; }
</style>
""", unsafe_allow_html=True)
# ๐ŸŽต Background Music
if not st.session_state.quiz_started:
st.audio("https://www.bensound.com/bensound-music/bensound-sunny.mp3", autoplay=True)
# ๐Ÿ’ฌ Typing Intro
if not st.session_state.quiz_started:
components.html("""
<div style="text-align: center; font-size: 2em; font-family: 'Courier New', monospace; color: #d63384;">
<span id="typing-text"></span>
</div>
<script>
const textElement = document.getElementById("typing-text");
const words = ["Welcome to The Ludus Quiz, Darling", "I'm Ludus, Let's figure you out", "Get to the bottom of what you want in a relationship", "And your expectations in one place"];
let wordIndex = 0;
let letterIndex = 0;
let currentWord = "";
let isDeleting = false;
function type() {
currentWord = words[wordIndex];
if (!isDeleting) {
textElement.textContent = currentWord.substring(0, letterIndex + 1);
letterIndex++;
} else {
textElement.textContent = currentWord.substring(0, letterIndex - 1);
letterIndex--;
}
if (!isDeleting && letterIndex === currentWord.length) {
isDeleting = true;
setTimeout(type, 1000);
} else if (isDeleting && letterIndex === 0) {
isDeleting = false;
wordIndex = (wordIndex + 1) % words.length;
setTimeout(type, 500);
} else {
setTimeout(type, isDeleting ? 40 : 90);
}
}
document.addEventListener("DOMContentLoaded", type);
</script>
""", height=80)
# ๐Ÿง  Start Quiz Button
if not st.session_state.quiz_started:
if st.button("Let's Begin ๐Ÿ’–", type="primary"):
st.session_state.quiz_started = True
st.rerun()
st.info("๐ŸŽผ This quiz is designed to gently uncover your emotional love style.")
# ๐Ÿงฉ Quiz in Progress
else:
if st.session_state.question_index < len(QUESTION_BANK):
current_question = QUESTION_BANK[st.session_state.question_index]
# โœจ JS Typing Animation
components.html(f"""
<div style="text-align: center; font-size: 1.4em; font-family: 'Courier New', monospace; color: #ff75a0;">
<span id="question-typing"></span>
</div>
<script>
const textElement = document.getElementById("question-typing");
const text = `{current_question}`;
let idx = 0;
function type() {{
if (idx < text.length) {{
textElement.textContent += text.charAt(idx);
idx++;
setTimeout(type, 45);
}}
}}
type();
</script>
""", height=80)
# ๐Ÿ’ฌ Input + Emotion Detection
user_input = st.text_input("Your response", key=f"q_{st.session_state.question_index}")
if user_input and "answered" not in st.session_state:
ensembler = get_ensembler()
tone = ensembler.predict_emotion(user_input)
update_memory(user_input, tone["top_emotions"], st.session_state.memory)
log_interaction(st.session_state.answers, current_question, user_input, tone)
st.session_state.question_index += 1
st.session_state.answered = True
st.rerun()
if "answered" in st.session_state:
del st.session_state.answered
# โœ… End of Quiz: Show Final Results on Button Press
elif len(st.session_state.answers) >= len(QUESTION_BANK):
if not st.session_state.show_final:
if st.button("Reveal My Final Reflection ๐Ÿ’–"):
st.session_state.show_final = True
st.rerun()
else:
ensembler = get_ensembler()
st.markdown("## ๐Ÿ’Œ Final Reflection")
all_text = " ".join([entry["response"] for entry in st.session_state.answers])
result = ensembler.predict_emotion(all_text)
st.subheader("๐Ÿ’ซ Your Emotional Reflection")
for label, score in result["top_emotions"]:
st.write(f"**{label.capitalize()}**: {score:.2f}")
st.markdown("**Vibe Tags:** " + ", ".join(result["vibe_tags"]))
st.success("๐Ÿƒ Vibe Tags: " + ", ".join(result["vibe_tags"]))
st.subheader("๐Ÿ’Œ Overall Sentiment")
st.write(f"**Sentiment:** {result['sentiment_label']} ({result['sentiment_score']:.2f})")
st.progress(min(max(result['sentiment_score'], 0.0), 1.0))
st.subheader("๐Ÿ”ž NSFW Detection")
if result.get("spicy", False):
st.error("Ooooo hot~")
else:
st.success("All clear โ€” nothing spicy detected.")
st.markdown("---")
st.subheader("๐Ÿ“œ Your Answers Log")
for idx, entry in enumerate(st.session_state.answers):
st.markdown(f"**Q{idx+1}:** {entry['question']}")
st.markdown(f"**A{idx+1}:** {entry['response']}")
if "reflection" in entry:
st.markdown(f"*Reflection:* {entry['reflection']}")
tarot_summary = generate_tarot_card_summary(st.session_state.reflections)
st.markdown("---")
st.subheader("๐Ÿ”ฎ Your Romantic Archetype")
st.markdown(tarot_summary)