Skip to content

Commit db3375b

Browse files
add some more weights
1 parent 02e5190 commit db3375b

File tree

2 files changed

+220
-317
lines changed

2 files changed

+220
-317
lines changed

app.py

Lines changed: 62 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44
import pandas as pd
55
import plotly.express as px
66
from backend import ModalXSystem
7-
from emotion_engine import EmotionAnalyzer
87

8+
# --- PAGE CONFIG ---
99
st.set_page_config(
1010
page_title="DIU Presentation Grader",
1111
page_icon="🎓",
1212
layout="wide",
1313
initial_sidebar_state="expanded"
1414
)
1515

16+
# --- CSS STYLING ---
1617
st.markdown("""
1718
<style>
1819
.stApp { background-color: #0E1117; color: #FAFAFA; }
@@ -49,11 +50,6 @@
4950
color: white;
5051
border: 1px solid #444;
5152
}
52-
[data-testid="stSidebar"] {
53-
background-color: #12151e;
54-
border-right: 1px solid #333;
55-
}
56-
5753
div.stDownloadButton > button {
5854
background: linear-gradient(90deg, #198754, #20c997);
5955
color: white !important;
@@ -63,27 +59,13 @@
6359
font-size: 16px;
6460
font-weight: bold;
6561
border-radius: 8px;
66-
box-shadow: 0 4px 12px rgba(32, 201, 151, 0.4);
67-
transition: transform 0.2s;
68-
}
69-
div.stDownloadButton > button:hover {
70-
transform: scale(1.02);
71-
}
72-
73-
.stStatusWidget {
74-
background-color: #1E2130;
75-
border: 1px solid #444;
76-
border-radius: 10px;
7762
}
7863
</style>
7964
""", unsafe_allow_html=True)
8065

8166
st.markdown('<div class="main-header"><h1>🎓 DIU Smart Faculty Grader</h1><p>AI-Powered Multi-Modal Presentation Assessment</p></div>', unsafe_allow_html=True)
8267

83-
@st.cache_resource
84-
def load_emotion_engine():
85-
return EmotionAnalyzer()
86-
68+
# --- SIDEBAR ---
8769
with st.sidebar:
8870
st.header("📋 Student Details")
8971
s_name = st.text_input("Student Name", placeholder="e.g. Muntasir Islam")
@@ -115,22 +97,18 @@ def load_emotion_engine():
11597

11698
st.markdown("---")
11799
analyze_btn = st.button("🚀 Start Analysis", type="primary", use_container_width=True)
118-
st.caption("Powered by ModalX Engine v2.1")
100+
st.caption("Powered by ModalX Engine v3.0")
119101

102+
# --- MAIN LOGIC ---
120103
if analyze_btn:
121104
if not video_path or not s_name:
122105
st.error("⚠️ Please enter Student Name and Provide a Video first.")
123106
else:
107+
# Initialize Backend
124108
if 'modalx' not in st.session_state:
125109
with st.spinner("⚡ Booting AI Engine..."):
126110
st.session_state.modalx = ModalXSystem()
127111

128-
try:
129-
emotion_engine = load_emotion_engine()
130-
except Exception as e:
131-
st.error(f"Could not load Emotion Model: {e}")
132-
st.stop()
133-
134112
proc_col1, proc_col2 = st.columns([1, 1])
135113

136114
with proc_col1:
@@ -142,46 +120,34 @@ def load_emotion_engine():
142120
st.video(video_path)
143121

144122
results = None
145-
emotion_results = None
146123

147124
with proc_col2:
148125
with st.status("🚀 ModalX Engine Running...", expanded=True) as status:
149126
st.write("🔄 Initializing Neural Networks...")
150127
time.sleep(1)
151128

152-
st.write("🧠 Running General Assessment (Whisper + CV)...")
129+
st.write("🧠 Extracting Audio & Transcribing (Whisper)...")
130+
st.write("👁️ Scanning Facial Landmarks (MediaPipe)...")
131+
st.write("🎭 Analyzing Micro-Expressions & Tone (CNN)...")
132+
st.write("📝 Evaluating Content Impact & Vocabulary...")
133+
153134
try:
135+
# The backend now handles EVERYTHING (Audio, Visual, Emotion, Content)
154136
results = st.session_state.modalx.analyze(video_path, s_name, s_id, is_url)
155-
except Exception as e:
156-
st.error(f"General Engine Error: {e}")
157-
158-
if video_path and os.path.exists(video_path) and not is_url:
159-
st.write("🎭 Analyzing Emotional Tones (CNN-1D)...")
160-
try:
161-
e_times, e_emotions, e_summary = emotion_engine.predict(video_path)
162-
emotion_results = {
163-
"times": e_times,
164-
"emotions": e_emotions,
165-
"summary": e_summary
166-
}
167-
except Exception as e:
168-
st.warning(f"Emotion Analysis skipped: {e}")
169-
elif is_url:
170-
st.warning("⚠️ Emotion Graph unavailable for external URLs (Download required)")
171-
172-
if results:
173137
status.update(label="✅ Analysis Complete!", state="complete", expanded=False)
174-
else:
138+
except Exception as e:
139+
st.error(f"Engine Error: {e}")
175140
status.update(label="❌ Analysis Failed", state="error")
176141

