Spaces:
Sleeping
Sleeping
| import pandas as pd | |
| import matplotlib.pyplot as plt | |
| from io import BytesIO | |
| def analyze_results(input_file): | |
| """ | |
| Analyzes a VTU result Excel file with multi-level headers. | |
| Args: | |
| input_file: A file path or a file-like object (e.g., from a file uploader). | |
| Returns: | |
| BytesIO: An in-memory Excel file containing the analysis report. | |
| """ | |
| try: | |
| # Step 1: Read the Excel file, telling pandas the first two rows are the header. | |
| # This creates a MultiIndex for the columns. | |
| df = pd.read_excel(input_file, header=[0, 1]) | |
| # Step 2: Clean and flatten the multi-level column headers. | |
| # e.g., ('BCS401', 'IA') becomes 'BCS401_IA' | |
| # e.g., ('Class', 'Unnamed: ...') becomes 'Class' | |
| new_cols = [] | |
| for col in df.columns: | |
| if 'Unnamed' in str(col[1]): | |
| # This handles single-level headers like 'USN', 'Name', 'Class' | |
| new_cols.append(col[0]) | |
| else: | |
| # This joins multi-level headers like ('BCS401', 'Pass/Fail') | |
| new_cols.append(f"{col[0]}_{col[1]}") | |
| df.columns = new_cols | |
| except Exception as e: | |
| raise ValueError(f"Could not read the Excel file. Please ensure it has the correct two-level header format. Error: {e}") | |
| # --- 1. Analyze Pass Percentage for Each Subject --- | |
| analysis_data = [] | |
| total_students = len(df) | |
| # Find all columns that represent a subject's pass/fail status | |
| pass_fail_columns = [col for col in df.columns if col.endswith('_Pass/Fail')] | |
| if not pass_fail_columns: | |
| raise ValueError("Could not find any 'Pass/Fail' columns. Please check the Excel file headers.") | |
| for pf_col in pass_fail_columns: | |
| # Extract subject code from the column name (e.g., 'BCS401_Pass/Fail' -> 'BCS401') | |
| subject_code = pf_col.split('_')[0] | |
| pass_count = (df[pf_col] == 'P').sum() | |
| pass_percentage = (pass_count / total_students) * 100 if total_students > 0 else 0 | |
| analysis_data.append({ | |
| "Subject": subject_code, | |
| "Total Students": total_students, | |
| "Pass Count": pass_count, | |
| "Pass Percentage": f"{pass_percentage:.2f}%" | |
| }) | |
| pass_percentage_df = pd.DataFrame(analysis_data) | |
| pass_percentage_df['Pass Percentage (Numeric)'] = pass_percentage_df['Pass Percentage'].str.replace('%', '').astype(float) | |
| # --- 2. Analyze Class Distribution --- | |
| # This part will now work because the 'Class' column is correctly identified. | |
| class_distribution = df['Class'].value_counts().reset_index() | |
| class_distribution.columns = ['Class', 'Number of Students'] | |
| # --- 3. Create Charts in Memory (No changes needed here) --- | |
| # Bar Chart for Pass Percentage | |
| plt.figure(figsize=(10, 6)) | |
| bars = plt.bar(pass_percentage_df['Subject'], pass_percentage_df['Pass Percentage (Numeric)'], color='skyblue') | |
| plt.title('Pass Percentage per Subject', fontsize=16) | |
| plt.ylabel('Pass Percentage (%)', fontsize=12) | |
| plt.xlabel('Subject', fontsize=12) | |
| plt.xticks(rotation=45, ha='right') | |
| plt.ylim(0, 105) | |
| plt.grid(axis='y', linestyle='--', alpha=0.7) | |
| for bar in bars: | |
| yval = bar.get_height() | |
| plt.text(bar.get_x() + bar.get_width()/2.0, yval + 1, f'{yval:.1f}%', ha='center', va='bottom') | |
| plt.tight_layout() | |
| bar_chart_buffer = BytesIO() | |
| plt.savefig(bar_chart_buffer, format='png') | |
| plt.close() | |
| # Pie Chart for Class Distribution | |
| plt.figure(figsize=(8, 8)) | |
| plt.pie( | |
| class_distribution['Number of Students'], | |
| labels=class_distribution['Class'], | |
| autopct='%1.1f%%', | |
| startangle=140, | |
| colors=['#66b3ff','#ff9999','#99ff99','#ffcc99'] | |
| ) | |
| plt.title('Overall Class Distribution', fontsize=16) | |
| plt.axis('equal') | |
| pie_chart_buffer = BytesIO() | |
| plt.savefig(pie_chart_buffer, format='png') | |
| plt.close() | |
| # --- 4. Write Everything to an In-Memory Excel File (No changes needed here) --- | |
| output_buffer = BytesIO() | |
| with pd.ExcelWriter(output_buffer, engine='openpyxl') as writer: | |
| pass_percentage_df[['Subject', 'Total Students', 'Pass Count', 'Pass Percentage']].to_excel( | |
| writer, sheet_name='Dashboard', startrow=1, index=False | |
| ) | |
| class_distribution.to_excel( | |
| writer, sheet_name='Dashboard', startrow=1, startcol=6, index=False | |
| ) | |
| workbook = writer.book | |
| worksheet = writer.sheets['Dashboard'] | |
| worksheet['A1'] = "Subject Pass/Fail Analysis" | |
| worksheet['G1'] = "Class Distribution" | |
| from openpyxl.drawing.image import Image | |
| bar_chart_img = Image(bar_chart_buffer) | |
| worksheet.add_image(bar_chart_img, 'A10') | |
| pie_chart_img = Image(pie_chart_buffer) | |
| worksheet.add_image(pie_chart_img, 'J10') | |
| # We will write the cleaned data back to the report | |
| df.to_excel(writer, sheet_name='Raw Data', index=False) | |
| output_buffer.seek(0) | |
| return output_buffer |