import streamlit as st
import json
import os
import random
from datetime import datetime, date
# ─────────────────────────────────────────
# CONFIG
# ─────────────────────────────────────────
st.set_page_config(
page_title="EchoEnglish AI 15.0",
page_icon="
⚡
",
layout="wide",
initial_sidebar_state="expanded"
)
DATA_FILE = "echo_ai_database.json"
# ─────────────────────────────────────────
# DATABASE (fusão completa)
# ─────────────────────────────────────────
def load_data():
if os.path.exists(DATA_FILE):
with open(DATA_FILE, "r", encoding="utf-8") as f:
return json.load(f)
return {}
def save_data(data):
with open(DATA_FILE, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
if "data" not in st.session_state:
st.session_state.data = load_data()
defaults = {
"history": [],
"scores": {"Grammar": 5.0, "Vocabulary": 5.0, "Fluency": 5.0, "Context": 5.0},
"xp_total": 0,
"xp_today": 0,
"streak": 1,
"last_day": None,
"known_words": [],
"new_words_today": [],
"achievements": [],
"study_time": 0,
"sessions": 0,
"daily_goal": 5,
"task_completed": False,
"persona": "Friendly British Tutor"
}
for k, v in defaults.items():
if k not in st.session_state.data:
st.session_state.data[k] = v
data = st.session_state.data
# ─────────────────────────────────────────
# LEVEL SYSTEM
# ─────────────────────────────────────────
LEVELS = [(0, "A1"), (3, "A2"), (5, "B1"), (7, "B2"), (8.5, "C1"), (9.5, "C2")]
def calculate_level():
avg = sum(data["scores"].values()) / 4
for score, level in reversed(LEVELS):
if avg >= score:
return level
return "A0"
level = calculate_level()
# ─────────────────────────────────────────
# DAILY STREAK (fixo e robusto)
# ─────────────────────────────────────────
today = date.today().isoformat()
if data["last_day"] is None:
data["streak"] = 1
elif data["last_day"] == today:
pass # Já usou hoje
elif (date.today() - date.fromisoformat(data["last_day"])).days == 1:
data["streak"] += 1
else:
data["streak"] = 1
data["last_day"] = today
# ─────────────────────────────────────────
# AI ANALYSIS (rubrica profunda + multi-avaliadores)
# ─────────────────────────────────────────
def analyze_text(text):
text_lower = text.lower()
words = text.split()
word_count = len(words)
delta = {"Grammar": 0.0, "Vocabulary": 0.0, "Fluency": 0.0, "Context": 0.0}
feedback = []
xp = random.randint(10, 20)
# Grammar
grammar_issues = []
if "i goes" in text_lower or "he go" in text_lower:
grammar_issues.append("Subject-verb: I/he/she **go** → **goes**")
delta["Grammar"] -= 1.0
if any(w in text_lower for w in ["dont", "doesnt", "isnt"]) and "'" not in text:
grammar_issues.append("Contractions: don't, doesn't, isn't")
delta["Grammar"] -= 0.5
if not grammar_issues and word_count > 8:
delta["Grammar"] += 0.3
# Vocabulary
advanced = [w for w in words if len(w) > 6 and w.lower() not in {"good", "bad", "big",
"small", "nice", "eat", "go", "like"}]
if len(advanced) >= 2:
delta["Vocabulary"] += 0.5
new_adv = [w for w in advanced if w.lower() not in data["known_words"]]
data["new_words_today"].extend(new_adv)
data["known_words"].extend(new_adv)
xp += 5
feedback.append("
🌟
New vocab detected!")
# Fluency
sentences = text.count(".") + text.count("!") + text.count("?")
if sentences >= 2:
delta["Fluency"] += 0.3
# Context
connectors = ["because", "but", "so"]
if any(word in text_lower for word in connectors):
delta["Context"] += 0.5
# Mini-lesson se erra
if delta["Grammar"] < -0.5:
feedback.append(random.choice([
"
💡
Tip: Present simple 3rd person → he/she/it + verb-s/es",
"Watch articles: a / an / the. Ex: I have __ dog."
]))
feedback.extend([f"
📌
{issue}" for issue in grammar_issues])
# Multi-avaliadores (média de 3)
evaluator_deltas = [
{k: v * random.uniform(0.9, 1.1) for k, v in delta.items()},
{k: v * random.uniform(0.8, 1.2) for k, v in delta.items()},
{k: v * random.uniform(0.95, 1.05) for k, v in delta.items()}
]
avg_delta = {k: sum(d[k] for d in evaluator_deltas) / 3 for k in delta}
return avg_delta, feedback, xp
# ─────────────────────────────────────────
# ACHIEVEMENTS (expandidos)
# ─────────────────────────────────────────
def check_achievements():
if data["xp_total"] > 500 and "Rising Learner" not in data["achievements"]:
data["achievements"].append("Rising Learner")
if data["streak"] >= 7 and "7 Day Streak" not in data["achievements"]:
data["achievements"].append("7 Day Streak")
if len(data["new_words_today"]) >= 20 and "Vocab Master" not in data["achievements"]:
data["achievements"].append("Vocab Master")
if data["sessions"] >= 10 and "Dedicated Student" not in data["achievements"]:
data["achievements"].append("Dedicated Student")
# ─────────────────────────────────────────
# PERSONA RESPONSE (variada e contextual)
# ─────────────────────────────────────────
def get_persona_response(feedback, persona):
openings = {
"Friendly British Tutor": ["Splendid effort!", "Well done!", "That's coming along nicely!"],
"Strict American Coach": ["Solid—now level up.", "Not bad, refine it.", "Focus on
structure."],
"Chill Australian Mate": ["Nice one, mate!", "You're getting it!", "Sweet as!"],
"Patient Canadian Guide": ["Great job trying!", "Progress visible.", "Let's polish this."]
}
opening = random.choice(openings.get(persona, ["Good job!"]))
response = f"{opening} Tell me more—what happened next?\n\n"
if feedback:
response += "Quick Tips:\n" + "\n".join(feedback) + "\n\n"
return response
# ─────────────────────────────────────────
# SIDEBAR (tudo junto: progress, achievements, task, review, persona)
# ─────────────────────────────────────────
with st.sidebar:
st.title("
📊
Progress Hub")
st.metric("Level", level)
st.metric("Streak", f"{data['streak']} days
�
�
")
st.metric("XP Today", data["xp_today"])
st.metric("Total XP", f"{data['xp_total']:,}")
st.metric("Sessions", data["sessions"])
st.caption(f"Study Time: {data['study_time']} min")
st.divider()
st.subheader("Achievements")
if data["achievements"]:
for a in data["achievements"]:
st.write("
🏆
", a)
else:
st.write("Keep going—no badges yet!")
st.divider()
st.subheader("Daily Task")
hour = datetime.now().hour
task = "Morning: Talk about breakfast/sleep." if hour < 12 else "Afternoon: Describe your
environment." if hour < 18 else "Evening: Reflect on challenges."
st.info(task)
if st.checkbox("Mark Completed", key="task_done"):
if not data["task_completed"]:
data["xp_today"] += 5
data["task_completed"] = True
st.success("+5 XP!")
st.divider()
st.subheader("New Words Today")
if data["new_words_today"]:
for w in data["new_words_today"][:5]:
st.write(f"• {w}")
if len(data["new_words_today"]) > 5:
st.caption(f"...and {len(data['new_words_today']) - 5} more")
else:
st.write("No new words yet—try longer texts!")
st.divider()
st.subheader("Teacher Style")
personas = ["Friendly British Tutor", "Strict American Coach", "Chill Australian Mate",
"Patient Canadian Guide"]
selected = st.selectbox("Choose vibe", personas, index=personas.index(data["persona"]))
if selected != data["persona"]:
data["persona"] = selected
st.rerun()
# ─────────────────────────────────────────
# MAIN UI
# ─────────────────────────────────────────
st.title("EchoEnglish AI 15.0
⚡
")
st.caption("Ultimate English Practice • Powered by Smart Feedback")
cols = st.columns(4)
for i, (skill, val) in enumerate(data["scores"].items()):
delta = random.choice([-0.1, 0.2, 0.1]) # Leve variação visual
cols[i].metric(skill, f"{val:.1f}/10", delta=delta)
st.divider()
# ─────────────────────────────────────────
# CHAT HISTORY
# ─────────────────────────────────────────
for msg in data["history"]:
role = msg["role"]
avatar = "
" if role == "user" else "
🤖
"
with st.chat_message(role, avatar=avatar):
st.markdown(msg["content"])
# ─────────────────────────────────────────
# INPUT & PROCESS
# ─────────────────────────────────────────
prompt = st.chat_input("Write in English... (e.g., describe your day or feelings)")
if prompt:
data["history"].append({"role": "user", "content": prompt})
with st.chat_message("user", avatar="
"):
st.markdown(prompt)
with st.chat_message("assistant", avatar="
🤖
"):
with st.spinner("Analyzing with AI..."):
delta, feedback, xp = analyze_text(prompt)
# Update scores com clamp
for skill, value in delta.items():
data["scores"][skill] = max(0.0, min(10.0, data["scores"][skill] + value))
data["xp_today"] += xp
data["xp_total"] += xp
data["sessions"] += 1
data["study_time"] += random.randint(2, 5) # Minutos simulados
data["daily_goal"] = min(10, data["daily_goal"] + 1)
check_achievements()
response = get_persona_response(feedback, data["persona"])
st.markdown(response)
# Expander pra details
with st.expander("Detailed Breakdown"):
st.write("**Strengths:** Good flow and connectors used.")
st.write(f"**XP Gained:** +{xp}")
if feedback:
st.write("**Feedback Items:**", len(feedback))
data["history"].append({"role": "assistant", "content": response})
data["task_completed"] = False # Reset pra amanhã
save_data(data)
st.rerun()
# Rodapé
st.divider()
col1, col2 = st.columns([6, 1])
with col1:
st.caption("EchoEnglish AI • Version 15.0 • March 2026 • Built for you, Mesaque!")
with col2:
if st.button("Reset All", type="primary"):
for k in list(data.keys()):
del data[k]
if os.path.exists(DATA_FILE):
os.remove(DATA_FILE)
st.rerun()