142+
# --- RESULTS DASHBOARD ---
177143
if results:
178144
st.divider()
179145
st.balloons()
180146

181147
score = results['score']
182148

183-
grade = "F"
184-
grade_bg = "#dc3545"
149+
# Grade Logic
150+
grade = "F"; grade_bg = "#dc3545"
185151
if score >= 80: grade, grade_bg = "A+", "#198754"
186152
elif score >= 75: grade, grade_bg = "A", "#20c997"
187153
elif score >= 70: grade, grade_bg = "A-", "#0dcaf0"
@@ -192,7 +158,7 @@ def load_emotion_engine():
192158
c1, c2, c3 = st.columns([1.5, 1.5, 3])
193159

194160
with c1:
195-
st.metric("Final Score", f"{score}/100", delta=f"{score-70} vs Avg")
161+
st.metric("Final Weighted Score", f"{score}/100", delta=f"{score-70} vs Avg")
196162

197163
with c2:
198164
st.markdown(f"""
@@ -205,115 +171,104 @@ def load_emotion_engine():
205171
with c3:
206172
st.markdown(f"### 👤 {s_name}")
207173
st.caption(f"Student ID: {s_id}")
208-
st.info("Grading Logic: Weighted average of Speech Clarity (60%) and Visual Engagement (40%).")
174+
st.info("Grading Logic: Audio (30%), Visual (30%), Emotion (20%), Content (20%)")
209175

210176
st.divider()
211177

212-
tab1, tab2, tab3 = st.tabs(["📊 Performance Metrics", "🎭 Emotional Intelligence", "📝 Transcript & Feedback"])
178+
# --- TABS ---
179+
tab1, tab2, tab3 = st.tabs(["📊 Performance Metrics", "🎭 Emotional Intelligence", "📝 Content & Report"])
213180

181+
# TAB 1: CORE METRICS
214182
with tab1:
215183
col_a, col_b = st.columns(2)
216184

217185
with col_a:
218186
st.subheader("🗣️ Audio Intelligence")
219187
audio = results['metrics']['audio']
220-
221188
st.write(f"**Speaking Pace** ({audio['wpm']} WPM)")
222189
st.progress(float(min(audio['wpm']/160, 1.0)))
223-
224-
st.write(f"**Tonal Variation** (Score: {audio['physics']['pitch_variation']})")
190+
st.write(f"**Pitch Variation** ({audio['physics']['pitch_variation']})")
225191
st.progress(float(min(audio['physics']['pitch_variation']/50, 1.0)))
226-
227-
st.write(f"**Confidence (Volume)** (Score: {audio['physics']['volume_score']})")
228-
st.progress(float(min(audio['physics']['volume_score']/100, 1.0)))
229-
230-
if audio['filler_count'] > 3:
231-
st.warning(f"⚠️ High Filler Words Detected: {audio['filler_count']}")
232-
else:
233-
st.success(f"✅ Low Filler Words: {audio['filler_count']}")
192+
st.metric("Pause Ratio", f"{audio['physics']['pause_ratio']}%")
234193

235194
with col_b:
236195
visual = results['metrics']['visual']
237-
238196
if visual.get('is_slide_mode', False):
239197
st.subheader("🖼️ Slide Design AI")
240-
st.info("Scanner Mode: Slide Presentation")
198+
st.info("Mode: Slide Presentation")
241199
slides = results['metrics']['slides']
242-
243200
st.metric("Word Density", f"{slides['avg_words_per_slide']} words/slide")
244-
st.write("**Readability Score**")
245-
st.progress(float(min(slides['readability_score']/100, 1.0)))
246-
st.write(f"**Slide Transitions Detected:** {slides['slide_changes']}")
201+
st.metric("Slide Readability", f"{int(slides['readability_score'])}/100")
247202
else:
248203
st.subheader("👁️ Behavioral AI")
249-
st.info("Scanner Mode: Presenter Face")
250-
251-
st.write(f"**Eye Contact Consistency** ({visual['eye_contact_score']}%)")
204+
st.info("Mode: Face Presentation")
205+
st.write(f"**Eye Contact** ({visual['eye_contact_score']}%)")
252206
st.progress(float(visual['eye_contact_score']/100))
253-
254207
st.write(f"**Posture Stability** ({visual['posture_score']}%)")
255208
st.progress(float(visual['posture_score']/100))
256209

210+
# TAB 2: EMOTION ANALYZER
257211
with tab2:
258-
if emotion_results and emotion_results['times']:
212+
emo_data = results.get('emotion_data', {})
213+
if emo_data and emo_data.get('times'):
259214
st.subheader("📈 Emotional Flow Over Time")
260215

261-
e_times = emotion_results['times']
262-
e_emotions = emotion_results['emotions']
263-
e_summary = emotion_results['summary']
216+
e_times = emo_data['times']
217+
e_emotions = emo_data['emotions']
218+
e_summary = emo_data['summary']
264219

220+
# Timeline Graph
265221
df = pd.DataFrame({"Time (s)": e_times, "Emotion": e_emotions})
266222
emotion_order = sorted(list(set(e_emotions)))
267223

268224
fig = px.scatter(
269225
df, x="Time (s)", y="Emotion", color="Emotion",
270226
size=[15]*len(df), template="plotly_dark",
271-
category_orders={"Emotion": emotion_order},
272-
title="Speaker Emotion Timeline"
227+
category_orders={"Emotion": emotion_order}
273228
)
274229
fig.update_traces(mode='lines+markers', line=dict(width=1, color='gray'))
275-
fig.update_layout(height=400, paper_bgcolor="#0E1117", plot_bgcolor="#0E1117")
230+
fig.update_layout(height=350, paper_bgcolor="#0E1117", plot_bgcolor="#0E1117")
276231
st.plotly_chart(fig, use_container_width=True)
277232

278-
c_pie, c_dom = st.columns([1, 1])
233+
# Summary
234+
c_pie, c_stat = st.columns([1, 1])
279235
with c_pie:
280236
st.markdown("##### Emotion Distribution")
281-
fig_pie = px.pie(
282-
names=list(e_summary.keys()),
283-
values=list(e_summary.values()),
284-
hole=0.4, template="plotly_dark"
285-
)
237+
fig_pie = px.pie(names=list(e_summary.keys()), values=list(e_summary.values()), hole=0.4, template="plotly_dark")
286238
fig_pie.update_layout(paper_bgcolor="#0E1117")
287239
st.plotly_chart(fig_pie, use_container_width=True)
288-
289-
with c_dom:
240+
with c_stat:
290241
dom_emotion = max(e_summary, key=e_summary.get)
291-
st.markdown("##### Analysis")
292242
st.metric("Dominant Tone", dom_emotion.upper())
293-
294-
if dom_emotion in ['happy', 'neutral', 'surprise', 'surprised']:
295-
st.success("The speaker maintains a **Positive/Confident** tone.")
296-
elif dom_emotion in ['fear', 'sad']:
297-
st.warning("The speaker seems **Nervous or Low Energy**. Needs more enthusiasm.")
298-
elif dom_emotion in ['angry', 'disgust', 'anger']:
299-
st.error("The speaker sounds **Aggressive/Frustrated**. Needs a softer tone.")
243+
if dom_emotion in ['happy', 'neutral', 'surprise']:
244+
st.success("Positive tone detected. Good confidence.")
245+
else:
246+
st.warning("Negative/Nervous tone detected.")
300247
else:
301-
st.info("Emotion analysis is not available for this file type or URL.")
248+
st.info("Emotion analysis unavailable for this file.")
302249

250+
# TAB 3: CONTENT & REPORT
303251
with tab3:
304-
fb_col1, fb_col2 = st.columns([2, 1])
252+
c_col1, c_col2 = st.columns([2, 1])
305253

306-
with fb_col1:
254+
with c_col1:
255+
st.subheader("🧠 Content Intelligence")
256+
audio = results['metrics']['audio']
257+
258+
col_i1, col_i2 = st.columns(2)
259+
col_i1.metric("Content Score", f"{int(audio.get('content_score', 0))}/100")
260+
col_i2.metric("Power Words Used", audio.get('impact_words', 0))
261+
307262
st.markdown("### 🤖 AI Recommendations")
308263
for item in results['feedback']:
309264
st.warning(f"👉 {item}")
310-
311-
st.markdown("### 📄 Speech Transcript")
312-
st.text_area("Full Transcript", results['metrics']['audio']['transcript'], height=150)
265+
266+
with st.expander("View Full Transcript"):
267+
st.text(audio['transcript'])
313268

314-
with fb_col2:
269+
with c_col2:
315270
st.markdown("### 📥 Official Report")
316-
st.write("Download the verified PDF report for faculty submission.")
271+
st.write("Download the verified PDF report containing all graphs and scores.")
317272

318273
if results['report']:
319274
st.download_button(
@@ -323,5 +278,6 @@ def load_emotion_engine():
323278
mime="application/pdf"
324279
)
325280

281+
# Cleanup
326282
if video_path and os.path.exists(video_path) and not is_url:
327283
os.remove(video_path)

0 commit comments

Comments
 (0)