Practice generating PDF student mark sheets using multi-source data (CSV, Excel, JSON, XML, SQL dump, SQLite, and MySQL) with custom templates directly inside Google Colab.
View on GitHub
We will create this sample mark sheet by using data from SQLite (or MySQL) database student table.
We used two files for this script: the main.py file creates the canvas and uses the template file temp_marksheet.py to generate the PDF file with data. Source code for both files is available below.
We will use the SQLite database student table. The script can easily be modified to retrieve data from a MySQL database table or external files.
We can also use a MySQL database:
Full student table with SQL DumpYou can also download sample student datasets in multiple formats directly from the plus2net pandas student tutorial page:
!wget https://www.plus2net.com/python/download/student.csv
!wget https://www.plus2net.com/python/download/student.xlsx
!wget https://www.plus2net.com/python/download/student.json
!wget https://www.plus2net.com/python/download/student.xml
!wget https://www.plus2net.com/python/download/student.sql
We use try-except error handling to catch and display database errors cleanly:
from sqlalchemy import create_engine
from sqlalchemy.exc import SQLAlchemyError
db_file = 'my_db.db'
try:
file1 = 'sqlite:///' + db_file
my_conn = create_engine(file1)
# For MySQL use:
# my_conn = create_engine("mysql+mysqldb://root:pw@localhost/my_tutorial")
r_set = my_conn.execute("SELECT * FROM student WHERE id = 6")
data = r_set.fetchone()
except SQLAlchemyError as e:
error = str(e.__dict__['orig'])
print(error)
import csv
with open('student.csv', mode='r', encoding='utf-8') as f:
reader = csv.DictReader(f)
students = list(reader)
# Pick target student record by ID
st = next(s for s in students if s['id'] == '6')
data = (int(st['id']), st['name'], st['class'], int(st['mark']), st['gender'])
import openpyxl
wb = openpyxl.load_workbook('student.xlsx')
sheet = wb.active
rows = list(sheet.iter_rows(values_only=True))
header, records = rows[0], rows[1:]
data = next(r for r in records if r[0] == 6)
import json
with open('student.json', 'r', encoding='utf-8') as f:
json_list = json.load(f)
st = next(s for s in json_list if int(s['id']) == 6)
data = (st['id'], st['name'], st['class'], int(st['mark']), st['gender'])
import xml.etree.ElementTree as ET
tree = ET.parse('student.xml')
root = tree.getroot()
st = next(elem for elem in root.findall('student') if elem.find('id').text == '6')
data = (int(st.find('id').text), st.find('name').text, st.find('class').text, int(st.find('mark').text), st.find('gender').text)
from pathlib import Path
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter
from temp_marksheet import my_temp # import template
from sqlalchemy import create_engine
from sqlalchemy.exc import SQLAlchemyError
output_dir = Path('output')
output_dir.mkdir(exist_ok=True)
my_path = output_dir / 'student_marksheet.pdf'
c = canvas.Canvas(str(my_path), pagesize=letter)
c = my_temp(c) # run template
c.setFillColorRGB(1, 0, 0)
c.setFont("Helvetica", 70)
c.drawRightString(6.4 * inch, 7.5 * inch, 'MARKSHEET')
c.setFillColorRGB(0, 0, 0)
c.setFont("Helvetica", 24)
c.drawRightString(2.5 * inch, 6.8 * inch, 'ID:')
c.drawRightString(2.5 * inch, 6 * inch, 'Name:')
c.drawRightString(2.5 * inch, 5 * inch, 'Class:')
c.drawRightString(2.5 * inch, 4 * inch, 'Gender:')
c.drawRightString(2.5 * inch, 3 * inch, 'Mark:')
c.drawRightString(3 * inch, 2 * inch, 'Grade:')
c.drawRightString(6 * inch, -0.4 * inch, 'Signature')
# Connect to SQLite/MySQL and fetch student details
db_file = 'my_db.db'
try:
file1 = 'sqlite:///' + db_file
my_conn = create_engine(file1)
r_set = my_conn.execute("SELECT * FROM student WHERE id = 6")
data = r_set.fetchone()
except SQLAlchemyError as e:
error = str(e.__dict__['orig'])
print(error)
c.setFillColorRGB(0, 0, 1)
c.setFont("Helvetica", 20)
c.drawString(3 * inch, 6.8 * inch, str(data[0]))
c.drawString(3 * inch, 6 * inch, data[1])
c.drawString(3 * inch, 5 * inch, data[2])
c.drawString(3 * inch, 4 * inch, data[4])
c.drawString(3 * inch, 3 * inch, str(data[3]))
if data[3] >= 80:
c.drawString(4 * inch, 2 * inch, 'A')
elif data[3] >= 60:
c.drawString(4 * inch, 2 * inch, 'B')
else:
c.drawString(4 * inch, 2 * inch, 'C')
c.showPage()
c.save()
print(f"Mark sheet PDF generated: {my_path}")
from reportlab.lib.units import inch
from datetime import date
def my_temp(c):
c.translate(inch, inch)
c.setFont("Helvetica", 14)
c.setStrokeColorRGB(0.1, 0.8, 0.1)
c.setFillColorRGB(0, 0, 1) # font color
# Header Info
c.drawString(0, 9 * inch, "1234, ABCD Road")
c.drawString(0, 8.7 * inch, "Mycity, ZIP : 12345")
c.setFillColorRGB(0, 0.5, 1)
c.drawString(2 * inch, 8.7 * inch, " www.plus2net.com ")
c.setFillColorRGB(0, 0, 0)
c.line(0, 8.6 * inch, 6.8 * inch, 8.6 * inch)
dt = date.today().strftime('%d-%b-%Y')
c.drawString(5.6 * inch, 9.3 * inch, dt)
c.setFont("Helvetica", 8)
c.drawString(3 * inch, 9.6 * inch, 'End Semester Examination')
c.line(0, -0.7 * inch, 6.8 * inch, -0.7 * inch)
c.setFillColorRGB(1, 0, 0)
c.drawString(0, -0.9 * inch, "\u00A9 plus2net.com")
# Watermark
c.rotate(45)
c.setFillColorCMYK(0, 0, 0, 0.08)
c.setFont("Helvetica", 100)
c.drawString(2 * inch, 1 * inch, "SAMPLE")
c.rotate(-45)
return c
Automated mark sheet generation with customized ReportLab templates is essential across educational institutions:
c.translate(inch, inch) shifts the canvas origin (0, 0) by 1 inch right and upwards. Ensure subsequent coordinate calls account for this origin shift.r_set.fetchone() or list search iterators return None before attempting tuple indexing to avoid TypeError: 'NoneType' object is not subscriptable.Q: How do I change the grading criteria logic?
A: Modify the conditional logic checking data[3] (the mark value index) inside main.py before drawing the grade string on canvas.
Q: Can I batch generate mark sheets for all students in a database?
A: Yes! Loop over r_set.fetchall(), invoke c = my_temp(c) for each student record, write the text details, and call c.showPage() inside the loop before saving the canvas.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.