studfaceval / app5_selectbox /evaluation.py
MENG21's picture
aec2e12
import streamlit as st
import pandas as pd
import plotly.graph_objs as go
# import time
import plotly.express as px
# import ast
import numpy as np
from app5_selectbox.database_con import cursor, db_connection
from app5_selectbox.app5_selectbox_func import generate_unique_4
from app5_selectbox.evaluation_analysis import eval_analysis
# from app5_selectbox.evaluation_analysis_g4f import eval_analysis
# from app5_selectbox.langchain_llama_gpu import llm_chain
from app5_selectbox.g4f_prompt import g4f_prompt
# Function to fetch evaluation data
def fetch_evaluation_data():
cursor.execute("SELECT * FROM evaluation")
evaluation_data = cursor.fetchall()
if not evaluation_data:
st.warning("No evaluation data found.")
return None
column_names = [i[0] for i in cursor.description]
return pd.DataFrame(evaluation_data, columns=column_names)
# Function to analyze instructors
def analyze_instructors(evaluation_df):
if evaluation_df is None:
return
column_names = evaluation_df.columns[4:14]
criteria_labels = [column.replace("_", " ") for column in column_names]
cursor.execute("SELECT * FROM instructor")
instructor_data = cursor.fetchall()
# st.write(instructor_data)
instructor_df = pd.DataFrame(instructor_data, columns=["inst_id", "instructor name","program code","user name","password"])
instructor_avg_scores = evaluation_df.groupby("inst_id")[column_names].mean().reset_index()
instructor_avg_scores = instructor_avg_scores.merge(instructor_df, on="inst_id", how="left")
# st.write(instructor_avg_scores)
# programs_list = sorted(instructor_avg_scores["program code"].unique())
# Fetch program options from the program table
cursor.execute("SELECT prog_id, prog_code, prog_name FROM program")
selected_program = pd.DataFrame(cursor.fetchall(), columns=["prog_id", "prog_code", "prog_name"])
# st.write(selected_program)
# st.write(list({str(prog): prog[0] for prog in program_options}))
selected_program_select = st.selectbox("Select Program", selected_program["prog_code"])
# selected_program = ast.literal_eval(str(selected_program))
# selected_program = st.selectbox("Select Program", programs_list)
filtered_instructor_list = pd.DataFrame(instructor_avg_scores)
# st.write(filtered_instructor_list)
mask = filtered_instructor_list["program code"] == selected_program.loc[selected_program['prog_code'] == selected_program_select, 'prog_id'].values[0]
# st.write(mask)
filtered_instructor_list = filtered_instructor_list.loc[mask]
# st.write(filtered_instructor_list)
instructors_list = sorted(filtered_instructor_list["instructor name"].unique())
# print(type(instructor_avg_scores))
# instructors_list = instructor_avg_scores.query("program code == {selected_program}")
# st.write(len(instructors_list)) # df to graph
selected_instructor = st.selectbox("Select Instructor", instructors_list)
filtered_data = evaluation_df[evaluation_df["inst_id"] == instructor_avg_scores[instructor_avg_scores["instructor name"] == selected_instructor]["inst_id"].values[0]]
selected_instructor_comments = list(filtered_data["comments"])
st.write(filtered_data)
st.subheader(f"Evaluated by: {len(selected_instructor_comments)} students")
cursor.execute("""
SELECT subj_inst.subj_inst_id, subject.sub_name
FROM subj_inst
INNER JOIN subject
ON subj_inst.sub_id_code = subject.sub_id_code
""")
# Assuming you have a DataFrame named 'filtered_data'
# and column_names is a list of column names you want to consider for calculating average scores
# Convert all columns to numeric data
filtered_data[column_names] = filtered_data[column_names].apply(pd.to_numeric, errors='coerce')
# Fetch subject data from the cursor
subject_data = cursor.fetchall()
# Create a DataFrame for subject data
subject_df = pd.DataFrame(subject_data, columns=["subj_inst_id", "sub name"])
# Merge subject data with filtered data based on 'subj_inst_id'
filtered_data = filtered_data.merge(subject_df, on="subj_inst_id", how="left")
# Group by subject name and calculate average scores
subject_avg_scores = filtered_data.groupby("sub name")[column_names].mean().reset_index()
# Calculate total average and add it as a new column
subject_avg_scores["total average"] = subject_avg_scores[column_names].mean(axis=1)
subject_avg_scores = filtered_data.groupby("sub name")[column_names].mean().reset_index()
subject_avg_scores["total average"] = subject_avg_scores[column_names].mean(axis=1)
fig = go.Figure()
for criterion, label in zip(column_names, criteria_labels):
fig.add_trace(go.Bar(
x=subject_avg_scores["sub name"],
y=subject_avg_scores[criterion],
name=label,
))
# Add the total average score above the bars
total_average = subject_avg_scores["total average"].mean()
fig.add_trace(go.Scatter(
x=subject_avg_scores["sub name"],
y=subject_avg_scores["total average"],
mode="markers+text",
text=round(subject_avg_scores["total average"], 2),
textposition="top center",
textfont=dict(size=14),
marker=dict(size=10, color="black"),
name="Total Average",
))
fig.update_layout(
width=1000,height=600,
barmode="group",
title=f"Average Scores per Criteria by Subject for Instructor: {selected_instructor}",
xaxis_title=f"Overall Average Score (All Subjects): {total_average:.2f}",
yaxis_title="Average Score",
)
st.plotly_chart(fig)
results_to_prompt = "Average score per Criteria\n"
criteria_averages = [(criteria.replace("_", " "), round(filtered_data[criteria].mean(), 2)) for criteria in column_names]
for criteria, average_score in criteria_averages:
results_to_prompt += f"{criteria}: {average_score}/5, \n"
fig = go.Figure()
fig.add_trace(go.Bar(
x=[criteria for criteria, _ in criteria_averages],
y=[average_score for _, average_score in criteria_averages],
text=[f"{average_score}/5" for _, average_score in criteria_averages],
))
fig.update_layout(
width=1000,
title="Average Score per Criteria",
xaxis_title="Criteria",
yaxis_title="Average Score",
)
st.plotly_chart(fig)
results_to_prompt = f"""
Based from these over-all average score please Analyze it and provide short insights: {str(results_to_prompt)}.
Make it in sentence type and in English language only.
"""
while True:
try:
with st.spinner("Analyzing... "):
# st.write(llm_chain.run(prompt))
# st.write(g4f_prompt(results_to_prompt)) #############################
st.success("Analyzing Complete!")
break
except Exception as e:
# Handle the error (e.g., log it or take appropriate action)
# Sleep for a moment before retrying
# st.write("Error occurred.. Retrying")
pass
# time.sleep(0.4)
# Add pie graph of evaluation distribution per student's section
# Fetch program options from the program table
cursor.execute(f"""
SELECT
pr.prog_code || '-' || c.class_year || '-' || c.class_section AS merged_result,
COUNT(*) AS occurrence_count
FROM
student s
JOIN
class c ON s.class_id = c.class_id
JOIN
program pr ON c.prog_id = pr.prog_id
WHERE
s.stud_id IN {tuple(list(filtered_data["stud_id"]))}
GROUP BY
s.class_id, pr.prog_code, c.class_year, c.class_section;
""")
merged_result = pd.DataFrame(cursor.fetchall(), columns=["merged_result", "occurrence_count"])
st.write(filtered_data)
st.write(merged_result)
# section_counts = filtered_data["stud_id"].value_counts()
# st.write(section_counts)
fig = px.pie(
merged_result,
values="occurrence_count",
names="merged_result",
title="Evaluation Distribution per Student's Section",
)
# Add percentage and occurrence_count to the hover information
fig.update_traces(
hovertemplate="%{label}: %{percent} <br>Occurrence Count: %{value}",
textinfo="percent+value",
)
fig.update_layout(
width=600,
height=600,
font=dict(size=20),
)
st.plotly_chart(fig)
cursor.execute(f"""
SELECT
s.class_id,
pr.prog_code || '-' || c.class_year || '-' || c.class_section AS class_info,
COUNT(DISTINCT s.stud_id) AS num_respondents,
ROUND((AVG(Teaching_Effectiveness) + AVG(Course_Organization) + AVG(Accessibility_and_Communication) +
AVG(Assessment_and_Grading) + AVG(Respect_and_Inclusivity) + AVG(Engagement_and_Interactivity) +
AVG(Feedback_and_Improvement) + AVG(Accessibility_of_Learning_Resources) +
AVG(Passion_and_Enthusiasm) + AVG(Professionalism_and_Ethical_Conduct)) / 10, 2) AS avg_overall,
ROUND((COUNT(DISTINCT s.stud_id) * (AVG(Teaching_Effectiveness) + AVG(Course_Organization) + AVG(Accessibility_and_Communication) +
AVG(Assessment_and_Grading) + AVG(Respect_and_Inclusivity) + AVG(Engagement_and_Interactivity) +
AVG(Feedback_and_Improvement) + AVG(Accessibility_of_Learning_Resources) +
AVG(Passion_and_Enthusiasm) + AVG(Professionalism_and_Ethical_Conduct)) / 10), 2) AS weighted_avg_overall
FROM
evaluation e
JOIN
student s ON e.stud_id = s.stud_id
JOIN
class c ON s.class_id = c.class_id
JOIN
program pr ON c.prog_id = pr.prog_id
WHERE
s.stud_id IN {tuple(list(filtered_data["stud_id"]))}
GROUP BY
s.class_id, pr.prog_code, c.class_year, c.class_section, class_info;
""")
avg_scores_per_class = pd.DataFrame(cursor.fetchall(), columns=[
"class_id",
"class_info",
"num_respondents",
"avg_overall",
"weighted_avg_overall"
])
# Calculate the last row's weighted_avg_overall / num_respondents
last_row_index = avg_scores_per_class["weighted_avg_overall"].last_valid_index()
if last_row_index is not None:
avg_scores_per_class.at[last_row_index, "weighted_avg_overall"] /= avg_scores_per_class.at[last_row_index, "num_respondents"]
# Convert the column to decimal.Decimal before rounding
avg_scores_per_class["weighted_avg_overall"] = avg_scores_per_class["num_respondents"] * avg_scores_per_class["avg_overall"] # avg_scores_per_class["weighted_avg_overall"].apply(lambda x: round(float(x), 2))
# Drop rows with None values
avg_scores_per_class = avg_scores_per_class.dropna()
# Calculate the overall averages for avg_overall and weighted_avg_overall
num_respondents = round(avg_scores_per_class["num_respondents"].sum(), 2)
overall_avg_overall = round(avg_scores_per_class["avg_overall"].mean(), 2)
overall_weighted_avg_overall = round(avg_scores_per_class["weighted_avg_overall"].sum(),2)
weighted_avg_overall = round(overall_weighted_avg_overall / num_respondents,2)
# # Append an additional row for avg_overall and weighted_avg_overall
# avg_scores_per_class = avg_scores_per_class.append({
# "class_id": int(avg_scores_per_class["class_id"].max()) + 1,
# "class_info": "Total",
# "num_respondents": avg_scores_per_class["num_respondents"].sum(),
# "avg_overall": round(overall_avg_overall, 2),
# "weighted_avg_overall": round(overall_weighted_avg_overall / avg_scores_per_class["num_respondents"].sum(), 2)
# }, ignore_index=True)
# st.write(avg_scores_per_class.style.set_properties(**{'text-align': 'center'}))
# Add summary rows to the DataFrame
avg_scores_per_class = avg_scores_per_class.append({
"class_id": "",
"class_info": "Summary",
"num_respondents": num_respondents,
"avg_overall": " ",
"weighted_avg_overall": overall_weighted_avg_overall
}, ignore_index=True)
def calculate_satisfaction(weighted_avg_overall):
if weighted_avg_overall > 4:
return "Outstanding"
elif weighted_avg_overall > 3:
return "Above Average"
elif weighted_avg_overall > 2:
return "Average"
elif weighted_avg_overall > 1:
return "Below Average"
else:
return "Unsatisfactory"
def highlight_cell(col, col_label, row_label):
# check if col is a column we want to highlight
if col.name == col_label:
# a boolean mask where True represents a row we want to highlight
mask = (col.index == row_label)
# return an array of string styles (e.g. ["", "background-color: yellow"])
return ["background-color: lightgreen" if val_bool else "" for val_bool in mask]
else:
# return an array of empty strings that has the same size as col (e.g. ["",""])
return np.full_like(col, "", dtype="str")
avg_scores_per_class = avg_scores_per_class.append({
"class_id": "",
"class_info": "Weighted Avg.",
"num_respondents": " ", # You can set this to "N/A" or any appropriate value
"avg_overall": calculate_satisfaction(weighted_avg_overall), # You can set this to "N/A" or any appropriate value
"weighted_avg_overall": weighted_avg_overall
}, ignore_index=True)
# st.dataframe(avg_scores_per_class.style.background_gradient(subset=["C"], cmap="RdYlGn", vmin=0, vmax=2.5))
avg_scores_per_class =avg_scores_per_class.style.apply(highlight_cell, col_label="avg_overall", row_label=9)
st.write(avg_scores_per_class)
st.write(f"Number of respondents: {num_respondents}")
st.write(f"Overall weighted avg.: {overall_weighted_avg_overall}")
st.write(f"Weighted avg overall: {weighted_avg_overall}")
# if st.button("Analyze the results", key="analyze_results"):
for subject in subject_avg_scores["sub name"]:
with st.expander(subject):
subject_filtered_data = filtered_data[filtered_data["sub name"] == subject]
promt_txt = ""
fig = go.Figure()
# st.write(subject_filtered_data) # displays DF for every graphs
for criterion, label in zip(column_names, criteria_labels):
text = round(subject_filtered_data[criterion].mean(),2)
fig.add_trace(go.Bar(
x=[label],
y=[text],
text=text,
name=label,
))
promt_txt += criterion.replace("_", " ") + ": " + str(text)+ "\n"
# st.text(promt_txt) # prompt per graph
total_average = subject_filtered_data[column_names].mean(axis=1).mean()
total_average_txt = f"{subject} Average Score: {round(total_average,2)}/5"
fig.update_layout(
barmode="group",
width=1000,
title=total_average_txt,
yaxis_title="Average Score",
)
st.plotly_chart(fig)
prompt = f"generate a very short insights about this faculty evaluation result for the subject {subject}?\n{promt_txt}\nplease strictly shorten your response in sentence format"
# st.text(prompt)
while True:
with st.spinner("Generating Recommendation"):
try:
# st.write(g4f_prompt(prompt)) #############################
# pass
# break
break
except Exception as e:
# Handle the error (e.g., log it or take appropriate action)
# Sleep for a moment before retrying
# st.write("Error occurred.. Retrying")
pass
# time.sleep(0.4)
return selected_instructor, selected_instructor_comments, results_to_prompt
def evaluation():
try:
evaluation_df = fetch_evaluation_data()
if evaluation_df is not None:
analyze_instructors_results = analyze_instructors(evaluation_df)
# if st.button("Analyze comments"):
# eval_analysis(analyze_instructors_results[0], analyze_instructors_results[1], analyze_instructors_results[2])
with st.expander("Sentiment Analysis"):
eval_analysis(analyze_instructors_results[0], analyze_instructors_results[1], analyze_instructors_results[2]) #############################
# pass
except Exception as e:
st.error(f"An error occurred: {str(e)}")