|
import plotly.express as px |
|
import plotly.graph_objects as go |
|
from plotly.subplots import make_subplots |
|
import json |
|
from flask import Flask, render_template, request, session, send_file |
|
import pandas as pd |
|
import os |
|
import io |
|
import base64 |
|
import numpy as np |
|
from datetime import datetime |
|
from weasyprint import HTML |
|
import tempfile |
|
import warnings |
|
import secrets |
|
from typing import Dict, List, Tuple |
|
|
|
app = Flask(__name__) |
|
app.secret_key = secrets.token_hex(16) |
|
app.config['SESSION_TYPE'] = 'filesystem' |
|
warnings.filterwarnings('ignore') |
|
|
|
|
|
@app.template_filter('format_number') |
|
def format_number(value): |
|
"""Format number with commas as thousand separators""" |
|
try: |
|
return "{:,}".format(int(value)) |
|
except (ValueError, TypeError): |
|
return value |
|
|
|
|
|
plot_dir = 'static/plots' |
|
os.makedirs(plot_dir, exist_ok=True) |
|
|
|
def save_plot(fig, plots_dict, plot_name): |
|
"""Convert Plotly figure to HTML and add to plots dictionary.""" |
|
try: |
|
plot_html = fig.to_html(full_html=False, include_plotlyjs='cdn') |
|
plots_dict[plot_name] = plot_html |
|
except Exception as e: |
|
print(f"Error saving plot {plot_name}: {str(e)}") |
|
|
|
@app.route('/') |
|
def index(): |
|
"""Render the main page.""" |
|
return render_template('index.html') |
|
|
|
from flask import jsonify |
|
|
|
@app.route('/analyze', methods=['POST']) |
|
def analyze(): |
|
"""Handle file upload and store data for later analysis.""" |
|
if 'file' not in request.files: |
|
return render_template('index.html', error='No file uploaded') |
|
|
|
file = request.files['file'] |
|
if file.filename == '': |
|
return render_template('index.html', error='No file selected') |
|
|
|
if not file.filename.lower().endswith('.csv'): |
|
return render_template('index.html', error='Only CSV files are allowed') |
|
|
|
try: |
|
|
|
data = pd.read_csv(file, encoding='utf-8') |
|
validate_data(data) |
|
|
|
|
|
return render_template('index.html', |
|
show_scholarship_questionnaire=True, |
|
file_uploaded=True |
|
) |
|
|
|
except Exception as e: |
|
return render_template('index.html', error=f'An error occurred: {str(e)}') |
|
|
|
def validate_data(data): |
|
"""Validate the uploaded data.""" |
|
if data.empty: |
|
raise ValueError("The uploaded file is empty") |
|
|
|
required_columns = [ |
|
'Category', 'Scholarship Name', 'Eligibility', 'Benefits', |
|
'Provider', 'Year', 'Number of Beneficiaries', |
|
'Total Students Eligible', 'Percentage Benefited' |
|
] |
|
|
|
missing_columns = [col for col in required_columns if col not in data.columns] |
|
if missing_columns: |
|
raise ValueError(f"Missing required columns: {', '.join(missing_columns)}") |
|
|
|
|
|
numeric_columns = ['Number of Beneficiaries', 'Total Students Eligible', 'Percentage Benefited'] |
|
for col in numeric_columns: |
|
if not pd.to_numeric(data[col], errors='coerce').notnull().all(): |
|
raise ValueError(f"Column '{col}' contains invalid numeric values") |
|
|
|
return True |
|
|
|
def perform_scholarship_swot(data): |
|
"""Perform SWOT analysis for scholarship data.""" |
|
swot = { |
|
'strengths': [], |
|
'weaknesses': [], |
|
'opportunities': [], |
|
'threats': [] |
|
} |
|
|
|
total_beneficiaries = data['Number of Beneficiaries'].sum() |
|
total_eligible = data['Total Students Eligible'].sum() |
|
coverage_rate = (total_beneficiaries / total_eligible) * 100 |
|
|
|
|
|
if coverage_rate >= 75: |
|
swot['strengths'].append(f"High coverage rate of {coverage_rate:.1f}%") |
|
elif coverage_rate >= 50: |
|
swot['opportunities'].append(f"Room to improve coverage rate (currently {coverage_rate:.1f}%)") |
|
swot['weaknesses'].append(f"Moderate coverage rate needs improvement ({coverage_rate:.1f}%)") |
|
else: |
|
swot['weaknesses'].append(f"Low coverage rate of {coverage_rate:.1f}%") |
|
|
|
|
|
yearly_beneficiaries = data.groupby('Year')['Number of Beneficiaries'].sum() |
|
if len(yearly_beneficiaries) > 1: |
|
growth_rate = ((yearly_beneficiaries.iloc[-1] / yearly_beneficiaries.iloc[0]) - 1) * 100 |
|
if growth_rate > 20: |
|
swot['strengths'].append(f"Strong growth in beneficiaries ({growth_rate:.1f}% overall)") |
|
elif growth_rate > 0: |
|
swot['opportunities'].append(f"Moderate growth can be improved ({growth_rate:.1f}% overall)") |
|
else: |
|
swot['weaknesses'].append(f"Negative growth in beneficiaries ({growth_rate:.1f}% overall)") |
|
swot['threats'].append("Declining participation trend") |
|
|
|
|
|
provider_count = data['Provider'].nunique() |
|
if provider_count > 5: |
|
swot['strengths'].append(f"Diverse range of providers ({provider_count} different providers)") |
|
else: |
|
swot['weaknesses'].append(f"Limited provider diversity (only {provider_count} providers)") |
|
swot['opportunities'].append("Potential to expand provider network") |
|
|
|
|
|
category_dist = data.groupby('Category')['Number of Beneficiaries'].sum() |
|
max_category_pct = (category_dist.max() / total_beneficiaries) * 100 |
|
min_category_pct = (category_dist.min() / total_beneficiaries) * 100 |
|
|
|
if max_category_pct > 40: |
|
swot['weaknesses'].append(f"Uneven distribution across categories (highest: {max_category_pct:.1f}%)") |
|
if min_category_pct < 10: |
|
swot['weaknesses'].append(f"Underrepresented categories (lowest: {min_category_pct:.1f}%)") |
|
|
|
|
|
utilization_rates = (data['Number of Beneficiaries'] / data['Total Students Eligible']) * 100 |
|
low_util_count = (utilization_rates < 30).sum() |
|
if low_util_count > 0: |
|
swot['weaknesses'].append(f"Low utilization in {low_util_count} scholarship programs") |
|
|
|
|
|
swot['opportunities'].append("Potential for new scholarship categories") |
|
swot['threats'].extend([ |
|
"Changes in funding availability may affect program sustainability", |
|
"Increasing competition for limited scholarship resources", |
|
"Changing eligibility criteria may affect accessibility" |
|
]) |
|
|
|
return swot |
|
|
|
@app.route('/analyze_diversity', methods=['POST']) |
|
def analyze_diversity(): |
|
"""Handle diversity file upload and store data.""" |
|
if 'file' not in request.files: |
|
return render_template('index.html', error='No file uploaded') |
|
|
|
file = request.files['file'] |
|
if file.filename == '': |
|
return render_template('index.html', error='No file selected') |
|
|
|
try: |
|
data = pd.read_csv(file, encoding='utf-8') |
|
required_columns = ['Gender', 'Category', 'Branch'] |
|
|
|
missing_columns = [col for col in required_columns if col not in data.columns] |
|
if missing_columns: |
|
return render_template('index.html', |
|
error=f'Missing required columns: {", ".join(missing_columns)}') |
|
|
|
|
|
return render_template('index.html', |
|
show_diversity_questionnaire=True, |
|
file_uploaded=True |
|
) |
|
|
|
except Exception as e: |
|
return render_template('index.html', error=f'An error occurred: {str(e)}') |
|
|
|
def generate_scholarship_plots(data): |
|
"""Generate Plotly plots for scholarship data.""" |
|
plots = {} |
|
|
|
try: |
|
|
|
beneficiaries_trend = data.groupby('Year')['Number of Beneficiaries'].sum().reset_index() |
|
if not beneficiaries_trend.empty: |
|
fig = px.line(beneficiaries_trend, |
|
x='Year', |
|
y='Number of Beneficiaries', |
|
markers=True, |
|
title='Trend of Scholarship Beneficiaries Over Years') |
|
fig.update_layout( |
|
template='plotly_white', |
|
xaxis_title='Year', |
|
yaxis_title='Number of Beneficiaries' |
|
) |
|
save_plot(fig, plots, 'trend_analysis') |
|
|
|
|
|
category_data = data.groupby('Category')['Number of Beneficiaries'].sum() |
|
fig = px.pie(values=category_data.values, |
|
names=category_data.index, |
|
title='Scholarship Distribution by Category') |
|
fig.update_layout(template='plotly_white') |
|
save_plot(fig, plots, 'category_distribution') |
|
|
|
|
|
provider_data = data.groupby('Provider')['Number of Beneficiaries'].sum() |
|
fig = px.bar(x=provider_data.index, |
|
y=provider_data.values, |
|
title='Distribution by Scholarship Provider') |
|
fig.update_layout( |
|
template='plotly_white', |
|
xaxis_title='Provider', |
|
yaxis_title='Number of Beneficiaries', |
|
xaxis_tickangle=45 |
|
) |
|
save_plot(fig, plots, 'provider_distribution') |
|
|
|
except Exception as e: |
|
print(f"Error in generate_scholarship_plots: {str(e)}") |
|
|
|
return plots |
|
|
|
def generate_diversity_plots(data): |
|
"""Generate Plotly plots for diversity data.""" |
|
plots = {} |
|
|
|
try: |
|
|
|
gender_percentile = data.groupby('Gender')['Percentile_obtained_in_entrance'].mean() |
|
fig = make_subplots(specs=[[{"secondary_y": True}]]) |
|
|
|
|
|
gender_counts = data['Gender'].value_counts() |
|
fig.add_trace( |
|
go.Bar(x=gender_counts.index, y=gender_counts.values, name="Count"), |
|
secondary_y=False |
|
) |
|
|
|
|
|
fig.add_trace( |
|
go.Scatter(x=gender_percentile.index, y=gender_percentile.values, |
|
name="Avg. Percentile", line=dict(color='red')), |
|
secondary_y=True |
|
) |
|
|
|
fig.update_layout( |
|
title='Gender Distribution and Average Entrance Percentile', |
|
template='plotly_white', |
|
barmode='group' |
|
) |
|
fig.update_yaxes(title_text="Number of Students", secondary_y=False) |
|
fig.update_yaxes(title_text="Average Entrance Percentile", secondary_y=True) |
|
|
|
save_plot(fig, plots, 'gender_distribution') |
|
|
|
|
|
branch_metrics = data.groupby('Branch').agg({ |
|
'Percentile_obtained_in_entrance': 'mean', |
|
'Board_Percentage': 'mean' |
|
}).round(2) |
|
|
|
fig = make_subplots(specs=[[{"secondary_y": True}]]) |
|
|
|
|
|
fig.add_trace( |
|
go.Bar(x=branch_metrics.index, |
|
y=branch_metrics['Percentile_obtained_in_entrance'], |
|
name="Entrance Percentile"), |
|
secondary_y=False |
|
) |
|
|
|
|
|
fig.add_trace( |
|
go.Scatter(x=branch_metrics.index, |
|
y=branch_metrics['Board_Percentage'], |
|
name="Board %", line=dict(color='red')), |
|
secondary_y=True |
|
) |
|
|
|
fig.update_layout( |
|
title='Branch-wise Performance Metrics', |
|
template='plotly_white', |
|
xaxis_tickangle=45 |
|
) |
|
fig.update_yaxes(title_text="Average Entrance Percentile", secondary_y=False) |
|
fig.update_yaxes(title_text="Average Board Percentage", secondary_y=True) |
|
|
|
save_plot(fig, plots, 'branch_distribution') |
|
|
|
|
|
fig = go.Figure() |
|
|
|
fig.add_trace(go.Box( |
|
x=data['Category'], |
|
y=data['Percentile_obtained_in_entrance'], |
|
name='Entrance Percentile' |
|
)) |
|
|
|
fig.update_layout( |
|
title='Category-wise Entrance Percentile Distribution', |
|
template='plotly_white', |
|
yaxis_title='Entrance Percentile', |
|
xaxis_title='Category' |
|
) |
|
|
|
save_plot(fig, plots, 'category_distribution') |
|
|
|
|
|
numeric_cols = ['Percentile_obtained_in_entrance', 'Board_Percentage'] |
|
corr_matrix = data[numeric_cols].corr() |
|
|
|
fig = go.Figure(data=go.Heatmap( |
|
z=corr_matrix.values, |
|
x=corr_matrix.columns, |
|
y=corr_matrix.columns, |
|
colorscale='RdBu', |
|
zmin=-1, zmax=1 |
|
)) |
|
|
|
fig.update_layout( |
|
title='Performance Correlation Matrix', |
|
template='plotly_white' |
|
) |
|
|
|
save_plot(fig, plots, 'correlation_matrix') |
|
|
|
except Exception as e: |
|
print(f"Error in generate_diversity_plots: {str(e)}") |
|
|
|
return plots |
|
|
|
|
|
def analyze_performance(data): |
|
"""Analyze strengths and weaknesses of the scholarship program.""" |
|
insights = { |
|
'strengths': [], |
|
'weaknesses': [], |
|
'improvements': [] |
|
} |
|
|
|
|
|
total_beneficiaries = data['Number of Beneficiaries'].sum() |
|
total_eligible = data['Total Students Eligible'].sum() |
|
coverage_rate = (total_beneficiaries / total_eligible) * 100 |
|
|
|
|
|
if coverage_rate >= 75: |
|
insights['strengths'].append(f"Exceptional coverage rate of {coverage_rate:.1f}%") |
|
elif coverage_rate >= 50: |
|
insights['strengths'].append(f"Good coverage rate of {coverage_rate:.1f}%") |
|
else: |
|
insights['weaknesses'].append(f"Low coverage rate of {coverage_rate:.1f}%") |
|
insights['improvements'].append("Implement awareness campaigns to increase scholarship applications") |
|
|
|
|
|
yearly_data = data.groupby('Year')['Number of Beneficiaries'].sum() |
|
if len(yearly_data) > 1: |
|
yoy_growth = ((yearly_data.iloc[-1] - yearly_data.iloc[0]) / yearly_data.iloc[0]) * 100 |
|
if yoy_growth > 0: |
|
insights['strengths'].append(f"Positive growth in beneficiaries ({yoy_growth:.1f}% overall)") |
|
else: |
|
insights['weaknesses'].append(f"Declining number of beneficiaries ({abs(yoy_growth):.1f}% decrease)") |
|
insights['improvements'].append("Review and revise scholarship allocation strategy") |
|
|
|
|
|
provider_count = data['Provider'].nunique() |
|
if provider_count > 10: |
|
insights['strengths'].append(f"Diverse range of scholarship providers ({provider_count} providers)") |
|
else: |
|
insights['weaknesses'].append(f"Limited number of scholarship providers ({provider_count} providers)") |
|
insights['improvements'].append("Engage with more institutions and organizations for scholarship partnerships") |
|
|
|
|
|
category_dist = data.groupby('Category')['Number of Beneficiaries'].sum() |
|
max_category_pct = (category_dist.max() / total_beneficiaries) * 100 |
|
if max_category_pct > 50: |
|
insights['weaknesses'].append(f"Uneven distribution across categories (max {max_category_pct:.1f}% in one category)") |
|
insights['improvements'].append("Balance scholarship distribution across different categories") |
|
else: |
|
insights['strengths'].append("Well-balanced distribution across categories") |
|
|
|
|
|
utilization_rates = (data['Number of Beneficiaries'] / data['Total Students Eligible']) * 100 |
|
low_util_count = (utilization_rates < 30).sum() |
|
if low_util_count > 0: |
|
insights['weaknesses'].append(f"{low_util_count} scholarships have utilization rates below 30%") |
|
insights['improvements'].append("Review eligibility criteria for low-utilization scholarships") |
|
|
|
|
|
insights['improvements'].extend([ |
|
"Develop targeted outreach programs for underrepresented groups", |
|
"Streamline application process to increase accessibility", |
|
"Implement regular feedback mechanisms from beneficiaries" |
|
]) |
|
|
|
return insights |
|
|
|
def perform_swot_analysis(data): |
|
"""Perform SWOT analysis on the diversity dataset.""" |
|
swot = { |
|
'strengths': [], |
|
'weaknesses': [], |
|
'opportunities': [], |
|
'threats': [] |
|
} |
|
|
|
try: |
|
|
|
gender_dist = data['Gender'].value_counts(normalize=True) * 100 |
|
female_ratio = gender_dist.get('Female', 0) |
|
|
|
if female_ratio >= 40: |
|
swot['strengths'].append(f"Strong gender diversity with {female_ratio:.1f}% female students") |
|
swot['strengths'].append("Above average female representation in STEM fields") |
|
elif female_ratio >= 30: |
|
swot['opportunities'].append(f"Potential to improve gender diversity (currently {female_ratio:.1f}% female students)") |
|
swot['opportunities'].append("Implement targeted recruitment for female students") |
|
else: |
|
swot['weaknesses'].append(f"Low gender diversity with only {female_ratio:.1f}% female students") |
|
swot['threats'].append("Risk of gender imbalance affecting campus culture") |
|
|
|
|
|
avg_percentile = data['Percentile_obtained_in_entrance'].mean() |
|
std_percentile = data['Percentile_obtained_in_entrance'].std() |
|
|
|
if avg_percentile >= 80: |
|
swot['strengths'].append(f"High average entrance percentile ({avg_percentile:.1f})") |
|
swot['strengths'].append("Strong academic caliber of incoming students") |
|
elif avg_percentile < 60: |
|
swot['weaknesses'].append(f"Low average entrance percentile ({avg_percentile:.1f})") |
|
swot['threats'].append("May affect institution's academic reputation") |
|
|
|
if std_percentile > 20: |
|
swot['weaknesses'].append("High variability in student performance") |
|
swot['opportunities'].append("Implement targeted academic support programs") |
|
|
|
|
|
category_dist = data['Category'].value_counts(normalize=True) * 100 |
|
for category, percentage in category_dist.items(): |
|
if percentage < 10: |
|
swot['weaknesses'].append(f"Low representation of {category} category ({percentage:.1f}%)") |
|
swot['opportunities'].append(f"Increase outreach to {category} category students") |
|
swot['threats'].append(f"Risk of {category} category underrepresentation") |
|
|
|
|
|
branch_dist = data['Branch'].value_counts(normalize=True) * 100 |
|
branches_above_25 = branch_dist[branch_dist > 25].index.tolist() |
|
branches_below_10 = branch_dist[branch_dist < 10].index.tolist() |
|
|
|
if branches_above_25: |
|
swot['strengths'].append(f"Strong presence in: {', '.join(branches_above_25)}") |
|
swot['threats'].append("Over-dependence on specific branches") |
|
|
|
if branches_below_10: |
|
swot['opportunities'].append(f"Potential for growth in: {', '.join(branches_below_10)}") |
|
swot['weaknesses'].append(f"Limited presence in: {', '.join(branches_below_10)}") |
|
|
|
|
|
corr = data['Percentile_obtained_in_entrance'].corr(data['Board_Percentage']) |
|
if corr > 0.7: |
|
swot['strengths'].append("Strong correlation between board and entrance performance") |
|
swot['strengths'].append("Consistent academic performance across evaluations") |
|
elif corr < 0.3: |
|
swot['weaknesses'].append("Weak correlation between board and entrance performance") |
|
swot['opportunities'].append("Investigate factors affecting performance inconsistency") |
|
swot['threats'].append("Unpredictable student performance patterns") |
|
|
|
|
|
swot['opportunities'].extend([ |
|
"Develop mentorship programs for underrepresented groups", |
|
"Implement cross-branch collaborative programs", |
|
"Create targeted support systems for struggling students" |
|
]) |
|
|
|
swot['threats'].extend([ |
|
"Increasing competition from other institutions", |
|
"Changing diversity trends in higher education", |
|
"Resource allocation challenges across diverse student needs" |
|
]) |
|
|
|
except Exception as e: |
|
print(f"Error in perform_swot_analysis: {str(e)}") |
|
swot['weaknesses'].append("Error in data analysis") |
|
|
|
return swot |
|
|
|
def generate_diversity_insights(data): |
|
"""Generate insights from diversity data.""" |
|
insights = [] |
|
|
|
|
|
total_students = len(data) |
|
insights.append(f"Total number of students: {total_students:,}") |
|
|
|
|
|
if 'Gender' in data.columns: |
|
gender_dist = data['Gender'].value_counts() |
|
for gender, count in gender_dist.items(): |
|
percentage = (count/total_students) * 100 |
|
insights.append(f"{gender}: {count:,} students ({percentage:.1f}%)") |
|
|
|
|
|
if 'Branch' in data.columns: |
|
branch_dist = data['Branch'].value_counts() |
|
insights.append("\nTop 3 branches by enrollment:") |
|
for branch, count in branch_dist.nlargest(3).items(): |
|
percentage = (count/total_students) * 100 |
|
insights.append(f"{branch}: {count:,} students ({percentage:.1f}%)") |
|
|
|
|
|
if 'Category' in data.columns: |
|
category_dist = data['Category'].value_counts() |
|
insights.append("\nCategory distribution:") |
|
for category, count in category_dist.items(): |
|
percentage = (count/total_students) * 100 |
|
insights.append(f"{category}: {count:,} students ({percentage:.1f}%)") |
|
|
|
|
|
if 'Percentile_obtained_in_entrance' in data.columns: |
|
avg_percentile = data['Percentile_obtained_in_entrance'].mean() |
|
max_percentile = data['Percentile_obtained_in_entrance'].max() |
|
min_percentile = data['Percentile_obtained_in_entrance'].min() |
|
insights.append("\nEntrance Exam Performance:") |
|
insights.append(f"Average Percentile: {avg_percentile:.2f}") |
|
insights.append(f"Highest Percentile: {max_percentile:.2f}") |
|
insights.append(f"Lowest Percentile: {min_percentile:.2f}") |
|
|
|
|
|
if 'Board_Percentage' in data.columns: |
|
avg_board = data['Board_Percentage'].mean() |
|
max_board = data['Board_Percentage'].max() |
|
min_board = data['Board_Percentage'].min() |
|
insights.append("\nBoard Exam Performance:") |
|
insights.append(f"Average Percentage: {avg_board:.2f}%") |
|
insights.append(f"Highest Percentage: {max_board:.2f}%") |
|
insights.append(f"Lowest Percentage: {min_board:.2f}%") |
|
|
|
|
|
if all(col in data.columns for col in ['Category', 'Percentile_obtained_in_entrance']): |
|
insights.append("\nCategory-wise Average Entrance Percentile:") |
|
cat_perf = data.groupby('Category')['Percentile_obtained_in_entrance'].mean() |
|
for category, avg in cat_perf.items(): |
|
insights.append(f"{category}: {avg:.2f}") |
|
|
|
return insights |
|
|
|
@app.route('/analyze_scholarship_questionnaire', methods=['POST']) |
|
def analyze_scholarship_questionnaire(): |
|
"""Analyze scholarship data with questionnaire responses.""" |
|
try: |
|
if 'file' not in request.files: |
|
return render_template('index.html', error='Please upload the data file again') |
|
|
|
file = request.files['file'] |
|
if file.filename == '': |
|
return render_template('index.html', error='No file selected') |
|
|
|
data = pd.read_csv(file, encoding='utf-8') |
|
|
|
|
|
plots = generate_scholarship_plots(data) |
|
insights = analyze_performance(data) |
|
swot = perform_scholarship_swot(data) |
|
|
|
|
|
mentorship_programs = request.form['mentorship_programs'] == 'yes' |
|
career_guidance = request.form['career_guidance'] == 'yes' |
|
academic_support = request.form['academic_support'] == 'yes' |
|
graduation_rate = float(request.form['graduation_rate']) |
|
application_success_rate = float(request.form['application_success_rate']) |
|
funding_sustainability = int(request.form['funding_sustainability']) |
|
|
|
|
|
if mentorship_programs: |
|
swot['strengths'].append("Active mentorship program for scholarship recipients") |
|
else: |
|
swot['opportunities'].append("Implement mentorship program for better student support") |
|
|
|
if career_guidance: |
|
swot['strengths'].append("Career guidance services available") |
|
else: |
|
swot['opportunities'].append("Introduce career development services") |
|
|
|
if academic_support: |
|
swot['strengths'].append("Academic support system in place") |
|
else: |
|
swot['weaknesses'].append("Lack of academic support services") |
|
|
|
if graduation_rate >= 85: |
|
swot['strengths'].append(f"High graduation rate ({graduation_rate:.1f}%)") |
|
elif graduation_rate < 70: |
|
swot['weaknesses'].append(f"Low graduation rate ({graduation_rate:.1f}%)") |
|
|
|
if application_success_rate >= 75: |
|
swot['strengths'].append(f"High application success rate ({application_success_rate:.1f}%)") |
|
elif application_success_rate < 50: |
|
swot['weaknesses'].append(f"Low application success rate ({application_success_rate:.1f}%)") |
|
|
|
if funding_sustainability >= 5: |
|
swot['strengths'].append(f"Secure funding for {funding_sustainability} years") |
|
else: |
|
swot['threats'].append("Limited long-term funding security") |
|
|
|
return render_template('index.html', |
|
plots=plots, |
|
insights=insights, |
|
swot=swot, |
|
show_scholarship_results=True, |
|
hide_questionnaire=True |
|
) |
|
|
|
except Exception as e: |
|
return render_template('index.html', error=f'An error occurred: {str(e)}') |
|
|
|
@app.route('/analyze_diversity_questionnaire', methods=['POST']) |
|
def analyze_diversity_questionnaire(): |
|
"""Analyze diversity data with questionnaire responses.""" |
|
try: |
|
|
|
if 'file' not in request.files: |
|
return render_template('index.html', error='Please upload the data file again') |
|
|
|
file = request.files['file'] |
|
if file.filename == '': |
|
return render_template('index.html', error='No file selected') |
|
|
|
data = pd.read_csv(file, encoding='utf-8') |
|
|
|
|
|
students_with_disabilities = int(request.form['students_with_disabilities']) |
|
first_gen_students = int(request.form['first_gen_students']) |
|
international_students = int(request.form['international_students']) |
|
|
|
|
|
student_faculty_ratio = float(request.form['student_faculty_ratio']) |
|
avg_class_size = float(request.form['avg_class_size']) |
|
research_active_faculty = float(request.form['research_active_faculty']) |
|
|
|
|
|
avg_age = float(request.form['avg_age']) |
|
retention_rate = float(request.form['retention_rate']) |
|
graduation_rate = float(request.form['graduation_rate']) |
|
|
|
|
|
counseling_services = request.form['counseling_services'] == 'yes' |
|
career_services = request.form['career_services'] == 'yes' |
|
tutoring_services = request.form['tutoring_services'] == 'yes' |
|
|
|
|
|
housing_capacity = float(request.form['housing_capacity']) |
|
student_organizations = int(request.form['student_organizations']) |
|
athletic_programs = int(request.form['athletic_programs']) |
|
|
|
|
|
total_students = data['Number_of_Students'].sum() if 'Number_of_Students' in data.columns else 0 |
|
if total_students == 0: |
|
|
|
total_students = max( |
|
students_with_disabilities + first_gen_students + international_students, |
|
int(avg_class_size * student_faculty_ratio) |
|
) |
|
|
|
|
|
plots = generate_diversity_plots(data) |
|
|
|
|
|
swot = { |
|
'strengths': [], |
|
'weaknesses': [], |
|
'opportunities': [], |
|
'threats': [] |
|
} |
|
|
|
|
|
if counseling_services and career_services and tutoring_services: |
|
swot['strengths'].append("Comprehensive student support services") |
|
else: |
|
swot['weaknesses'].append("Gaps in student support services") |
|
|
|
if housing_capacity >= 60: |
|
swot['strengths'].append(f"Strong residential community ({housing_capacity:.1f}% capacity)") |
|
elif housing_capacity < 30: |
|
swot['weaknesses'].append("Limited residential facilities") |
|
|
|
if student_organizations > 50: |
|
swot['strengths'].append(f"Vibrant campus life with {student_organizations} organizations") |
|
else: |
|
swot['opportunities'].append("Room for more student organizations") |
|
|
|
if research_active_faculty >= 70: |
|
swot['strengths'].append(f"Strong research faculty ({research_active_faculty:.1f}%)") |
|
elif research_active_faculty < 40: |
|
swot['weaknesses'].append("Limited research activity") |
|
|
|
if avg_class_size <= 25: |
|
swot['strengths'].append(f"Small class sizes (avg: {avg_class_size:.1f})") |
|
elif avg_class_size > 40: |
|
swot['weaknesses'].append("Large class sizes") |
|
|
|
|
|
disability_percentage = (students_with_disabilities / total_students * 100) |
|
first_gen_percentage = (first_gen_students / total_students * 100) |
|
international_percentage = (international_students / total_students * 100) |
|
|
|
if disability_percentage >= 5: |
|
swot['strengths'].append(f"Good support for students with disabilities ({disability_percentage:.1f}%)") |
|
else: |
|
swot['opportunities'].append("Enhance accessibility and support services") |
|
|
|
if first_gen_percentage >= 30: |
|
swot['strengths'].append(f"Strong first-generation student representation ({first_gen_percentage:.1f}%)") |
|
else: |
|
swot['opportunities'].append("Expand first-generation student outreach") |
|
|
|
if international_percentage >= 10: |
|
swot['strengths'].append(f"Good international diversity ({international_percentage:.1f}%)") |
|
else: |
|
swot['opportunities'].append("Increase international student recruitment") |
|
|
|
return render_template('index.html', |
|
plots=plots, |
|
swot=swot, |
|
show_results=True |
|
) |
|
|
|
except Exception as e: |
|
return render_template('index.html', error=f'An error occurred: {str(e)}') |
|
|
|
|
|
@app.route('/analyze_combined', methods=['POST']) |
|
def analyze_combined(): |
|
"""Handle combined analysis of scholarship and diversity data.""" |
|
try: |
|
|
|
scholarship_plots = {} |
|
diversity_plots = {} |
|
scholarship_swot = {'strengths': [], 'weaknesses': [], 'opportunities': [], 'threats': []} |
|
diversity_swot = {'strengths': [], 'weaknesses': [], 'opportunities': [], 'threats': []} |
|
combined_insights = {'scholarship': {}, 'diversity': []} |
|
total_students = 0 |
|
|
|
|
|
is_pdf_request = request.args.get('format') == 'pdf' |
|
|
|
|
|
if not is_pdf_request: |
|
|
|
scholarship_file = request.files['scholarship_file'] |
|
diversity_file = request.files['diversity_file'] |
|
|
|
if not scholarship_file or not diversity_file: |
|
return render_template('index.html', error='Please upload both files') |
|
|
|
try: |
|
|
|
scholarship_data = pd.read_csv(scholarship_file, encoding='utf-8') |
|
diversity_data = pd.read_csv(diversity_file, encoding='utf-8') |
|
|
|
validate_data(scholarship_data) |
|
|
|
|
|
scholarship_plots = generate_scholarship_plots(scholarship_data) |
|
diversity_plots = generate_diversity_plots(diversity_data) |
|
scholarship_swot = perform_scholarship_swot(scholarship_data) |
|
diversity_swot = perform_swot_analysis(diversity_data) |
|
combined_insights = { |
|
'scholarship': analyze_performance(scholarship_data), |
|
'diversity': generate_diversity_insights(diversity_data) |
|
} |
|
|
|
|
|
total_students = len(diversity_data) |
|
|
|
|
|
session['total_students'] = total_students |
|
session['scholarship_swot'] = scholarship_swot |
|
session['diversity_swot'] = diversity_swot |
|
|
|
except Exception as e: |
|
return render_template('index.html', error=f'Error processing files: {str(e)}') |
|
|
|
|
|
scholarship_metrics = { |
|
'graduation_rate': float(request.form['scholarship_graduation_rate']), |
|
'application_success_rate': float(request.form['application_success_rate']), |
|
'funding_sustainability': int(request.form['funding_sustainability']), |
|
'mentorship_programs': request.form.get('mentorship_programs'), |
|
'career_guidance': request.form.get('career_guidance'), |
|
'academic_support': request.form.get('academic_support') |
|
} |
|
|
|
diversity_metrics = { |
|
'total_students': total_students, |
|
'students_with_disabilities': int(request.form['students_with_disabilities']), |
|
'first_gen_students': int(request.form['first_gen_students']), |
|
'international_students': int(request.form['international_students']), |
|
'student_faculty_ratio': float(request.form['student_faculty_ratio']), |
|
'research_active_faculty': float(request.form['research_active_faculty']) |
|
} |
|
|
|
scholarship_score, scholarship_explanations = calculate_scholarship_score( |
|
scholarship_data, scholarship_metrics) |
|
diversity_score, diversity_explanations = calculate_diversity_score( |
|
diversity_data, diversity_metrics) |
|
|
|
|
|
overall_score = (scholarship_score + diversity_score) / 2 |
|
|
|
return render_template('index.html', |
|
show_combined_results=True, |
|
scholarship_plots=scholarship_plots, |
|
diversity_plots=diversity_plots, |
|
scholarship_swot=scholarship_swot, |
|
diversity_swot=diversity_swot, |
|
combined_insights=combined_insights, |
|
hide_questionnaire=True, |
|
total_students=total_students, |
|
graduation_rate=scholarship_metrics['graduation_rate'], |
|
application_success_rate=scholarship_metrics['application_success_rate'], |
|
international_students=diversity_metrics['international_students'], |
|
student_faculty_ratio=diversity_metrics['student_faculty_ratio'], |
|
research_active_faculty=diversity_metrics['research_active_faculty'], |
|
first_gen_students=diversity_metrics['first_gen_students'], |
|
students_with_disabilities=diversity_metrics['students_with_disabilities'], |
|
|
|
scholarship_score=scholarship_score, |
|
scholarship_explanations=scholarship_explanations, |
|
diversity_score=diversity_score, |
|
diversity_explanations=diversity_explanations, |
|
overall_score=overall_score |
|
) |
|
|
|
except Exception as e: |
|
return render_template('index.html', error=f'An error occurred: {str(e)}') |
|
|
|
def generate_pdf_report(data): |
|
"""Generate and return a PDF report.""" |
|
try: |
|
|
|
html_content = render_template('pdf_report.html', **data) |
|
|
|
|
|
with tempfile.NamedTemporaryFile(suffix='.pdf', delete=False) as tmp: |
|
|
|
HTML(string=html_content).write_pdf(tmp.name) |
|
|
|
|
|
return send_file( |
|
tmp.name, |
|
mimetype='application/pdf', |
|
as_attachment=True, |
|
download_name='educational_data_analysis_report.pdf' |
|
) |
|
|
|
except Exception as e: |
|
return render_template('index.html', error=f'Error generating PDF: {str(e)}') |
|
|
|
@app.route('/download_report') |
|
def download_report(): |
|
"""Generate and download PDF report.""" |
|
try: |
|
|
|
html_content = render_template('pdf_report.html', |
|
timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), |
|
total_students=session.get('total_students', 0), |
|
graduation_rate=session.get('graduation_rate', 0), |
|
application_success_rate=session.get('application_success_rate', 0), |
|
international_students=session.get('international_students', 0), |
|
student_faculty_ratio=session.get('student_faculty_ratio', 0), |
|
research_active_faculty=session.get('research_active_faculty', 0), |
|
first_gen_students=session.get('first_gen_students', 0), |
|
students_with_disabilities=session.get('students_with_disabilities', 0), |
|
scholarship_swot=session.get('scholarship_swot', {'strengths': [], 'weaknesses': [], 'opportunities': [], 'threats': []}), |
|
diversity_swot=session.get('diversity_swot', {'strengths': [], 'weaknesses': [], 'opportunities': [], 'threats': []}) |
|
) |
|
|
|
|
|
with tempfile.NamedTemporaryFile(suffix='.pdf', delete=False) as tmp: |
|
|
|
HTML(string=html_content).write_pdf(tmp.name) |
|
|
|
|
|
return send_file( |
|
tmp.name, |
|
mimetype='application/pdf', |
|
as_attachment=True, |
|
download_name='educational_data_analysis_report.pdf' |
|
) |
|
|
|
except Exception as e: |
|
return render_template('index.html', error=f'Error generating PDF: {str(e)}') |
|
|
|
def calculate_scholarship_score(data: pd.DataFrame, metrics: Dict) -> Tuple[int, List[str]]: |
|
""" |
|
Calculate scholarship score out of 100 based on various metrics. |
|
Returns tuple of (score, explanations) |
|
""" |
|
score = 0 |
|
explanations = [] |
|
|
|
|
|
coverage_rate = (data['Number of Beneficiaries'].sum() / data['Total Students Eligible'].sum()) * 100 |
|
if coverage_rate >= 75: |
|
score += 30 |
|
explanations.append("Excellent coverage rate (30/30)") |
|
elif coverage_rate >= 50: |
|
score += 20 |
|
explanations.append("Good coverage rate (20/30)") |
|
elif coverage_rate >= 25: |
|
score += 10 |
|
explanations.append("Fair coverage rate (10/30)") |
|
else: |
|
explanations.append("Poor coverage rate (0/30)") |
|
|
|
|
|
grad_rate = float(metrics.get('graduation_rate', 0)) |
|
app_success = float(metrics.get('application_success_rate', 0)) |
|
|
|
|
|
if grad_rate >= 90: |
|
score += 15 |
|
explanations.append("Outstanding graduation rate (15/15)") |
|
elif grad_rate >= 80: |
|
score += 10 |
|
explanations.append("Good graduation rate (10/15)") |
|
elif grad_rate >= 70: |
|
score += 5 |
|
explanations.append("Fair graduation rate (5/15)") |
|
else: |
|
explanations.append("Needs improvement in graduation rate (0/15)") |
|
|
|
|
|
if app_success >= 80: |
|
score += 10 |
|
explanations.append("High application success rate (10/10)") |
|
elif app_success >= 60: |
|
score += 5 |
|
explanations.append("Moderate application success rate (5/10)") |
|
else: |
|
explanations.append("Low application success rate (0/10)") |
|
|
|
|
|
support_score = 0 |
|
if metrics.get('mentorship_programs') == 'yes': |
|
support_score += 8 |
|
if metrics.get('career_guidance') == 'yes': |
|
support_score += 8 |
|
if metrics.get('academic_support') == 'yes': |
|
support_score += 9 |
|
score += support_score |
|
explanations.append(f"Support services score ({support_score}/25)") |
|
|
|
|
|
funding_years = int(metrics.get('funding_sustainability', 0)) |
|
if funding_years >= 5: |
|
score += 20 |
|
explanations.append("Strong funding sustainability (20/20)") |
|
elif funding_years >= 3: |
|
score += 15 |
|
explanations.append("Good funding sustainability (15/20)") |
|
elif funding_years >= 1: |
|
score += 10 |
|
explanations.append("Limited funding sustainability (10/20)") |
|
else: |
|
explanations.append("Funding sustainability concerns (0/20)") |
|
|
|
return score, explanations |
|
|
|
def calculate_diversity_score(data: pd.DataFrame, metrics: Dict) -> Tuple[int, List[str]]: |
|
""" |
|
Calculate diversity score out of 100 based on various metrics. |
|
Returns tuple of (score, explanations) |
|
""" |
|
score = 0 |
|
explanations = [] |
|
|
|
total_students = float(metrics.get('total_students', 0)) |
|
if total_students == 0: |
|
total_students = len(data) |
|
|
|
|
|
if 'Gender' in data.columns: |
|
gender_dist = data['Gender'].value_counts(normalize=True) |
|
min_gender_ratio = gender_dist.min() |
|
if min_gender_ratio >= 0.4: |
|
score += 25 |
|
explanations.append("Excellent gender balance (25/25)") |
|
elif min_gender_ratio >= 0.3: |
|
score += 20 |
|
explanations.append("Good gender balance (20/25)") |
|
elif min_gender_ratio >= 0.2: |
|
score += 15 |
|
explanations.append("Fair gender balance (15/25)") |
|
else: |
|
explanations.append("Gender balance needs improvement (0/25)") |
|
|
|
|
|
disabilities_pct = (float(metrics.get('students_with_disabilities', 0)) / total_students) * 100 |
|
if disabilities_pct >= 5: |
|
score += 15 |
|
explanations.append("Strong accessibility support (15/15)") |
|
elif disabilities_pct >= 3: |
|
score += 10 |
|
explanations.append("Good accessibility support (10/15)") |
|
else: |
|
explanations.append("Accessibility support needs improvement (0/15)") |
|
|
|
first_gen_pct = (float(metrics.get('first_gen_students', 0)) / total_students) * 100 |
|
if first_gen_pct >= 30: |
|
score += 10 |
|
explanations.append("Strong first-generation representation (10/10)") |
|
elif first_gen_pct >= 20: |
|
score += 5 |
|
explanations.append("Moderate first-generation representation (5/10)") |
|
else: |
|
explanations.append("First-generation representation needs improvement (0/10)") |
|
|
|
|
|
student_faculty = float(metrics.get('student_faculty_ratio', 0)) |
|
if student_faculty > 0 and student_faculty <= 15: |
|
score += 15 |
|
explanations.append("Excellent student-faculty ratio (15/15)") |
|
elif student_faculty <= 20: |
|
score += 10 |
|
explanations.append("Good student-faculty ratio (10/15)") |
|
elif student_faculty <= 25: |
|
score += 5 |
|
explanations.append("Fair student-faculty ratio (5/15)") |
|
else: |
|
explanations.append("Student-faculty ratio needs improvement (0/15)") |
|
|
|
research_faculty = float(metrics.get('research_active_faculty', 0)) |
|
if research_faculty >= 70: |
|
score += 10 |
|
explanations.append("Strong research activity (10/10)") |
|
elif research_faculty >= 50: |
|
score += 5 |
|
explanations.append("Moderate research activity (5/10)") |
|
else: |
|
explanations.append("Research activity needs improvement (0/10)") |
|
|
|
|
|
intl_pct = (float(metrics.get('international_students', 0)) / total_students) * 100 |
|
if intl_pct >= 15: |
|
score += 25 |
|
explanations.append("Excellent international diversity (25/25)") |
|
elif intl_pct >= 10: |
|
score += 20 |
|
explanations.append("Good international diversity (20/25)") |
|
elif intl_pct >= 5: |
|
score += 15 |
|
explanations.append("Fair international diversity (15/25)") |
|
else: |
|
explanations.append("International diversity needs improvement (0/25)") |
|
|
|
return score, explanations |
|
|
|
if __name__ == '__main__': |
|
app.run(host='0.0.0.0', port=7860) |
|
|