0% found this document useful (0 votes)
3 views

Python code for grade 9 analysis tool

The document outlines a Python function to create an Excel workbook for class assessment analysis, which includes three sheets: Mark Schedule, Summary Analysis, and Dashboard. It details the setup of the Mark Schedule with headers, data validation, sample data, and conditional formatting for grades. Additionally, it includes functions for adding summary tables and performance categories in the Summary Analysis sheet.

Uploaded by

Patrick Kamfwa
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

Python code for grade 9 analysis tool

The document outlines a Python function to create an Excel workbook for class assessment analysis, which includes three sheets: Mark Schedule, Summary Analysis, and Dashboard. It details the setup of the Mark Schedule with headers, data validation, sample data, and conditional formatting for grades. Additionally, it includes functions for adding summary tables and performance categories in the Summary Analysis sheet.

Uploaded by

Patrick Kamfwa
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 13

import pandas as pd

import numpy as np
import openpyxl
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.styles.differential import DifferentialStyle
from openpyxl.formatting.rule import Rule, FormulaRule
from openpyxl.chart import BarChart, PieChart, Reference
from openpyxl.chart.label import DataLabelList
from openpyxl.worksheet.datavalidation import DataValidation
from openpyxl.utils import get_column_letter
from openpyxl.drawing.image import Image
import datetime
import os

def create_class_assessment_tool(filename='Class_Assessment_Tool.xlsx',
num_students=30):
"""
Create an Excel workbook for class assessment analysis with three sheets:
1. Mark Schedule
2. Summary Analysis
3. Dashboard

Args:
filename (str): Output Excel filename
num_students (int): Number of sample student rows to include
"""
# Create a new workbook
wb = openpyxl.Workbook()

# Create the three sheets


mark_schedule = wb.active
mark_schedule.title = "Mark Schedule"
summary_analysis = wb.create_sheet(title="Summary Analysis")
dashboard = wb.create_sheet(title="Dashboard")

# Set up each sheet


setup_mark_schedule(mark_schedule, num_students)
setup_summary_analysis(summary_analysis)
setup_dashboard(dashboard)

# Define named ranges for easier formula references


define_named_ranges(wb)

# Adjust column widths and row heights


adjust_column_widths(wb)

# Add cell protection


add_cell_protection(wb)

# Add instructions
add_instructions(wb)

# Set print areas


set_print_areas(wb)

# Save the workbook


wb.save(filename)
print(f"Excel file created: {filename}")
def setup_mark_schedule(ws, num_students):
"""
Set up the Mark Schedule sheet with headers, data validation, and conditional
formatting
"""
# Define headers
headers = ["SN", "Name", "Sex", "Assessment 1", "Assessment 2", "Assessment 3",
"Average", "Grade", "Standard"]

# Create headers with formatting


for col_idx, header in enumerate(headers, 1):
cell = ws.cell(row=1, column=col_idx, value=header)
cell.font = Font(bold=True)
cell.alignment = Alignment(horizontal='center')

# Apply border to header row


border = Border(bottom=Side(style='medium'))
for col_idx in range(1, len(headers) + 1):
ws.cell(row=1, column=col_idx).border = border

# Add sample data


add_sample_data(ws, num_students)

# Set data validation for Sex column (M/F only)


sex_validation = DataValidation(type="list", formula1='"M,F"',
allow_blank=True)
ws.add_data_validation(sex_validation)

# Apply sex validation to all rows


for row in range(2, num_students + 2):
sex_validation.add(f'C{row}')

# Set data validation for Assessment scores (0-100)


score_validation = DataValidation(type="decimal", operator="between",
formula1="0", formula2="100", allow_blank=True)
ws.add_data_validation(score_validation)

# Apply score validation to all assessment columns


for col in ['D', 'E', 'F']:
for row in range(2, num_students + 2):
score_validation.add(f'{col}{row}')

# Add formulas for Average, Grade, and Standard


for row in range(2, num_students + 2):
# Average formula
ws[f'G{row}'] =
f'=IF(OR(D{row}="",E{row}="",F{row}=""),"",AVERAGE(D{row}:F{row}))'

