File size: 23,340 Bytes
f94dafd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
from datetime import time
import os

from flask import Flask, render_template, request
import pandas as pd
import plotly.express as px
import io
import base64
import google.generativeai as genai
from collections import defaultdict
import numpy as np
import google.generativeai as genai

app = Flask(__name__)

# Configure Gemini API
genai.configure(api_key=os.getenv('GEMINI_API_KEY'))
model = genai.GenerativeModel("gemini-1.5-flash")

WEIGHTS = {
    'experience': 20,
    'degree': 20,
    'research': 20,
    'publication': 20,
    'skills': 20
}

# Ideal student-to-faculty ratio
IDEAL_RATIO = 20


def calculate_grades(row, weights):
    """Function to calculate grades."""
    experience_grade = normalize(row['Years_of_Experience'], 0, 35) * weights['experience']
    degree_grade = (1 if row['Degree_Held'] in ['PhD', 'MPhil'] else 0.75) * weights['degree']
    research_grade = normalize(row['Research_Count'], 0, 20) * weights['research']
    publication_grade = normalize(row['Publications_Count'], 0, 50) * weights['publication']
    skills_grade = normalize(len(str(row['Skills']).split(',')), 0, 10) * weights['skills']

    total_grade = (experience_grade + degree_grade + research_grade + publication_grade + skills_grade) / sum(weights.values())

    if row['Publications_Count'] > 30:
        total_grade += 0.05
    if row['Years_of_Experience'] < 2:
        total_grade -= 0.05

    return min(1.0, max(0.0, total_grade))


def normalize(value, min_value, max_value):
    """Function to normalize values."""
    return (value - min_value) / (max_value - min_value) if max_value - min_value != 0 else 0

def generate_with_retry(query, retries=3, delay=2):
    for attempt in range(retries):
        try:
            gemini_response = model.generate_content(query)
            return gemini_response.text
        except Exception as e:
            if "429" in str(e) and attempt < retries - 1:
                time.sleep(delay * (2 ** attempt))  # Correctly use time.sleep
            else:
                raise e


