Sentiment-Analysis/PWU-MSIT
0
1import gradio as gr2import pandas as pd3import matplotlib.pyplot as plt4import numpy as np5import csv6import os7from datetime import datetime8import torch9from transformers import BertTokenizer, BertForSequenceClassification10import traceback11 12# Define CSV file path13CSV_FILE = "pwu_survey_data.csv" # Added CSV_FILE definition14 15# Initialize data storage16if not os.path.exists(CSV_FILE):17 with open(CSV_FILE, "w", newline='', encoding='utf-8') as f:18 writer = csv.writer(f)19 headers = [20 "Timestamp", "Program", "Age", "Gender", "Enrolment", "Units",21 "Overall Satisfaction", "Teaching Faculty", "Course Availability", 22 "Academic Advising", "Access to Faculty", "Fellow Students", 23 "Academic Reputation", "Value for Price",24 "Educational Comment", "Educational Sentiment", "Educational Confidence",25 "Classrooms", "Lab Facilities", "Mini Library", "Career Counseling", 26 "OJT Placement", "Conference Room", "Faculty Room", "Head Office",27 "Facilities Comment", "Facilities Sentiment", "Facilities Confidence",28 "Clubs", "Diversity", "Dept Safety", "Activities", 29 "Student Safety", "Social Life",30 "Campus Comment", "Campus Sentiment", "Campus Confidence"31 ]32 writer.writerow(headers)33 34# Load BERT model and tokenizer35try:36 print("Loading BERT model...")37 model_name = "nlptown/bert-base-multilingual-uncased-sentiment"38 tokenizer = BertTokenizer.from_pretrained(model_name)39 model = BertForSequenceClassification.from_pretrained(model_name)40 model.eval()41 print("BERT model loaded successfully!")42except Exception as e:43 print(f"Error loading BERT model: {e}")44 # Fallback to simple sentiment analysis if BERT fails45 model = None46 tokenizer = None47 48def bert_sentiment(text):49 """Analyze sentiment using BERT model with confidence scores"""50 if not text.strip():51 return "", 0.052 53 if model is None or tokenizer is None:54 return "⚠️ Model Error", 0.055 56 try:57 inputs = tokenizer(58 text, 59 return_tensors="pt", 60 truncation=True, 61 max_length=51262 )63 64 with torch.no_grad():65 outputs = model(**inputs)66 67 logits = outputs.logits68 probabilities = torch.softmax(logits, dim=1).numpy()[0]69 rating = np.argmax(probabilities) + 1 # Ratings 1-570 71 # Map ratings to sentiment labels72 if rating >= 4:73 sentiment = "😊 Positive"74 elif rating <= 2:75 sentiment = "😠 Negative"76 else:77 sentiment = "😐 Neutral"78 79 confidence = probabilities[np.argmax(probabilities)]80 return sentiment, round(float(confidence), 3)81 except Exception as e:82 print(f"Sentiment analysis error: {e}")83 return "⚠️ Analysis Error", 0.084 85# Save response to CSV86def save_to_csv(data):87 try:88 with open(CSV_FILE, "a", newline='', encoding='utf-8') as f:89 writer = csv.writer(f)90 writer.writerow(data)91 print(f"Data saved to {os.path.abspath(CSV_FILE)}")92 return True93 except Exception as e:94 print(f"CSV save error: {e}")95 return False96 97# Generate visualizations98def generate_visualizations():99 try:100 # Check if CSV exists and has data101 if not os.path.exists(CSV_FILE) or os.path.getsize(CSV_FILE) < 100:102 return None, None, None, None103 104 df = pd.read_csv(CSV_FILE)105 106 if len(df) < 1:107 return None, None, None, None108 109 # Create visualization placeholders for when data is insufficient110 overall_plot = None111 category_plot = None112 sentiment_plot = None113 heatmap_plot = None114 115 # Overall satisfaction distribution - FIXED116 if 'Overall Satisfaction' in df.columns:117 plt.figure(figsize=(10, 5))118 119 # Define the correct order of satisfaction levels120 satisfaction_order = ['Very satisfied', 'Satisfied', 'Neutral', 'Dissatisfied', 'Very dissatisfied']121 122 # Count occurrences of each satisfaction level123 satisfaction_counts = df['Overall Satisfaction'].value_counts()124 125 # Reindex to include all satisfaction levels, even if count is zero126 satisfaction_counts = satisfaction_counts.reindex(satisfaction_order, fill_value=0)127 128 # Create bar plot129 colors = ['#4CAF50', '#8BC34A', '#FFC107', '#FF9800', '#F44336']130 satisfaction_counts.plot(kind='bar', color=colors)131 132 plt.title("Overall Satisfaction Distribution")133 plt.ylabel("Number of Students")134 plt.xlabel("Satisfaction Level")135 plt.xticks(rotation=45)136 plt.tight_layout()137 overall_plot = plt.gcf()138 plt.close()139 140 # Convert ratings to numerical values for other visualizations141 rating_map = {'Poor':1, 'Fair':2, 'Good':3, 'Very Good':4, 'Excellent':5}142 rating_cols = [143 'Teaching Faculty', 'Course Availability', 'Academic Advising', 144 'Access to Faculty', 'Fellow Students', 'Academic Reputation', 'Value for Price',145 'Classrooms', 'Lab Facilities', 'Mini Library', 'Career Counseling', 146 'OJT Placement', 'Conference Room', 'Faculty Room', 'Head Office',147 'Clubs', 'Diversity', 'Dept Safety', 'Activities', 'Student Safety', 'Social Life'148 ]149 150 for col in rating_cols:151 if col in df.columns:152 df[col] = df[col].map(rating_map)153 154 # Average ratings by category155 categories = {156 "Educational Experience": [157 'Teaching Faculty', 'Course Availability', 'Academic Advising', 158 'Access to Faculty', 'Fellow Students', 'Academic Reputation', 'Value for Price'159 ],160 "Facilities & Services": [161 'Classrooms', 'Lab Facilities', 'Mini Library', 'Career Counseling', 162 'OJT Placement', 'Conference Room', 'Faculty Room', 'Head Office'163 ],164 "Campus Life": [165 'Clubs', 'Diversity', 'Dept Safety', 'Activities', 166 'Student Safety', 'Social Life'167 ]168 }169 170 avg_ratings = {}171 for category, cols in categories.items():172 available_cols = [col for col in cols if col in df.columns]173 if available_cols:174 # Calculate average only if we have at least 1 valid value175 if len(df[available_cols].dropna()) > 0:176 avg_ratings[category] = df[available_cols].mean().mean()177 178 if avg_ratings:179 plt.figure(figsize=(10, 5))180 pd.Series(avg_ratings).plot(kind='bar', color=['#2196F3', '#3F51B5', '#9C27B0'])181 plt.title("Average Ratings by Category")182 plt.ylabel("Average Rating (1-5)")183 plt.ylim(0, 5)184 plt.xticks(rotation=0)185 plt.tight_layout()186 category_plot = plt.gcf()187 plt.close()188 189 # Sentiment distribution190 sentiment_cols = ['Educational Sentiment', 'Facilities Sentiment', 'Campus Sentiment']191 available_sentiment_cols = [col for col in sentiment_cols if col in df.columns]192 193 if available_sentiment_cols:194 sentiment_counts = pd.concat([df[col].value_counts() for col in available_sentiment_cols], axis=1)195 sentiment_counts.columns = ['Educational', 'Facilities', 'Campus']196 sentiment_counts = sentiment_counts.fillna(0)197 198 if not sentiment_counts.empty:199 plt.figure(figsize=(10, 5))200 sentiment_counts.plot(kind='bar', color=['#4CAF50', '#2196F3', '#FF9800'])201 plt.title("Sentiment Distribution by Category")202 plt.ylabel("Number of Comments")203 plt.xticks(rotation=0)204 plt.legend(title="Category")205 plt.tight_layout()206 sentiment_plot = plt.gcf()207 plt.close()208 209 # Detailed ratings heatmap210 detailed_ratings = []211 for category, cols in categories.items():212 available_cols = [col for col in cols if col in df.columns]213 if available_cols:214 # Calculate average only if we have data215 if len(df[available_cols].dropna()) > 0:216 category_avg = df[available_cols].mean()217 for col in available_cols:218 detailed_ratings.append({219 'Category': category,220 'Aspect': col,221 'Rating': category_avg[col]222 })223 224 if detailed_ratings:225 detailed_df = pd.DataFrame(detailed_ratings)226 pivot_df = detailed_df.pivot(index='Category', columns='Aspect', values='Rating')227 228 plt.figure(figsize=(12, 8))229 plt.imshow(pivot_df, cmap='RdYlGn', vmin=1, vmax=5)230 plt.colorbar(label='Rating (1-5)')231 plt.title("Detailed Aspect Ratings")232 plt.xticks(range(len(pivot_df.columns)), pivot_df.columns, rotation=45, ha='right')233 plt.yticks(range(len(pivot_df.index)), pivot_df.index)234 235 # Add text annotations236 for i in range(len(pivot_df.index)):237 for j in range(len(pivot_df.columns)):238 plt.text(j, i, f"{pivot_df.iloc[i, j]:.1f}", 239 ha="center", va="center", color="black", fontsize=9)240 241 plt.tight_layout()242 heatmap_plot = plt.gcf()243 plt.close()244 245 return overall_plot, category_plot, sentiment_plot, heatmap_plot246 247 except Exception as e:248 print(f"Visualization error: {e}")249 return None, None, None, None250 251# Create placeholder visualizations for when data is insufficient252def create_empty_plot(message):253 plt.figure(figsize=(10, 5))254 plt.text(0.5, 0.5, message, 255 ha='center', va='center', 256 fontsize=14, color='gray')257 plt.axis('off')258 plt.tight_layout()259 fig = plt.gcf()260 plt.close()261 return fig262 263# Reset all form fields264def reset_form():265 return [266 None, # program267 20, # age268 None, # gender269 None, # enrolment270 15, # units271 None, # overall_satisfaction272 None, # teaching_faculty273 None, # course_avail274 None, # academic_advising275 None, # access_faculty276 None, # fellow_students277 None, # academic_reputation278 None, # value_price279 "", # edu_comment280 None, # classrooms281 None, # lab_facilities282 None, # mini_library283 None, # career_counseling284 None, # ojt_placement285 None, # conference_room286 None, # faculty_room287 None, # head_office288 "", # facility_comment289 None, # clubs290 None, # diversity291 None, # dept_safety292 None, # activities293 None, # student_safety294 None, # social_life295 "", # campus_comment296 ]297 298# Function to handle CSV download299def get_csv_file():300 if os.path.exists(CSV_FILE):301 return os.path.abspath(CSV_FILE)302 else:303 raise gr.Error("CSV file does not exist yet. Submit at least one survey to create it.")304 305# Main survey function with error handling306def submit_survey(307 program, age, gender, enrolment, units,308 overall_satisfaction,309 teaching_faculty, course_avail, academic_advising, 310 access_faculty, fellow_students, academic_reputation, value_price,311 edu_comment,312 classrooms, lab_facilities, mini_library, career_counseling,313 ojt_placement, conference_room, faculty_room, head_office,314 facility_comment,315 clubs, diversity, dept_safety, activities, student_safety, social_life,316 campus_comment317):318 try:319 # Sentiment analysis with BERT320 edu_sentiment, edu_conf = bert_sentiment(edu_comment)321 facility_sentiment, facility_conf = bert_sentiment(facility_comment)322 campus_sentiment, campus_conf = bert_sentiment(campus_comment)323 324 # Prepare data for CSV325 timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")326 data = [327 timestamp, 328 program or "", 329 age or 0, 330 gender or "", 331 enrolment or "", 332 units or 0,333 overall_satisfaction or "",334 teaching_faculty or "", 335 course_avail or "", 336 academic_advising or "", 337 access_faculty or "", 338 fellow_students or "", 339 academic_reputation or "", 340 value_price or "",341 edu_comment or "", 342 edu_sentiment or "", 343 edu_conf or 0,344 classrooms or "", 345 lab_facilities or "", 346 mini_library or "", 347 career_counseling or "",348 ojt_placement or "", 349 conference_room or "", 350 faculty_room or "", 351 head_office or "",352 facility_comment or "", 353 facility_sentiment or "", 354 facility_conf or 0,355 clubs or "", 356 diversity or "", 357 dept_safety or "", 358 activities or "", 359 student_safety or "", 360 social_life or "",361 campus_comment or "", 362 campus_sentiment or "", 363 campus_conf or 0364 ]365 366 # Save to CSV367 save_success = save_to_csv(data)368 369 # Generate visualizations370 overall_plot, category_plot, sentiment_plot, heatmap_plot = generate_visualizations()371 372 # Create sentiment display373 sentiment_html = f"""374 <div style="background-color:maroon; padding:20px; border-radius:10px; margin-bottom:20px; border:1px solid #dee2e6;">375 <h3 style="color:#343a40;">📝 Sentiment Analysis</h3>376 <div style="display:flex; justify-content:space-between; margin-top:15px; gap:15px;">377 <div style="text-align:center; background-color:blue; padding:15px; border-radius:8px; width:100%; border:1px solid #cfe2ff;">378 <h4 style="color:#0d6efd;">Educational Experience</h4>379 <div style="font-size:36px;">{edu_sentiment.split()[0] if edu_sentiment and ' ' in edu_sentiment else ""}</div>380 <p><b>{edu_sentiment}</b><br>(Confidence: {edu_conf*100:.1f}%)</p>381 </div>382 <div style="text-align:center; background-color:blue; padding:15px; border-radius:8px; width:100%; border:1px solid #cfe2ff;">383 <h4 style="color:#0d6efd;">Facilities & Services</h4>384 <div style="font-size:36px;">{facility_sentiment.split()[0] if facility_sentiment and ' ' in facility_sentiment else ""}</div>385 <p><b>{facility_sentiment}</b><br>(Confidence: {facility_conf*100:.1f}%)</p>386 </div>387 <div style="text-align:center; background-color:blue; padding:15px; border-radius:8px; width:100%; border:1px solid #cfe2ff;">388 <h4 style="color:#0d6efd;">Campus Life</h4>389 <div style="font-size:36px;">{campus_sentiment.split()[0] if campus_sentiment and ' ' in campus_sentiment else ""}</div>390 <p><b>{campus_sentiment}</b><br>(Confidence: {campus_conf*100:.1f}%)</p>391 </div>392 </div>393 </div>394 """395 396 if save_success:397 status = "<div style='color:green; padding:10px; background-color:#e6ffe6; border-radius:5px;'>✅ Survey submitted successfully!</div>"398 else:399 status = "<div style='color:red; padding:10px; background-color:#ffebee; border-radius:5px;'>❌ Error saving data! Check console for details.</div>"400 401 # Create placeholder plots if needed402 if overall_plot is None:403 overall_plot = create_empty_plot("Not enough data yet\nfor overall satisfaction")404 if category_plot is None:405 category_plot = create_empty_plot("Not enough data yet\nfor category ratings")406 if sentiment_plot is None:407 sentiment_plot = create_empty_plot("Not enough data yet\nfor sentiment analysis")408 if heatmap_plot is None:409 heatmap_plot = create_empty_plot("Not enough data yet\nfor detailed ratings")410 411 return sentiment_html, overall_plot, category_plot, sentiment_plot, heatmap_plot, status412 413 except Exception as e:414 error_trace = traceback.format_exc()415 print(f"Submission error: {error_trace}")416 error_html = f"""417 <div style="background-color:#ffebee; padding:20px; border-radius:10px; margin-bottom:20px;">418 <h3>⚠️ Submission Error</h3>419 <p>An error occurred while processing your submission:</p>420 <pre>{str(e)}</pre>421 <p>Please try again or contact support.</p>422 </div>423 """424 status = f"<div style='color:red; padding:10px; background-color:#ffebee; border-radius:5px;'>❌ Error: {str(e)}</div>"425 426 # Create placeholder plots for error case427 empty_plot = create_empty_plot("Data not available due to error")428 return error_html, empty_plot, empty_plot, empty_plot, empty_plot, status429 430# Create Gradio interface with Tabs431with gr.Blocks(title="PWU-Student Satisfaction Survey", theme=gr.themes.Soft()) as demo:432 gr.Markdown("# 🎓 PWU-Student Satisfaction Survey")433 gr.Markdown("Share your experiences to help us improve our programs and facilities!")434 gr.Markdown("Complete the survey by clicking the tabs")435 436 with gr.Tab("Student Profile"):437 gr.Markdown("## Part I: Student Profile")438 with gr.Group():439 program = gr.Radio(440 label="Program", 441 choices=["", "BSFT", "MSFS", "MFSM"],442 value="",443 interactive=True444 )445 age = gr.Number(label="Age (in years)", minimum=16, maximum=60, value=20)446 gender = gr.Radio(447 label="Gender", 448 choices=["", "Male", "Female", "Other/Prefer not to say"],449 value=""450 )451 enrolment = gr.Radio(452 label="Enrolment status", 453 choices=["", "Freshman", "New Student/Transferee", "Old student", "Returnee"],454 value=""455 )456 units = gr.Slider(457 label="Current load (no. of units)", 458 minimum=1, maximum=24, step=1, value=15459 )460 461 with gr.Tab("Educational Experiences"):462 gr.Markdown("## Part II: Educational Experiences")463 overall_satisfaction = gr.Radio(464 label="1. Overall, how satisfied are you with your educational experience at our school?",465 choices=["", "Very satisfied", "Satisfied", "Neutral", "Dissatisfied", "Very dissatisfied"],466 value="",467 interactive=True468 )469 470 gr.Markdown("2. How would you rate the following aspects of your educational experience?")471 with gr.Row():472 teaching_faculty = gr.Radio(473 label="Quality of teaching faculty",474 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],475 value=""476 )477 course_avail = gr.Radio(478 label="Course availability",479 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],480 value=""481 )482 with gr.Row():483 academic_advising = gr.Radio(484 label="Academic advising",485 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],486 value=""487 )488 access_faculty = gr.Radio(489 label="Access to teaching faculty",490 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],491 value=""492 )493 with gr.Row():494 fellow_students = gr.Radio(495 label="Fellow students' academic ability",496 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],497 value=""498 )499 academic_reputation = gr.Radio(500 label="Academic reputation of the school",501 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],502 value=""503 )504 value_price = gr.Radio(505 label="Value of the education for the price",506 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],507 value=""508 )509 510 edu_comment = gr.Textbox(511 label="3. Is there anything else you'd like to share about your educational experience?",512 placeholder="Share your thoughts here...",513 lines=3514 )515 516 with gr.Tab("Facilities & Services"):517 gr.Markdown("## Support Services & Facilities")518 gr.Markdown("4. How would you rate the following services/facilities at the school?")519 with gr.Row():520 classrooms = gr.Radio(521 label="Classrooms",522 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],523 value=""524 )525 lab_facilities = gr.Radio(526 label="Food & Research Laboratory Facilities",527 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],528 value=""529 )530 with gr.Row():531 mini_library = gr.Radio(532 label="Mini Library",533 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],534 value=""535 )536 career_counseling = gr.Radio(537 label="Career Counseling",538 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],539 value=""540 )541 with gr.Row():542 ojt_placement = gr.Radio(543 label="OJT/Practicum placement",544 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],545 value=""546 )547 conference_room = gr.Radio(548 label="Department's Conference Room",549 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],550 value=""551 )552 with gr.Row():553 faculty_room = gr.Radio(554 label="Faculty room",555 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],556 value=""557 )558 head_office = gr.Radio(559 label="Head's Office",560 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],561 value=""562 )563 564 facility_comment = gr.Textbox(565 label="5. Is there anything else you'd like to share about our support services and facilities?",566 placeholder="Share your thoughts here...",567 lines=3568 )569 570 with gr.Tab("Campus Life"):571 gr.Markdown("## Campus Life")572 gr.Markdown("6. How would you rate the following aspects of your student life at the school?")573 with gr.Row():574 clubs = gr.Radio(575 label="Clubs and student organizations",576 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],577 value=""578 )579 diversity = gr.Radio(580 label="Student diversity",581 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],582 value=""583 )584 with gr.Row():585 dept_safety = gr.Radio(586 label="Department's safety",587 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],588 value=""589 )590 activities = gr.Radio(591 label="Co- & Extracurricular activities",592 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],593 value=""594 )595 with gr.Row():596 student_safety = gr.Radio(597 label="Student safety",598 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],599 value=""600 )601 social_life = gr.Radio(602 label="Social life",603 choices=["", "Poor", "Fair", "Good", "Very Good", "Excellent"],604 value=""605 )606 607 campus_comment = gr.Textbox(608 label="7. Is there anything else you'd like to share about your campus life?",609 placeholder="Share your thoughts here...",610 lines=3611 )612 613 with gr.Tab("Results & Analytics"):614 gr.Markdown("## Survey Results & Analytics")615 gr.Markdown("Submit your survey to see real-time analysis and visualizations")616 617 with gr.Row():618 submit_btn = gr.Button("Submit Survey", variant="primary")619 reset_btn = gr.Button("Clear Form", variant="secondary")620 download_btn = gr.Button("Click to activate CSV report below ", variant="secondary")621 622 # Output components623 sentiment_output = gr.HTML(label="Sentiment Analysis")624 625 with gr.Row():626 with gr.Column():627 gr.Markdown("### Satisfaction Distribution")628 plot1 = gr.Plot(label="Overall Satisfaction")629 with gr.Column():630 gr.Markdown("### Category Ratings")631 plot2 = gr.Plot(label="Category Ratings")632 633 with gr.Row():634 with gr.Column():635 gr.Markdown("### Sentiment Analysis")636 plot3 = gr.Plot(label="Sentiment Distribution")637 with gr.Column():638 gr.Markdown("### Detailed Ratings")639 plot4 = gr.Plot(label="Aspect Ratings Heatmap")640 641 file_download = gr.File(label="CSV Report", visible=True)642 643 # Status message644 status = gr.HTML()645 646 # Input components list647 input_components = [648 program, age, gender, enrolment, units,649 overall_satisfaction,650 teaching_faculty, course_avail, academic_advising, 651 access_faculty, fellow_students, academic_reputation, value_price,652 edu_comment,653 classrooms, lab_facilities, mini_library, career_counseling,654 ojt_placement, conference_room, faculty_room, head_office,655 facility_comment,656 clubs, diversity, dept_safety, activities, student_safety, social_life,657 campus_comment658 ]659 660 # Submit action661 submit_btn.click(662 fn=submit_survey,663 inputs=input_components,664 outputs=[sentiment_output, plot1, plot2, plot3, plot4, status]665 )666 667 # Reset action668 reset_btn.click(669 fn=reset_form,670 inputs=[],671 outputs=input_components672 )673 674 # Download action - fixed675 download_btn.click(676 fn=get_csv_file,677 inputs=[],678 outputs=file_download679 )680 681 # Footer682 gr.HTML(f"""683 <div style="text-align: center; padding: 20px; margin-top: 20px; background-color: maroon; border-radius: 8px; border:1px solid #dee2e6;">684 <p style="margin-bottom:10px; color:#6c757d; ">CSV saved to: <code>{os.path.abspath(CSV_FILE)}</code></p>685 <p style="color:#6c757d;">Thank you for helping us improve our programs and services</p>686 </div>687 """)688 689# Launch the application690if __name__ == "__main__":691 print("Starting Gradio server...")692 demo.launch()