# Grade formula
ws[f'H{row}'] =
f'=IF(G{row}="","",IF(ISNA(G{row}),"X",IF(G{row}>=75,"ONE",IF(G{row}>=60,"TWO",IF(G
{row}>=50,"THREE",IF(G{row}>=40,"FOUR",IF(G{row}>=0,"F","X")))))))'

# Standard formula
ws[f'I{row}'] =
f'=IF(H{row}="","",IF(H{row}="X","ABSENT",IF(H{row}="F","FAIL",IF(H{row}="FOUR","PA
SS",IF(H{row}="THREE","CREDIT",IF(H{row}="TWO","MERIT","DISTINCTION"))))))'

# Set conditional formatting for grades


add_conditional_formatting(ws, num_students)

def add_sample_data(ws, num_students):


"""
Add sample student data to the Mark Schedule sheet
"""
# Sample first names
first_names = ['John', 'Mary', 'Michael', 'Emma', 'James', 'Sarah', 'David',
'Jessica',
'Robert', 'Jennifer', 'William', 'Elizabeth', 'Joseph', 'Linda',
'Richard',
'Susan', 'Thomas', 'Karen', 'Charles', 'Nancy', 'Daniel',
'Lisa', 'Matthew',
'Emily', 'Andrew', 'Amanda', 'Anthony', 'Barbara',
'Christopher', 'Melissa']

# Sample last names


last_names = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia',
'Miller', 'Davis',
'Rodriguez', 'Martinez', 'Wilson', 'Anderson', 'Taylor', 'Thomas',
'Moore',
'Jackson', 'Martin', 'Lee', 'Harris', 'Clark', 'Lewis',
'Robinson', 'Walker',
'Young', 'Allen', 'King', 'Wright', 'Scott', 'Green', 'White']

# Gender distribution (approx. 50-50)


