File size: 7,451 Bytes
a321c55 0f0c69b ebebf5a a321c55 01df3ce 0f0c69b e9f9da3 0f0c69b e9f9da3 0f0c69b a321c55 0f0c69b e9f9da3 0f0c69b e9f9da3 0f0c69b a926557 0f0c69b e9f9da3 0f0c69b a926557 0f0c69b 01df3ce ca30333 0f0c69b ebebf5a 0f0c69b ebebf5a a926557 ca30333 a926557 01df3ce a926557 01df3ce 1b25018 ebebf5a e9f9da3 0f0c69b a321c55 01df3ce a321c55 0f0c69b 1b25018 0f0c69b a321c55 0f0c69b | 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 | import pandas as pd
import gradio as gr
import io
import os
import tempfile
# Define which grades count as completed
COMPLETED_GRADES = {"GM", "RI", "RU", "RG", "RB", "RU+", "R1", "R2", "R3", "S"}
PROGRESS_GRADE = "MP"
def process_files(course_files, crn_file, session_name):
# Validate files exist
if not course_files or not crn_file:
return None, "Please upload both course roster files and a CRN mapping file."
# Validate file extensions
for file in course_files + [crn_file]:
_, ext = os.path.splitext(file.name)
if ext.lower() not in ['.xlsx', '.xls']:
return None, f"File {file.name} is not an Excel file. Please upload only Excel files."
try:
# Read CRN mapping (Course Name in A, CRN in E)
crn_map_df = pd.read_excel(crn_file.name, header=None)
# Convert CRNs to strings for consistent lookup
crn_map = {}
for idx, row in crn_map_df.iterrows():
if not pd.isna(row[4]) and not pd.isna(row[0]): # Ensure both CRN and course name exist
crn_map[str(row[4])] = str(row[0])
students = {}
for course_file in course_files:
try:
df = pd.read_excel(course_file.name, header=None)
# Get CRN from the appropriate cell and convert to string
crn = str(df.iloc[0, 2]) if not pd.isna(df.iloc[0, 2]) else "Unknown"
course_name = crn_map.get(crn, f"Unknown Course (CRN: {crn})")
row = 1 # Start at C2/G2
while row < df.shape[0]:
try:
# Read name, grade code, grade
name = df.iloc[row, 2] # C (names)
if pd.isna(name):
row += 1
continue
grade_code = str(df.iloc[row, 3]).strip().upper() if not pd.isna(df.iloc[row, 3]) else "" # D (grade code)
grade = str(df.iloc[row, 6]).strip().upper() if not pd.isna(df.iloc[row, 6]) else "" # G (grade)
# Convert "Last, First Middle" to "First Middle Last"
try:
if isinstance(name, str) and "," in name:
last, first = [x.strip() for x in str(name).split(",", 1)]
full_name = f"{first} {last}"
else:
full_name = str(name).strip()
except Exception:
full_name = str(name).strip()
if full_name not in students:
students[full_name] = {"progress": set(), "completed": set()}
# Parse the course name without CRN
course = course_name # Just the course name without the CRN
if grade == PROGRESS_GRADE:
students[full_name]["progress"].add(course)
elif grade in COMPLETED_GRADES:
students[full_name]["completed"].add(course)
except Exception as e:
print(f"Error processing row {row} in file {course_file.name}: {e}")
row += 1
except Exception as e:
print(f"Error processing file {course_file.name}: {e}")
continue
# Build summary DataFrame
summary = []
for full_name, data in students.items():
# Join with newline character for Excel
progress = "\n".join(sorted(data["progress"]))
completed = "\n".join(sorted(data["completed"]))
both_types = "Y" if data["progress"] and data["completed"] else ""
summary.append({
"Name": full_name,
"Session": session_name, # Add the session name here
"Progress_Courses": progress,
"Completed_Courses": completed,
"Both_Types": both_types
})
summary_df = pd.DataFrame(summary)
# Sort by student name
if not summary_df.empty:
summary_df = summary_df.sort_values("Name")
# Save to a temporary file
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx")
temp_file.close()
with pd.ExcelWriter(temp_file.name, engine='openpyxl') as writer:
summary_df.to_excel(writer, index=False)
# Get the worksheet
worksheet = writer.sheets['Sheet1']
# Auto-adjust row heights to accommodate multiple lines and wrap text
for idx, row in summary_df.iterrows():
# Set row height based on number of newlines in progress and completed columns
progress_lines = row['Progress_Courses'].count('\n') + 1 if isinstance(row['Progress_Courses'], str) else 1
completed_lines = row['Completed_Courses'].count('\n') + 1 if isinstance(row['Completed_Courses'], str) else 1
max_lines = max(progress_lines, completed_lines)
# Set row height (approximately 15 points per line)
worksheet.row_dimensions[idx + 2].height = max(15 * max_lines, 15) # +2 because of header and 1-based indexing
# Enable text wrapping for these columns
for col_idx in [2, 3]: # Columns C and D (0-indexed would be 2 and 3)
for cell in worksheet[chr(65 + col_idx)]:
cell.alignment = cell.alignment.copy(wrapText=True)
# Auto-adjust column widths
for column in worksheet.columns:
max_length = 0
column_letter = column[0].column_letter
for cell in column:
try:
if len(str(cell.value)) > max_length:
max_length = len(str(cell.value))
except:
pass
adjusted_width = (max_length + 2)
worksheet.column_dimensions[column_letter].width = min(adjusted_width, 50) # Cap width at 50
# Return the path to the temporary file
return temp_file.name, "Summary generated successfully!"
except Exception as e:
print(f"Error generating summary: {e}")
return None, f"Error: {str(e)}"
iface = gr.Interface(
fn=process_files,
inputs=[
gr.Files(label="Upload ALL Course Roster Files (.xlsx)", file_count="multiple"),
gr.File(label="Upload CRN-to-Course Mapping File (.xlsx)"),
gr.Textbox(label="Session (e.g., Fall 1, Spring 2)", placeholder="Enter session name")
],
outputs=[
gr.File(label="Download Student Summary Excel", file_types=[".xlsx"]),
gr.Textbox(label="Status/Error Messages")
],
title="Student Course Summary Generator",
description="Upload all course roster Excel files and a CRN-to-course mapping file. Get a summary Excel file per student."
)
if __name__ == "__main__":
iface.launch() |