def perform_swot_analysis(faculty_df, teaching_responses):
    """Enhanced SWOT analysis based on faculty data and teaching responses."""
    
    strengths = []
    weaknesses = []
    opportunities = []
    threats = []
    
    # Analyze faculty data
    avg_experience = faculty_df['Years_of_Experience'].mean()
    avg_publications = faculty_df['Publications_Count'].mean()
    avg_research = faculty_df['Research_Count'].mean()
    phd_count = len(faculty_df[faculty_df['Degree_Held'] == 'PhD'])
    phd_percentage = (phd_count / len(faculty_df)) * 100
    
    # Faculty Qualifications Analysis
    if phd_percentage > 60:
        strengths.append("High percentage of PhD holders ({}%)".format(round(phd_percentage)))
    elif phd_percentage < 30:
        weaknesses.append("Low percentage of PhD holders ({}%)".format(round(phd_percentage)))
        opportunities.append("Encourage faculty to pursue higher education")

    if avg_experience > 10:
        strengths.append("Strong experienced faculty base (avg. {} years)".format(round(avg_experience)))
    elif avg_experience < 5:
        weaknesses.append("Relatively inexperienced faculty (avg. {} years)".format(round(avg_experience)))
        opportunities.append("Implement mentorship programs")
        
    # Research Output Analysis
    if avg_publications > 20:
        strengths.append("High research output through publications")
    elif avg_publications < 10:
        weaknesses.append("Low publication count")
        opportunities.append("Create research incentives")
        
    # Teaching Methodology Analysis
    if teaching_responses:
        # Course Design Analysis
        course_revision = teaching_responses.get('course_revision', '')
        if course_revision == 'Annually':
            strengths.append("Regular curriculum updates")
        elif course_revision == 'Rarely':
            weaknesses.append("Infrequent curriculum revision")
            threats.append("Risk of outdated curriculum")

        # Technology Integration
        tech_usage = teaching_responses.get('tech_usage', '')
        if tech_usage == 'Yes':
            strengths.append("Strong technology integration in teaching")
        else:
            weaknesses.append("Limited use of technology in teaching")
            opportunities.append("Implement modern teaching technologies")

        # Assessment Methods
        assessment_methods = teaching_responses.get('assessment_methods', [])
        if len(assessment_methods) >= 3:
            strengths.append("Diverse assessment methods")
        elif len(assessment_methods) < 2:
            weaknesses.append("Limited assessment variety")
            opportunities.append("Diversify assessment methods")

        # Practical Learning
        practical_percentage = int(teaching_responses.get('practical_percentage', 0))
        if practical_percentage > 50:
            strengths.append("Strong practical learning focus")
        elif practical_percentage < 30:
            weaknesses.append("Limited practical exposure")
            opportunities.append("Increase hands-on learning activities")

        # Student Engagement
        student_participation = int(teaching_responses.get('student_participation', 0))
        if student_participation > 75:
            strengths.append("High student engagement")
        elif student_participation < 50:
            weaknesses.append("Low student participation")
            opportunities.append("Implement engagement strategies")

        # Teaching Methods
        teaching_methods = teaching_responses.get('teaching_methods', [])
        if len(teaching_methods) >= 3:
            strengths.append("Diverse teaching methodologies")
        elif len(teaching_methods) < 2:
            weaknesses.append("Limited teaching methods")
            opportunities.append("Expand teaching methodology")

        # Professional Development
        prof_dev = int(teaching_responses.get('professional_development', 0))
        if prof_dev > 3:
            strengths.append("Strong commitment to professional development")
        elif prof_dev < 2:
            weaknesses.append("Limited professional development")
            opportunities.append("Increase faculty development programs")

        # Industry Relevance
        curriculum_relevance = int(teaching_responses.get('curriculum_relevance', 0))
        if curriculum_relevance >= 8:
            strengths.append("High industry relevance")
        elif curriculum_relevance <= 5:
            weaknesses.append("Low industry alignment")
            threats.append("Risk of skill-industry mismatch")

    # Add general threats
    threats.extend([
        "Rapid technological changes in education",
        "Increasing competition from online education",
        "Changing student learning preferences"
    ])

    # Add general opportunities
    opportunities.extend([
        "Integration of emerging technologies",
        "Industry collaboration potential",
        "International academic partnerships"
    ])
            
    return {
        'strengths': strengths,
        'weaknesses': weaknesses,
        'opportunities': opportunities,
        'threats': threats
    }

