empower-reports / pages /enter_results.py
mikaelJ46's picture
Create /enter_results.py
3a989b7 verified
raw
history blame
17.9 kB
# pages/enter_results.py
import streamlit as st
import pandas as pd
import json
from datetime import datetime
from modules.database import Session, User, Student, AcademicTerm, ComponentMark, ENGINE, log_audit
from modules.grading import (calculate_compiled_score, convert_to_base, compute_total,
get_grade, update_compiled_marks)
def show():
st.header("✏️ Enter Results")
session = Session()
user = session.query(User).get(st.session_state.user_id)
my_subjects = [s.strip() for s in user.subjects_taught.split(',')] if user.subjects_taught else []
if not my_subjects:
st.warning("You have no subjects assigned. Please contact administrator.")
session.close()
st.stop()
active_term = session.query(AcademicTerm).filter_by(is_active=True).first()
if not active_term:
st.warning("No active term set. Please contact administrator.")
session.close()
st.stop()
st.info(f"πŸ“… Current Term: **{active_term.term_name}** (ID: {active_term.id})")
# Select Class
classes = pd.read_sql("SELECT DISTINCT class_name FROM students ORDER BY class_name", ENGINE)
if classes.empty:
st.info("No students enrolled yet")
session.close()
st.stop()
st.markdown("### Step 1: Select Class")
selected_class = st.selectbox("Class", classes['class_name'].tolist(), label_visibility="collapsed")
# Select what to enter
st.markdown("### Step 2: Select What to Enter")
result_types = ["Coursework (CW)", "Mid of Term (MOT)", "End of Term (EOT)", "All Three"]
selected_type = st.selectbox("Result Type", result_types, label_visibility="collapsed")
# Select Subject
st.markdown("### Step 3: Select Subject")
selected_subject = st.selectbox("Subject", my_subjects, label_visibility="collapsed")
# Get students in this class
students_in_class = pd.read_sql(
f"SELECT id, name, year, subjects FROM students WHERE class_name = '{selected_class}'",
ENGINE
)
def has_subject(subjects_json, subject):
try:
subjects = json.loads(subjects_json)
return subject in subjects
except:
return False
students_in_class['has_subject'] = students_in_class['subjects'].apply(
lambda x: has_subject(x, selected_subject)
)
students_with_subject = students_in_class[students_in_class['has_subject']]
if students_with_subject.empty:
st.warning(f"No students in {selected_class} take {selected_subject}")
session.close()
st.stop()
st.markdown("---")
st.subheader(f"Enter {selected_type} for {selected_class} - {selected_subject}")
# Show existing marks for verification
with st.expander("πŸ“Š View Existing Marks for This Class/Subject"):
existing_marks_query = f"""
SELECT s.name, m.coursework_out_of_20, m.midterm_out_of_20,
m.endterm_out_of_60, m.total, m.grade
FROM marks m
JOIN students s ON m.student_id = s.id
WHERE m.term_id = {active_term.id}
AND m.subject = '{selected_subject}'
AND s.class_name = '{selected_class}'
ORDER BY s.name
"""
existing_marks_df = pd.read_sql(existing_marks_query, ENGINE)
if not existing_marks_df.empty:
st.dataframe(existing_marks_df, use_container_width=True)
else:
st.info("No marks entered yet for this class/subject combination")
# Component marks entry system
st.markdown("---")
st.subheader("πŸ“ Enter Component Marks")
st.info("Enter individual test/paper scores below. The system will automatically compile them into the final scores.")
# Select student
student_name = st.selectbox("Select Student", students_with_subject['name'].tolist())
student_id = int(students_with_subject[students_with_subject['name'] == student_name].iloc[0]['id'])
# Tabs for different component types
tab1, tab2, tab3, tab4 = st.tabs(["Coursework", "Mid-term", "End-term", "Compile Final Marks"])
with tab1:
if selected_type in ["Coursework (CW)", "All Three"]:
st.markdown("### πŸ“ Coursework Components")
# Get existing component marks for coursework
cw_components = pd.read_sql(f"""
SELECT id, component_name, score, total
FROM component_marks
WHERE student_id = {student_id}
AND subject = '{selected_subject}'
AND term_id = {active_term.id}
AND component_type = 'coursework'
ORDER BY component_name
""", ENGINE)
if not cw_components.empty:
st.subheader("Existing Coursework Components")
st.dataframe(cw_components, use_container_width=True)
# Add new component
with st.form("add_cw_component"):
st.markdown("#### Add New Coursework Component")
component_name = st.text_input("Component Name (e.g., Test 1, Assignment 1)")
score = st.number_input("Score", 0.0, 1000.0, step=0.5)
total = st.number_input("Total Marks", 0.1, 1000.0, step=0.5)
if st.form_submit_button("Add Coursework Component"):
if component_name and total > 0:
new_component = ComponentMark(
student_id=student_id,
subject=selected_subject,
term_id=active_term.id,
component_type='coursework',
component_name=component_name,
score=score,
total=total,
submitted_by=st.session_state.user_id
)
session.add(new_component)
session.commit()
log_audit(session, st.session_state.user_id, "add_cw_component",
f"{student_name} - {selected_subject} - {component_name}")
st.success(f"βœ… Coursework component added: {component_name}")
st.rerun()
else:
st.error("Please provide a component name and total marks")
# Delete component
if not cw_components.empty:
st.markdown("#### Delete Coursework Component")
component_to_delete = st.selectbox("Select Component to Delete",
cw_components['id'].tolist(),
format_func=lambda x: cw_components[cw_components['id']==x]['component_name'].iloc[0])
if st.button("Delete Selected Component", key="delete_cw"):
component = session.query(ComponentMark).get(component_to_delete)
if component:
component_name = component.component_name
session.delete(component)
session.commit()
log_audit(session, st.session_state.user_id, "delete_cw_component",
f"{student_name} - {selected_subject} - {component_name}")
st.success(f"βœ… Component deleted: {component_name}")
st.rerun()
with tab2:
if selected_type in ["Mid of Term (MOT)", "All Three"]:
st.markdown("### πŸ“‹ Mid of Term Components")
mt_components = pd.read_sql(f"""
SELECT id, component_name, score, total
FROM component_marks
WHERE student_id = {student_id}
AND subject = '{selected_subject}'
AND term_id = {active_term.id}
AND component_type = 'midterm'
ORDER BY component_name
""", ENGINE)
if not mt_components.empty:
st.subheader("Existing Mid-term Components")
st.dataframe(mt_components, use_container_width=True)
with st.form("add_mt_component"):
st.markdown("#### Add New Mid-term Component")
component_name = st.text_input("Component Name (e.g., Paper 1, Paper 2)")
score = st.number_input("Score", 0.0, 1000.0, step=0.5)
total = st.number_input("Total Marks", 0.1, 1000.0, step=0.5)
if st.form_submit_button("Add Mid-term Component"):
if component_name and total > 0:
new_component = ComponentMark(
student_id=student_id,
subject=selected_subject,
term_id=active_term.id,
component_type='midterm',
component_name=component_name,
score=score,
total=total,
submitted_by=st.session_state.user_id
)
session.add(new_component)
session.commit()
log_audit(session, st.session_state.user_id, "add_mt_component",
f"{student_name} - {selected_subject} - {component_name}")
st.success(f"βœ… Mid-term component added: {component_name}")
st.rerun()
else:
st.error("Please provide a component name and total marks")
if not mt_components.empty:
st.markdown("#### Delete Mid-term Component")
component_to_delete = st.selectbox("Select Component to Delete",
mt_components['id'].tolist(),
format_func=lambda x: mt_components[mt_components['id']==x]['component_name'].iloc[0],
key="mt_delete_select")
if st.button("Delete Selected Component", key="delete_mt"):
component = session.query(ComponentMark).get(component_to_delete)
if component:
component_name = component.component_name
session.delete(component)
session.commit()
log_audit(session, st.session_state.user_id, "delete_mt_component",
f"{student_name} - {selected_subject} - {component_name}")
st.success(f"βœ… Component deleted: {component_name}")
st.rerun()
with tab3:
if selected_type in ["End of Term (EOT)", "All Three"]:
st.markdown("### πŸ“„ End of Term Components")
et_components = pd.read_sql(f"""
SELECT id, component_name, score, total
FROM component_marks
WHERE student_id = {student_id}
AND subject = '{selected_subject}'
AND term_id = {active_term.id}
AND component_type = 'endterm'
ORDER BY component_name
""", ENGINE)
if not et_components.empty:
st.subheader("Existing End-term Components")
st.dataframe(et_components, use_container_width=True)
with st.form("add_et_component"):
st.markdown("#### Add New End-term Component")
component_name = st.text_input("Component Name (e.g., Paper 1, Paper 2)")
score = st.number_input("Score", 0.0, 1000.0, step=0.5)
total = st.number_input("Total Marks", 0.1, 1000.0, step=0.5)
if st.form_submit_button("Add End-term Component"):
if component_name and total > 0:
new_component = ComponentMark(
student_id=student_id,
subject=selected_subject,
term_id=active_term.id,
component_type='endterm',
component_name=component_name,
score=score,
total=total,
submitted_by=st.session_state.user_id
)
session.add(new_component)
session.commit()
log_audit(session, st.session_state.user_id, "add_et_component",
f"{student_name} - {selected_subject} - {component_name}")
st.success(f"βœ… End-term component added: {component_name}")
st.rerun()
else:
st.error("Please provide a component name and total marks")
if not et_components.empty:
st.markdown("#### Delete End-term Component")
component_to_delete = st.selectbox("Select Component to Delete",
et_components['id'].tolist(),
format_func=lambda x: et_components[et_components['id']==x]['component_name'].iloc[0],
key="et_delete_select")
if st.button("Delete Selected Component", key="delete_et"):
component = session.query(ComponentMark).get(component_to_delete)
if component:
component_name = component.component_name
session.delete(component)
session.commit()
log_audit(session, st.session_state.user_id, "delete_et_component",
f"{student_name} - {selected_subject} - {component_name}")
st.success(f"βœ… Component deleted: {component_name}")
st.rerun()
with tab4:
st.markdown("### πŸ”„ Compile and Update Final Marks")
# Get students with components
students_with_components = pd.read_sql(f"""
SELECT DISTINCT s.id, s.name
FROM students s
JOIN component_marks cm ON s.id = cm.student_id
WHERE s.class_name = '{selected_class}'
AND cm.subject = '{selected_subject}'
AND cm.term_id = {active_term.id}
ORDER BY s.name
""", ENGINE)
if students_with_components.empty:
st.info("No students with component marks found. Add component marks first.")
else:
selected_student_compile = st.selectbox("Select Student to Compile",
students_with_components['name'].tolist(),
key="compile_student_select")
student_id_compile = int(students_with_components[students_with_components['name'] == selected_student_compile].iloc[0]['id'])
# Show current compiled scores
cw_score, cw_total = calculate_compiled_score(session, student_id_compile, selected_subject, active_term.id, 'coursework')
mt_score, mt_total = calculate_compiled_score(session, student_id_compile, selected_subject, active_term.id, 'midterm')
et_score, et_total = calculate_compiled_score(session, student_id_compile, selected_subject, active_term.id, 'endterm')
cw_out_of_20 = convert_to_base(cw_score, cw_total, 20)
mt_out_of_20 = convert_to_base(mt_score, mt_total, 20)
et_out_of_60 = convert_to_base(et_score, et_total, 60)
total = compute_total(cw_out_of_20, mt_out_of_20, et_out_of_60)
grade = get_grade(total)
st.subheader(f"Compiled Scores for {selected_student_compile}")
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Coursework (out of 20)", f"{cw_out_of_20:.1f}")
st.write(f"Based on {cw_score:.1f}/{cw_total:.1f}")
with col2:
st.metric("Mid-term (out of 20)", f"{mt_out_of_20:.1f}")
st.write(f"Based on {mt_score:.1f}/{mt_total:.1f}")
with col3:
st.metric("End-term (out of 60)", f"{et_out_of_60:.1f}")
st.write(f"Based on {et_score:.1f}/{et_total:.1f}")
col4, col5 = st.columns(2)
with col4:
st.metric("Total (out of 100)", f"{total:.1f}")
with col5:
st.metric("Grade", grade)
if st.button(f"πŸ’Ύ Update Final Marks for {selected_student_compile}", use_container_width=True):
try:
total, grade = update_compiled_marks(session, student_id_compile, selected_subject, active_term.id)
log_audit(session, st.session_state.user_id, "update_compiled_marks",
f"{selected_student_compile} - {selected_subject} - {active_term.term_name}")
st.success(f"βœ… Final marks updated for {selected_student_compile}!")
st.success(f"πŸ“Š Total: {total:.1f}/100 | Grade: {grade}")
st.rerun()
except Exception as e:
st.error(f"❌ Error updating marks: {str(e)}")
session.close()