genders = ['M', 'F'] * (num_students // 2)
if num_students % 2 == 1:
genders.append(np.random.choice(['M', 'F']))
np.random.shuffle(genders)

# Generate sample data


for i in range(num_students):
row = i + 2 # Start from row 2

# Serial number
ws.cell(row=row, column=1, value=i+1)

# Name (random combination of first and last name)


first_name = np.random.choice(first_names)
last_name = np.random.choice(last_names)
ws.cell(row=row, column=2, value=f"{first_name} {last_name}")

# Sex (M/F)
ws.cell(row=row, column=3, value=genders[i])

# Generate assessment scores (adjusted to create a realistic distribution)


# Some students will have missing scores (absent)
if np.random.random() < 0.1: # 10% chance of being absent
# Leave assessments blank for absent students
pass
else:
# Normal distribution around 70 with std dev of 15
for j in range(3):
score = np.random.normal(70, 15)
score = max(0, min(100, score)) # Clamp between 0 and 100
ws.cell(row=row, column=j+4, value=round(score, 1))

def add_conditional_formatting(ws, num_students):


"""
Add conditional formatting to the Grade column
"""
# Define fill colors for each grade
gold_fill = PatternFill(start_color="FFD700", end_color="FFD700",
fill_type="solid") # ONE/DISTINCTION
silver_fill = PatternFill(start_color="ADD8E6", end_color="ADD8E6",
fill_type="solid") # TWO/MERIT
bronze_fill = PatternFill(start_color="D2B48C", end_color="D2B48C",
fill_type="solid") # THREE/CREDIT
green_fill = PatternFill(start_color="90EE90", end_color="90EE90",
fill_type="solid") # FOUR/PASS
red_fill = PatternFill(start_color="FF9999", end_color="FF9999",
fill_type="solid") # F/FAIL
gray_fill = PatternFill(start_color="D3D3D3", end_color="D3D3D3",
fill_type="solid") # X/ABSENT

# Apply conditional formatting rules to the Grade column (H)


# ONE (DISTINCTION): Gold/Yellow
ws.conditional_formatting.add(f'H2:H{num_students+1}',
FormulaRule(formula=['$H2="ONE"'],
fill=gold_fill))

# TWO (MERIT): Silver/Light Blue


ws.conditional_formatting.add(f'H2:H{num_students+1}',
FormulaRule(formula=['$H2="TWO"'],
fill=silver_fill))

# THREE (CREDIT): Bronze/Light Brown


ws.conditional_formatting.add(f'H2:H{num_students+1}',
FormulaRule(formula=['$H2="THREE"'],
fill=bronze_fill))

# FOUR (PASS): Green


ws.conditional_formatting.add(f'H2:H{num_students+1}',
FormulaRule(formula=['$H2="FOUR"'],
fill=green_fill))

# F (FAIL): Red
ws.conditional_formatting.add(f'H2:H{num_students+1}',
FormulaRule(formula=['$H2="F"'], fill=red_fill))

# X (ABSENT): Gray
ws.conditional_formatting.add(f'H2:H{num_students+1}',
FormulaRule(formula=['$H2="X"'], fill=gray_fill))

# Apply similar formatting to the Standard column (I)


# DISTINCTION: Gold/Yellow
ws.conditional_formatting.add(f'I2:I{num_students+1}',
FormulaRule(formula=['$I2="DISTINCTION"'],
fill=gold_fill))

# MERIT: Silver/Light Blue


ws.conditional_formatting.add(f'I2:I{num_students+1}',
FormulaRule(formula=['$I2="MERIT"'],
fill=silver_fill))

# CREDIT: Bronze/Light Brown


ws.conditional_formatting.add(f'I2:I{num_students+1}',
FormulaRule(formula=['$I2="CREDIT"'],
fill=bronze_fill))

# PASS: Green
ws.conditional_formatting.add(f'I2:I{num_students+1}',
FormulaRule(formula=['$I2="PASS"'],
fill=green_fill))

# FAIL: Red
ws.conditional_formatting.add(f'I2:I{num_students+1}',
FormulaRule(formula=['$I2="FAIL"'],
fill=red_fill))

# ABSENT: Gray
ws.conditional_formatting.add(f'I2:I{num_students+1}',
FormulaRule(formula=['$I2="ABSENT"'],
fill=gray_fill))

def setup_summary_analysis(ws):
"""
Set up the Summary Analysis sheet with various summary tables
"""
# Add sheet title
ws['A1'] = "Summary Analysis"
ws['A1'].font = Font(bold=True, size=14)

# Table 1: Grade Distribution by Gender


ws['A3'] = "Table 1: Grade Distribution by Gender"
ws['A3'].font = Font(bold=True, size=12)

# Headers for Table 1


headers = ["Grade", "Male Count", "Male %", "Female Count", "Female %", "Total
Count", "Total %"]
for col_idx, header in enumerate(headers):
cell = ws.cell(row=4, column=col_idx+1, value=header)
cell.font = Font(bold=True)
cell.alignment = Alignment(horizontal='center')

# Add grade rows


grades = ["ONE", "TWO", "THREE", "FOUR", "F", "X", "TOTAL"]
for row_idx, grade in enumerate(grades):
ws.cell(row=row_idx+5, column=1, value=grade)

# Add formulas for counts and percentages


for row_idx, grade in enumerate(grades):
row = row_idx + 5

if grade == "TOTAL":
# Total Males
ws.cell(row=row, column=2, value=f"=SUM(B5:B{row-1})")
# Total Females
ws.cell(row=row, column=4, value=f"=SUM(D5:D{row-1})")
# Total Students
ws.cell(row=row, column=6, value=f"=SUM(F5:F{row-1})")
# Percentages will be 100%
ws.cell(row=row, column=3, value="100%")
ws.cell(row=row, column=5, value="100%")
ws.cell(row=row, column=7, value="100%")
else:
# Male Count
ws.cell(row=row, column=2, value=f'=COUNTIFS(\'Mark Schedule\'!$C:
$C,"M",\'Mark Schedule\'!$H:$H,"{grade}")')
# Female Count
ws.cell(row=row, column=4, value=f'=COUNTIFS(\'Mark Schedule\'!$C:
$C,"F",\'Mark Schedule\'!$H:$H,"{grade}")')
# Total Count
ws.cell(row=row, column=6, value=f'=COUNTIFS(\'Mark Schedule\'!$H:
$H,"{grade}")')
# Male Percentage
ws.cell(row=row, column=3, value=f'=IF(B11=0,0,B{row}/B11)')
# Female Percentage
ws.cell(row=row, column=5, value=f'=IF(D11=0,0,D{row}/D11)')
# Total Percentage
ws.cell(row=row, column=7, value=f'=IF(F11=0,0,F{row}/F11)')

# Format percentage cells


for row in range(5, 12):
for col in [3, 5, 7]:
ws.cell(row=row, column=col).number_format = "0.0%"

# Table 2: Performance Categories Summary


ws['A14'] = "Table 2: Performance Categories Summary"
ws['A14'].font = Font(bold=True, size=12)

# Headers for Table 2


for col_idx, header in enumerate(headers):
if col_idx == 0:
header = "Standard"
cell = ws.cell(row=15, column=col_idx+1, value=header)
cell.font = Font(bold=True)
cell.alignment = Alignment(horizontal='center')

# Add standard rows


standards = ["DISTINCTION", "MERIT", "CREDIT", "PASS", "FAIL", "ABSENT",
"TOTAL"]
for row_idx, standard in enumerate(standards):
ws.cell(row=row_idx+16, column=1, value=standard)

# Add formulas for counts and percentages


for row_idx, standard in enumerate(standards):
row = row_idx + 16

if standard == "TOTAL":
# Total Males
ws.cell(row=row, column=2, value=f"=SUM(B16:B{row-1})")
# Total Females
ws.cell(row=row, column=4, value=f"=SUM(D16:D{row-1})")
# Total Students
ws.cell(row=row, column=6, value=f"=SUM(F16:F{row-1})")
# Percentages will be 100%
ws.cell(row=row, column=3, value="100%")
ws.cell(row=row, column=5, value="100%")
ws.cell(row=row, column=7, value="100%")
else:
# Male Count
ws.cell(row=row, column=2, value=f'=COUNTIFS(\'Mark Schedule\'!$C:
$C,"M",\'Mark Schedule\'!$I:$I,"{standard}")')
# Female Count
ws.cell(row=row, column=4, value=f'=COUNTIFS(\'Mark Schedule\'!$C:
$C,"F",\'Mark Schedule\'!$I:$I,"{standard}")')
# Total Count
ws.cell(row=row, column=6, value=f'=COUNTIFS(\'Mark Schedule\'!$I:
$I,"{standard}")')
# Male Percentage
ws.cell(row=row, column=3, value=f'=IF(B22=0,0,B{row}/B22)')
# Female Percentage
ws.cell(row=row, column=5, value=f'=IF(D22=0,0,D{row}/D22)')
# Total Percentage
ws.cell(row=row, column=7, value=f'=IF(F22=0,0,F{row}/F22)')

# Format percentage cells


for row in range(16, 23):
for col in [3, 5, 7]:
ws.cell(row=row, column=col).number_format = "0.0%"

# Table 3: Key Performance Indicators


ws['A25'] = "Table 3: Key Performance Indicators"
ws['A25'].font = Font(bold=True, size=12)

# Headers for Table 3


kpi_headers = ["Indicator", "Male", "Female", "Overall"]
for col_idx, header in enumerate(kpi_headers):
cell = ws.cell(row=26, column=col_idx+1, value=header)
cell.font = Font(bold=True)
cell.alignment = Alignment(horizontal='center')

# Add KPI rows


kpis = [
"Pass Rate (ONE-FOUR)",
"Fail Rate (F)",
"Absence Rate (X)",
"Male to Female Ratio",
"Average Score"
]

for row_idx, kpi in enumerate(kpis):


ws.cell(row=row_idx+27, column=1, value=kpi)

# Add formulas for KPIs


# Pass Rate (ONE-FOUR)
ws['B27'] = '=COUNTIFS(\'Mark Schedule\'!$C:$C,"M",\'Mark Schedule\'!$H:
$H,"ONE")+COUNTIFS(\'Mark Schedule\'!$C:$C,"M",\'Mark Schedule\'!$H:$H,"TWO")
+COUNTIFS(\'Mark Schedule\'!$C:$C,"M",\'Mark Schedule\'!$H:$H,"THREE")
+COUNTIFS(\'Mark Schedule\'!$C:$C,"M",\'Mark Schedule\'!$H:$H,"FOUR")'
ws['B27'].number_format = "0.0%"

ws['C27'] = '=COUNTIFS(\'Mark Schedule\'!$C:$C,"F",\'Mark Schedule\'!$H:


$H,"ONE")+COUNTIFS(\'Mark Schedule\'!$C:$C,"F",\'Mark Schedule\'!$H:$H,"TWO")
+COUNTIFS(\'Mark Schedule\'!$C:$C,"F",\'Mark Schedule\'!$H:$H,"THREE")
+COUNTIFS(\'Mark Schedule\'!$C:$C,"F",\'Mark Schedule\'!$H:$H,"FOUR")'
ws['C27'].number_format = "0.0%"

ws['D27'] = '=COUNTIFS(\'Mark Schedule\'!$H:$H,"ONE")+COUNTIFS(\'Mark


Schedule\'!$H:$H,"TWO")+COUNTIFS(\'Mark Schedule\'!$H:$H,"THREE")+COUNTIFS(\'Mark
Schedule\'!$H:$H,"FOUR")'
ws['D27'].number_format = "0.0%"
# Fail Rate (F)
ws['B28'] = '=COUNTIFS(\'Mark Schedule\'!$C:$C,"M",\'Mark Schedule\'!$H:
$H,"F")/COUNTIFS(\'Mark Schedule\'!$C:$C,"M")'
ws['B28'].number_format = "0.0%"

ws['C28'] = '=COUNTIFS(\'Mark Schedule\'!$C:$C,"F",\'Mark Schedule\'!$H:


$H,"F")/COUNTIFS(\'Mark Schedule\'!$C:$C,"F")'
ws['C28'].number_format = "0.0%"

ws['D28'] = '=COUNTIFS(\'Mark Schedule\'!$H:$H,"F")/COUNTIFS(\'Mark Schedule\'!


$A:$A,"<>"")'
ws['D28'].number_format = "0.0%"

# Absence Rate (X)


ws['B29'] = '=COUNTIFS(\'Mark Schedule\'!$C:$C,"M",\'Mark Schedule\'!$H:
$H,"X")/COUNTIFS(\'Mark Schedule\'!$C:$C,"M")'
ws['B29'].number_format = "0.0%"

ws['C29'] = '=COUNTIFS(\'Mark Schedule\'!$C:$C,"F",\'Mark Schedule\'!$H:


$H,"X")/COUNTIFS(\'Mark Schedule\'!$C:$C,"F")'
ws['C29'].number_format = "0.0%"

ws['D29'] = '=COUNTIFS(\'Mark Schedule\'!$H:$H,"X")/COUNTIFS(\'Mark Schedule\'!


$A:$A,"<>"")'
ws['D29'].number_format = "0.0%"

# Male to Female Ratio


ws['B30'] = '=COUNTIFS(\'Mark Schedule\'!$C:$C,"M")'
ws['C30'] = '=COUNTIFS(\'Mark Schedule\'!$C:$C,"F")'
ws['D30'] = '=B30&":"&C30'

# Average Score
ws['B31'] = '=AVERAGEIFS(\'Mark Schedule\'!$G:$G,\'Mark Schedule\'!$C:$C,"M")'
ws['B31'].number_format = "0.00"

ws['C31'] = '=AVERAGEIFS(\'Mark Schedule\'!$G:$G,\'Mark Schedule\'!$C:$C,"F")'


ws['C31'].number_format = "0.00"

ws['D31'] = '=AVERAGE(\'Mark Schedule\'!$G:$G)'


ws['D31'].number_format = "0.00"

def setup_dashboard(ws):
"""
Set up the Dashboard sheet with KPIs and charts
"""
# Add sheet title and information
ws['A1'] = "Class Assessment Dashboard"
ws['A1'].font = Font(bold=True, size=16)

ws['A2'] = "Class Performance Analysis"


ws['A2'].font = Font(italic=True, size=12)

ws['A3'] = f"Report Generated: {datetime.datetime.now().strftime('%Y-%m-%d')}"

# KPI Section
ws['A5'] = "Key Performance Indicators"
ws['A5'].font = Font(bold=True, size=14)

# KPI Headers
kpi_titles = [
"Class Average",
"Male Average",
"Female Average",
"Pass Rate",
"Distinction Rate",
"Merit Rate",
"Credit Rate",
"Pass Rate (Grade 4)",
"Failure Rate",
"Absence Rate",
"Total Students"
]

# Add KPI boxes


for i, title in enumerate(kpi_titles):
row = i + 6
ws.cell(row=row, column=1, value=title)
ws.cell(row=row, column=2, value=get_kpi_formula(title))

# Format numbers
if "Rate" in title:
ws.cell(row=row, column=2).number_format = "0.0%"
elif "Average" in title:
ws.cell(row=row, column=2).number_format = "0.00"

# Add charts
create_charts(ws)

def get_kpi_formula(kpi_title):
"""
Get the appropriate formula for each KPI
"""
if kpi_title == "Class Average":
return "=AVERAGE('Mark Schedule'!G:G)"
elif kpi_title == "Male Average":
return "=AVERAGEIFS('Mark Schedule'!G:G,'Mark Schedule'!C:C,\"M\")"
elif kpi_title == "Female Average":
return "=AVERAGEIFS('Mark Schedule'!G:G,'Mark Schedule'!C:C,\"F\")"
elif kpi_title == "Pass Rate":
return "=COUNTIFS('Mark Schedule'!H:H,\"ONE\")+COUNTIFS('Mark Schedule'!
H:H,\"TWO\")+COUNTIFS('Mark Schedule'!H:H,\"THREE\")+COUNTIFS('Mark Schedule'!
H:H,\"FOUR\")/COUNTIFS('Mark Schedule'!A:A,\"<>\"\"\")"
elif kpi_title == "Distinction Rate":
return "=COUNTIFS('Mark Schedule'!H:H,\"ONE\")/COUNTIFS('Mark Schedule'!
A:A,\"<>\"\"\")"
elif kpi_title == "Merit Rate":
return "=COUNTIFS('Mark Schedule'!H:H,\"TWO\")/COUNTIFS('Mark Schedule'!
A:A,\"<>\"\"\")"
elif kpi_title == "Credit Rate":
return "=COUNTIFS('Mark Schedule'!H:H,\"THREE\")/COUNTIFS('Mark Schedule'!
A:A,\"<>\"\"\")"
elif kpi_title == "Pass Rate (Grade 4)":
return "=COUNTIFS('Mark Schedule'!H:H,\"FOUR\")/COUNTIFS('Mark Schedule'!
A:A,\"<>\"\"\")"
elif kpi_title == "Failure Rate":
return "=COUNTIFS('Mark Schedule'!H:H,\"F\")/COUNTIFS('Mark Schedule'!
A:A,\"<>\"\"\")"
elif kpi_title == "Absence Rate":
return "=COUNTIFS('Mark Schedule'!H:H,\"X\")/COUNTIFS('Mark Schedule'!
A:A,\"<>\"\"\")"
elif kpi_title == "Total Students":
return "=COUNTIFS('Mark Schedule'!A:A,\"<>\"\"\")-1" # Subtract 1 for
header row
else:
return "=0"

def create_charts(ws):
"""
Create charts for the dashboard
"""
# Create a bar chart for grades by gender
bar_chart = BarChart()
bar_chart.title = "Grade Distribution by Gender"
bar_chart.style = 10
bar_chart.x_axis.title = "Grade"
bar_chart.y_axis.title = "Number of Students"

# Add data to the chart


# In a real implementation, this would need to be adjusted based on the actual
data
# For this example, we'll just show the concept

# Create data table for the chart


ws['D5'] = "Grade Distribution Data"
ws['D5'].font = Font(bold=True)

# Headers
grade_headers = ["Grade", "Male", "Female", "Total"]
for i, header in enumerate(grade_headers):
ws.cell(row=6, column=i+4, value=header)
ws.cell(row=6, column=i+4).font = Font(bold=True)

# Add data rows


grades = ["ONE", "TWO", "THREE", "FOUR", "F", "X"]
for i, grade in enumerate(grades):
row = i + 7
ws.cell(row=row, column=4, value=grade)
ws.cell(row=row, column=5, value=f"=COUNTIFS('Mark Schedule'!$C:
$C,\"M\",'Mark Schedule'!$H:$H,\"{grade}\")")
ws.cell(row=row, column=6, value=f"=COUNTIFS('Mark Schedule'!$C:
$C,\"F\",'Mark Schedule'!$H:$H,\"{grade}\")")
ws.cell(row=row, column=7, value=f"=COUNTIFS('Mark Schedule'!$H:
$H,\"{grade}\")")

# Add a bar chart for grade distribution


grade_chart = BarChart()
grade_chart.type = "col"
grade_chart.style = 10
grade_chart.title = "Grade Distribution by Gender"
grade_chart.x_axis.title = "Grade"
grade_chart.y_axis.title = "Number of Students"

data = Reference(ws, min_col=5, min_row=6, max_row=12, max_col=7)


cats = Reference(ws, min_col=4, min_row=7, max_row=12)
grade_chart.add_data(data, titles_from_data=True)
grade_chart.set_categories(cats)
grade_chart.shape = 4
ws.add_chart(grade_chart, "D15")

# Create data for pie charts


ws['I5'] = "Performance Distribution Data"
ws['I5'].font = Font(bold=True)

# Headers
perf_headers = ["Standard", "Male", "Female", "Total"]
for i, header in enumerate(perf_headers):
ws.cell(row=6, column=i+9, value=header)
ws.cell(row=6, column=i+9).font = Font(bold=True)

# Add data rows


standards = ["DISTINCTION", "MERIT", "CREDIT", "PASS", "FAIL", "ABSENT"]
for i, standard in enumerate(standards):
row = i + 7
ws.cell(row=row, column=9, value=standard)
ws.cell(row=row, column=10, value=f"=COUNTIFS('Mark Schedule'!$C:
$C,\"M\",'Mark Schedule'!$I:$I,\"{standard}\")")
ws.cell(row=row, column=11, value=f"=COUNTIFS('Mark Schedule'!$C:
$C,\"F\",'Mark Schedule'!$I:$I,\"{standard}\")")
ws.cell(row=row, column=12, value=f"=COUNTIFS('Mark Schedule'!$I:
$I,\"{standard}\")")

# Add a pie chart for performance distribution


pie_chart = PieChart()
pie_chart.title = "Performance Distribution by Standard"

data = Reference(ws, min_col=12, min_row=6, max_row=12)


cats = Reference(ws, min_col=9, min_row=7, max_row=12)
pie_chart.add_data(data, titles_from_data=True)
pie_chart.set_categories(cats)
pie_chart.dataLabels = DataLabelList()
pie_chart.dataLabels.showPercent = True

ws.add_chart(pie_chart, "I15")

# Add a pie chart for gender distribution


gender_pie_chart = PieChart()
gender_pie_chart.title = "Gender Distribution"

ws['L5'] = "Gender Distribution Data"


ws['L5'].font = Font(bold=True)

# Headers
gender_headers = ["Gender", "Count"]
for i, header in enumerate(gender_headers):
ws.cell(row=6, column=i+12, value=header)
ws.cell(row=6, column=i+12).font = Font(bold=True)

# Add data rows


genders = ["M", "F"]
for i, gender in enumerate(genders):
row = i + 7
ws.cell(row=row, column=12, value=gender)
ws.cell(row=row, column=13, value=f"=COUNTIFS('Mark Schedule'!$C:
$C,\"{gender}\")")
data = Reference(ws, min_col=13, min_row=6, max_row=8)
cats = Reference(ws, min_col=12, min_row=7, max_row=8)
gender_pie_chart.add_data(data, titles_from_data=True)
gender_pie_chart.set_categories(cats)
gender_pie_chart.dataLabels = DataLabelList()
gender_pie_chart.dataLabels.showPercent = True

ws.add_chart(gender_pie_chart, "L15")

def define_named_ranges(wb):
"""
Define named ranges for easier formula references
"""
# Define named ranges for the Mark Schedule sheet
mark_schedule = wb['Mark Schedule']

# Named range for the list of student names

wb.defined_names.append(openpyxl.workbook.defined_name.DefinedName('StudentNames',
attr_text=f"'Mark Schedule'!$B$2:$B${mark_schedule.max_row}"))

# Named range for the list of grades


wb.defined_names.append(openpyxl.workbook.defined_name.DefinedName('Grades',
attr_text=f"'Mark Schedule'!$H$2:$H${mark_schedule.max_row}"))

# Named range for the list of standards


wb.defined_names.append(openpyxl.workbook.defined_name.DefinedName('Standards',
attr_text=f"'Mark Schedule'!$I$2:$I${mark_schedule.max_row}"))

def adjust_column_widths(wb):
"""
Adjust column widths for better readability
"""
# Adjust column widths for the Mark Schedule sheet
mark_schedule = wb['Mark Schedule']
mark_schedule.column_dimensions['A'].width = 5 # SN
mark_schedule.column_dimensions['B'].width = 20 # Name
mark_schedule.column_dimensions['C'].width = 8 # Sex
mark_schedule.column_dimensions['D'].width = 12 # Assessment 1
mark_schedule.column_dimensions['E'].width = 12 # Assessment 2
mark_schedule.column_dimensions['F'].width = 12 # Assessment 3
mark_schedule.column_dimensions['G'].width = 12 # Average
mark_schedule.column_dimensions['H'].width = 10 # Grade
mark_schedule.column_dimensions['I'].width = 12 # Standard

# Adjust column widths for the Summary Analysis sheet


summary_analysis = wb['Summary Analysis']
summary_analysis.column_dimensions['A'].width = 20 # Grade/Standard
summary_analysis.column_dimensions['B'].width = 12 # Male Count
summary_analysis.column_dimensions['C'].width = 12 # Male %
summary_analysis.column_dimensions['D'].width = 12 # Female Count
summary_analysis.column_dimensions['E'].width = 12 # Female %
summary_analysis.column_dimensions['F'].width = 12 # Total Count
summary_analysis.column_dimensions['G'].width = 12 # Total %

# Adjust column widths for the Dashboard sheet


dashboard = wb['Dashboard']
dashboard.column_dimensions['A'].width = 25 # KPI Titles
dashboard.column_dimensions['B'].width = 15 # KPI Values
def add_cell_protection(wb):
"""
Add cell protection to prevent accidental edits
"""
# Protect all sheets
for sheet in wb:
sheet.protection.sheet = True

# Unprotect cells that need to be editable


mark_schedule = wb['Mark Schedule']
for row in range(2, mark_schedule.max_row + 1):
for col in ['B', 'C', 'D', 'E', 'F']: # Name, Sex, Assessment 1, 2, 3
mark_schedule[f'{col}{row}'].protection =
openpyxl.styles.Protection(locked=False)

def add_instructions(wb):
"""
Add instructions to the Mark Schedule sheet
"""
mark_schedule = wb['Mark Schedule']

# Add instructions at the bottom of the sheet


instructions = [
"Instructions:",
"1. Enter student names in column B.",
"2. Select student sex (M/F) in column C.",
"3. Enter assessment scores (0-100) in columns D-F.",
"4. The Average, Grade, and Standard columns will be calculated
automatically.",
"5. Do not edit columns G-I (calculated fields)."
]

for i, instruction in enumerate(instructions):


mark_schedule.cell(row=mark_schedule.max_row + 2, column=1,
value=instruction)

def set_print_areas(wb):
"""
Set print areas for each sheet
"""
# Set print area for Mark Schedule
mark_schedule = wb['Mark Schedule']
mark_schedule.print_area = f"A1:I{mark_schedule.max_row}"

# Set print area for Summary Analysis


summary_analysis = wb['Summary Analysis']
summary_analysis.print_area = f"A1:G{summary_analysis.max_row}"

# Set print area for Dashboard


dashboard = wb['Dashboard']
dashboard.print_area = f"A1:M{dashboard.max_row}"

# Run the function to create the Excel file


create_class_assessment_tool()

You might also like