@app.route('/', methods=['GET', 'POST'])
def index():
    plots = {}
    graded_csv = None
    department_tables = {}
    deficiency_table = None
    departments = []
    swot_results=None
    teaching_responses=None
    gemini_insights = {}

    if request.method == 'POST':
        if 'faculty_file' not in request.files:
            return render_template('index.html', error="Error: Faculty file must be uploaded.")

        faculty_file = request.files['faculty_file']

        if faculty_file.filename == '':
            return render_template('index.html', error="Error: Faculty file must be selected for upload.")

        try:
            # Load faculty data
            faculty_df = pd.read_csv(faculty_file)

            # Validate columns in faculty data
            required_faculty_columns = {'Name', 'Department', 'Post', 'Years_of_Experience', 'Degree_Held',
                                        'Research_Count', 'Publications_Count', 'Skills'}
            missing_faculty_columns = required_faculty_columns - set(faculty_df.columns)
            if missing_faculty_columns:
                return render_template('index.html',
                                       error=f"Error: The faculty CSV is missing the following columns: {', '.join(missing_faculty_columns)}")

            # Calculate grades
            faculty_df['Grade'] = faculty_df.apply(lambda row: calculate_grades(row, WEIGHTS), axis=1)

            # Get student counts from the form
            student_counts = {department: int(request.form.get(f'students_{department}', 0) or 0) for department in faculty_df['Department'].unique()}


            # Separate tables for each department
            for department in faculty_df['Department'].unique():
                department_data = faculty_df[faculty_df['Department'] == department]
                department_tables[department] = {
                    'columns': department_data.columns.tolist(),
                    'rows': department_data.values.tolist()
                }
                departments.append(department)

            graph_data = [
                {
                    "title": "Count of Faculty by Department",
                    "data": faculty_df['Department'].value_counts().reset_index(name='count'),
                    "graph": lambda df: px.bar(df, x='Department', y='count', title="Count of Faculty by Department",
                                               labels={'Department': 'Department', 'count': 'Count'}),
                    "query": "Provide insights into the distribution of faculty across departments based on this data."
                },
                {
                    "title": "Students vs Faculty by Department",
                    "data": pd.DataFrame({
                        "Department": faculty_df['Department'].unique(),
                        "Number_of_Students": [int(request.form.get(f'students_{dep}', 0)) for dep in
                                               faculty_df['Department'].unique()],
                        "Number_of_Faculty": faculty_df['Department'].value_counts().values
                    }),
                    "graph": lambda df: px.bar(df, x='Department', y=['Number_of_Students', 'Number_of_Faculty'],
                                               barmode='group',
                                               title="Students vs Faculty by Department"),
                    "query": "Analyze the relationship between the number of students and faculty by department based on this data."
                },
                {
                    "title": "Post vs Skills",
                    "data": faculty_df[['Post', 'Skills']].assign(
                        Skills_Count=lambda x: x['Skills'].apply(lambda y: len(str(y).split(',')))),
                    "graph": lambda df: px.scatter(df, x='Post', y='Skills_Count', title="Post vs Skills"),
                    "query": "Explain the relationship between Post and Skills based on this data."
                },
                {
                    "title": "Degree vs Publications",
                    "data": faculty_df[['Degree_Held', 'Publications_Count']],
                    "graph": lambda df: px.box(df, x='Degree_Held', y='Publications_Count',
                                               title="Degree vs Publications", color='Degree_Held'),
                    "query": "Describe the distribution of publications by degree based on this data."
                },
                {
                    "title": "Department-wise Faculty Count by Degree",
                    "data": faculty_df.groupby(['Department', 'Degree_Held']).size().reset_index(name='Count'),
                    "graph": lambda df: px.bar(df, x='Department', y='Count', color='Degree_Held', barmode='group',
                                               title="Department-wise Faculty Count by Degree"),
                    "query": "What can we infer about the qualifications of faculty across departments from this data?"
                },
                {
                    "title": "Experience Distribution by Degree",
                    "data": faculty_df[['Degree_Held', 'Years_of_Experience']],
                    "graph": lambda df: px.violin(df, x='Degree_Held', y='Years_of_Experience',
                                                  title="Experience Distribution by Degree",
                                                  color='Degree_Held'),
                    "query": "Analyze the distribution of experience across different degree levels using this data."
                },
                {
                    "title": "Research Count by Department",
                    "data": faculty_df.groupby('Department')['Research_Count'].sum().reset_index(),
                    "graph": lambda df: px.bar(df, x='Department', y='Research_Count',
                                               title="Research Count by Department"),
                    "query": "What insights can be drawn about the research output of each department based on this data?"
                },
                {
                    "title": "Publications Count by Department",
                    "data": faculty_df.groupby('Department')['Publications_Count'].sum().reset_index(),
                    "graph": lambda df: px.bar(df, x='Department', y='Publications_Count',
                                               title="Publications Count by Department"),
                    "query": "Describe the publication trends across departments using this data."
                },
                {
                    "title": "Skills Count by Department",
                    "data": faculty_df.groupby('Department').apply(
                        lambda x: x['Skills'].apply(lambda y: len(str(y).split(','))).sum()
                    ).reset_index(name='Skills_Count'),
                    "graph": lambda df: px.bar(df, x='Department', y='Skills_Count',
                                               title="Skills Count by Department"),
                    "query": "Explain the distribution of skills among faculty across different departments based on this data."
                },
                {
                    "title": "Grades Distribution",
                    "data": faculty_df[['Department', 'Grade']],
                    "graph": lambda df: px.box(df, x='Department', y='Grade', title="Grades Distribution by Department",
                                               color='Department'),
                    "query": "What insights can we infer from the grades distribution of faculty across departments?"
                },
                {
                    "title": "Experience vs Publications",
                    "data": faculty_df[['Years_of_Experience', 'Publications_Count']],
                    "graph": lambda df: px.scatter(df, x='Years_of_Experience', y='Publications_Count',
                                                   title="Experience vs Publications",
                                                   labels={'Years_of_Experience': 'Years of Experience',
                                                           'Publications_Count': 'Publications Count'}),
                    "query": "Analyze the relationship between years of experience and the number of publications based on this data."
                },
                {
                    "title": "Top Departments by Research",
                    "data": faculty_df.groupby('Department')['Research_Count'].sum().reset_index().sort_values(
                        by='Research_Count', ascending=False).head(5),
                    "graph": lambda df: px.bar(df, x='Department', y='Research_Count',
                                               title="Top 5 Departments by Research Output"),
                    "query": "Identify the top departments by research output and analyze their characteristics."
                }
            ]

            for graph in graph_data:
                # Generate the graph
                graph_df = graph["data"]
                fig = graph["graph"](graph_df)
                plot_html = fig.to_html(full_html=False)
                plots[graph["title"]] = plot_html

                # Prepare the query for Gemini
                query = (
                    f"{graph['query']}\n\n"
                    "Data:\n"
                    f"{graph_df.to_csv(index=False)}\n\n"
                    "Provide a concise summary in 100 words, formatted without special characters like '*'. "
                    "Use proper sentences and highlight key points using **bold text**."
                )

                # Use retry logic for Gemini API
                try:
                    gemini_response = generate_with_retry(query)
                    raw_text = gemini_response.replace('*', '').strip()

                    # Truncate if necessary
                    if len(raw_text) > 150:
                        raw_text = raw_text[:147].rsplit(' ', 1)[0] + "..."
                    gemini_insights[graph["title"]] = raw_text

                except Exception as e:
                    gemini_insights[graph["title"]] = f"Error generating insight: {str(e)}"

            # Move all faculty_counts calculations inside the try block
            faculty_counts = faculty_df['Department'].value_counts().reset_index()
            faculty_counts.columns = ['Department', 'Number_of_Faculty']

            # Map number of students
            faculty_counts['Number_of_Students'] = faculty_counts['Department'].map(student_counts)

            # Calculate ideal numbers based on S (students), R=9 (1+2+6)
            faculty_counts['Total_Ideal_Faculty'] = (faculty_counts['Number_of_Students'] / IDEAL_RATIO).apply(
                lambda x: int(x) if x.is_integer() else int(x) + 1
            )

            # Role-specific ideal faculty counts
            faculty_counts['Ideal_Principal'] = 1  # Always 1
            faculty_counts['Ideal_Professor'] = (faculty_counts['Number_of_Students'] * 1 / (20 * 9)).apply(
                lambda x: int(x) if x.is_integer() else int(x) + 1
            )
            faculty_counts['Ideal_Associate_Professor'] = (faculty_counts['Number_of_Students'] * 2 / (20 * 9)).apply(
                lambda x: int(x) if x.is_integer() else int(x) + 1
            )
            faculty_counts['Ideal_Assistant_Professor'] = (faculty_counts['Number_of_Students'] * 6 / (20 * 9)).apply(
                lambda x: int(x) if x.is_integer() else int(x) + 1
            )

            # Calculate deficiencies for each role
            faculty_counts['Deficiency_Principal'] = faculty_counts['Ideal_Principal'] - faculty_df[
                faculty_df['Post'] == 'Principal'].groupby('Department')['Post'].count().reindex(faculty_counts['Department']).fillna(0).astype(int)
            faculty_counts['Deficiency_Professor'] = faculty_counts['Ideal_Professor'] - faculty_df[
                faculty_df['Post'] == 'Professor'].groupby('Department')['Post'].count().reindex(faculty_counts['Department']).fillna(0).astype(int)
            faculty_counts['Deficiency_Associate_Professor'] = faculty_counts['Ideal_Associate_Professor'] - faculty_df[
                faculty_df['Post'] == 'Associate Professor'].groupby('Department')['Post'].count().reindex(faculty_counts['Department']).fillna(0).astype(int)
            faculty_counts['Deficiency_Assistant_Professor'] = faculty_counts['Ideal_Assistant_Professor'] - faculty_df[
                faculty_df['Post'] == 'Assistant Professor'].groupby('Department')['Post'].count().reindex(faculty_counts['Department']).fillna(0).astype(int)

            # Overall deficiency
            faculty_counts['Meets_Ratio'] = (faculty_counts['Deficiency_Principal'] <= 0) & \
                                            (faculty_counts['Deficiency_Professor'] <= 0) & \
                                            (faculty_counts['Deficiency_Associate_Professor'] <= 0) & \
                                            (faculty_counts['Deficiency_Assistant_Professor'] <= 0)

            faculty_counts['Meets_Ratio'] = faculty_counts['Meets_Ratio'].apply(lambda x: "✔️" if x else "❌")

            # Prepare the final deficiency table
            deficiency_table = faculty_counts[[
                'Department', 'Number_of_Students', 'Number_of_Faculty',
                'Ideal_Principal', 'Ideal_Professor', 'Ideal_Associate_Professor', 'Ideal_Assistant_Professor',
                'Deficiency_Principal', 'Deficiency_Professor', 'Deficiency_Associate_Professor', 'Deficiency_Assistant_Professor',
                'Meets_Ratio']].to_html(classes="table table-bordered table-hover", index=False, escape=False)

            # Encode graded CSV
            csv_output = io.BytesIO()
            faculty_df.to_csv(csv_output, index=False)
            csv_output.seek(0)
            graded_csv = base64.b64encode(csv_output.getvalue()).decode()

            # Collect teaching evaluation responses
            teaching_responses = {
                'course_revision': request.form.get('course_revision', ''),
                'case_studies': request.form.get('case_studies', ''),
                'assessment_methods': request.form.getlist('assessment_methods') or [],
                'practical_percentage': int(request.form.get('practical_percentage', 0) or 0),
                'curriculum_relevance': int(request.form.get('curriculum_relevance', 0) or 0),
                'interactive_sessions': request.form.get('interactive_sessions', ''),
                'student_participation': int(request.form.get('student_participation', 0) or 0),
                'personalized_feedback': request.form.get('personalized_feedback', ''),
                'student_interest': request.form.get('student_interest', ''),
                'tech_usage': request.form.get('tech_usage', ''),
                'teaching_methods': request.form.getlist('teaching_methods') or [],
                'critical_thinking': request.form.get('critical_thinking', ''),
                'student_feedback': request.form.get('student_feedback', ''),
                'feedback_actions': request.form.get('feedback_actions', ''),
                'professional_development': int(request.form.get('professional_development', 0) or 0)
            }

            # Perform SWOT analysis
            swot_results = perform_swot_analysis(faculty_df, teaching_responses)

        except Exception as e:
            return render_template('index.html', error=f"An unexpected error occurred: {str(e)}")

    return render_template('index.html', 
                        plots=plots, 
                        graded_csv=graded_csv, 
                        department_tables=department_tables,
                        departments=departments, 
                        deficiency_table=deficiency_table, 
                        gemini_insights=gemini_insights,
                        swot_results=swot_results,
                        teaching_responses=teaching_responses)


if __name__ == '__main__':
    app.run(debug=True, port=5632)