BRAINBIT INFOTECH PVT LTD · RANCHI
COMPLETE PYTHON COURSE · PART 5 OF 5

Pro Skills & Capstone Projects

Desktop apps with Tkinter, automation, professional habits like testing and virtual environments, and five complete projects.

3 modules~17.5 hours15 practice examplesNeeds Parts 1-4

By the end of this part you can build

  • A GUI tip calculator and login window
  • Automated certificates and file renaming
  • Tested, typed and logged code
  • Five capstone projects for your portfolio

Every code example has a ▶ Run button. Click ✎ Edit & Run, or press L, to open the Python Lab.

ROADMAP FOR THIS PART

Module 21GUI Apps with Tkinter & Office Automation4 h
Module 22Professional Python: Tools & Habits3.5 h
Module 23Capstone Projects10 h
print("Hi") >>> _
PythonBefore we begin

How to Use This Part

Suggested teaching hours per module (Part 5)02.557.5104 hM213.5 hM2210 hM23

Every module has

  1. 1
    A "Where is it used?" diagram of real projects
  2. 2
    Concept slides with code + terminal: press ▶ Run to watch the output appear
  3. 3
    Step-through animations: watch variables change line by line
  4. 4
    A Live Lab: edit the code and run real Python in the browser
  5. 5
    5 practice examples (Beginner to Advanced) with solutions
  6. 6
    An animated recap + quiz
Controls→ next, ← back, M menu, L Python Lab, F fullscreen. In the Lab, Ctrl+Enter runs the code.
Running PythonOutputs on the slides were produced by real Python 3, so they work offline. The Lab loads Python from the internet the first time (10-40 s). Offline? Use ⬇ .py and run the file in IDLE or VS Code (folder D:\python-practice).
MODULE 21

GUI Apps with Tkinter & Office Automation

⏱ 4 hours5 practice examplesStep-through animationsPython Lab

You will learn

  • Desktop windows with Tkinter: labels, buttons, entries
  • Layouts (pack, grid) and events (button clicks)
  • Message boxes and a simple login window
  • Automating Excel with openpyxl
  • Making certificates with Pillow and renaming files in bulk

You will build

  • A tip calculator window
  • A certificate generator for 100 students
  • A bulk photo renamer
Tk()Button(text="OK")openpyxl
Module 21Real-world use

Where Are GUIs and Automation Used?

GUI & automationwindows & robots for office work🖥Desktop billingshop counters🏫School kiosksresult lookup🧮CalculatorsEMI, GST tools🏅Certificates500 in a minute📊Excel reportsauto-filled sheets📧Emailssend reports

Real projects that use this

  • Many small offices in Ranchi use simple desktop tools built with Tkinter or similar
  • Automation saves hours: generate 500 certificates, fill Excel reports, rename photos
  • Python is the most popular language for office automation ("Automate the Boring Stuff")
  • Tkinter comes built into Python on Windows: nothing to install
Module 21First window

Your First Tkinter Window

first_gui.pyPY
1import tkinter as tk
2
3root = tk.Tk() # the main window
4root.title("BrainBit Infotech Welcome")
5root.geometry("360x150")
6
7label = tk.Label(root, text="Namaste, Ranchi!", font=("Segoe UI", 18))
8label.pack(pady=10)
9
10def say_hi():
11 label.config(text="Welcome to Python GUI!")
12
13button = tk.Button(root, text="Click me", command=say_hi)
14button.pack()
15
16root.mainloop() # keep the window open
✦BrainBit Infotech Welcome— ☐ ✕
Namaste, Ranchi!

A working preview of what the code shows on Windows. Click the button!

  1. 1
    tk.Tk() creates the window
  2. 2
    Create widgets (Label, Button) and place them with pack()
  3. 3
    command=say_hi: the function runs when clicked (an event)
  4. 4
    mainloop() waits for clicks and key presses
Common mistakeRun Tkinter programs on your own computer (IDLE or VS Code). They cannot run in the browser Lab.
Module 21Widgets

Common Widgets and Layout Managers

✦Admission Form (widget gallery)— ☐ ✕
Student name:
Science Commerce
Hostel needed
WidgetUseRead value
Labelshow text / image
Entryone-line inpute.get()
Textmulti-line inputt.get("1.0", "end")
Buttonrun a functioncommand=f
Checkbuttonyes / noBooleanVar
Radiobuttonone of manyStringVar
ttk.Comboboxdrop-downcb.get()
LayoutHow it places widgets
pack()one after another (top to bottom)
grid(row=, column=)in a table: best for forms
place(x=, y=)exact position (rarely needed)
Common mistakeDo not mix pack and grid in the same container.
Module 21Events in action

Read Inputs, Calculate, Show the Result

tip_calculator.pyPY
1import tkinter as tk
2from tkinter import messagebox
3
4def calculate():
5 try:
6 bill = float(bill_e.get())
7 pct = float(pct_e.get())
8 people = int(ppl_e.get())
9 except ValueError:
10 messagebox.showerror("Oops", "Please enter numbers only")
11 return
12 tip = bill * pct / 100
13 result.config(text=f"Tip: Rs {tip:.2f}\nTotal: Rs {bill + tip:.2f}\nEach pays: Rs {(bill + tip) / people:.2f}")
14
15root = tk.Tk()
16root.title("Tip Calculator")
17for r, text in enumerate(["Bill (Rs)", "Tip %", "People"]):
18 tk.Label(root, text=text).grid(row=r, column=0, sticky="w", padx=8, pady=4)
19bill_e, pct_e, ppl_e = tk.Entry(root), tk.Entry(root), tk.Entry(root)
20for r, e in enumerate([bill_e, pct_e, ppl_e]):
21 e.grid(row=r, column=1, padx=8)
22pct_e.insert(0, "10")
23ppl_e.insert(0, "1")
24tk.Button(root, text="Calculate", command=calculate).grid(row=3, column=0, columnspan=2, pady=6)
25result = tk.Label(root, text="", justify="left")
26result.grid(row=4, column=0, columnspan=2)
27root.mainloop()
✦Tip Calculator— ☐ ✕

Live preview: change the numbers and press Calculate.

  1. 1
    entry.get() reads text from an Entry
  2. 2
    Convert with float() / int() inside try (Module 13)
  3. 3
    label.config(text=...) updates the screen
  4. 4
    messagebox.showerror shows a pop-up
Module 21Excel automation

Automating Excel with openpyxl

main.py  PY
1from openpyxl import Workbook, load_workbook
2from openpyxl.styles import Font, PatternFill
3
4wb = Workbook()
5ws = wb.active
6ws.title = "Class 9A"
7ws.append(["Name", "Maths", "Science", "Total"])
8for name, m, s in [("Aman", 78, 85), ("Priya", 95, 91), ("Rahul", 65, 58)]:
9 r = ws.max_row + 1
10 ws.append([name, m, s, f"=B{r}+C{r}"]) # real Excel formula
11for cell in ws[1]:
12 cell.font = Font(bold=True, color="FFFFFF")
13 cell.fill = PatternFill("solid", fgColor="3776AB")
14ws.column_dimensions["A"].width = 14
15wb.save("class_9a.xlsx")
16
17ws2 = load_workbook("class_9a.xlsx").active
18for row in ws2.iter_rows(values_only=True):
19 print(row)
Terminal
C:\python-practice> python main.py
('Name', 'Maths', 'Science', 'Total')
('Aman', 78, 85, '=B2+C2')
('Priya', 95, 91, '=B3+C3')
('Rahul', 65, 58, '=B4+C4')
📄 class_9a.xlsx (file on disk)TXT
1Sheet "Class 9A" (Excel shows formulas as results)
2Name | Maths | Science | Total
3Aman | 78 | 85 | =B2+C2
4Priya | 95 | 91 | =B3+C3
5Rahul | 65 | 58 | =B4+C4
  1. 1
    pip install openpyxl
  2. 2
    Workbook() = new file, ws.append(list) = new row
  3. 3
    Formulas are written as text starting with =
  4. 4
    Style cells with Font and PatternFill, then save()
In real projectsOffices spend hours copying data into Excel. A Python script can fill, format and save hundreds of sheets in seconds.
Module 21Image automation

Generate Certificates Automatically with Pillow

main.py  PY
1from PIL import Image, ImageDraw, ImageFont
2
3def font(size, bold=False):
4 for name in (["arialbd.ttf", "DejaVuSans-Bold.ttf"] if bold else ["arial.ttf", "DejaVuSans.ttf"]):
5 try:
6 return ImageFont.truetype(name, size)
7 except OSError:
8 pass
9 return ImageFont.load_default()
10
11def certificate(name, course, grade, filename):
12 img = Image.new("RGB", (900, 560), "#fffdf5")
13 d = ImageDraw.Draw(img)
14 d.rectangle([15, 15, 885, 545], outline="#b07d00", width=8)
15 d.rectangle([32, 32, 868, 528], outline="#3776ab", width=2)
16 d.text((450, 95), "BRAINBIT INFOTECH PVT LTD, RANCHI", fill="#3776ab", font=font(30, True), anchor="mm")
17 d.text((450, 165), "Certificate of Completion", fill="#0b1f3a", font=font(44, True), anchor="mm")
18 d.text((450, 235), "This is to certify that", fill="#5b6577", font=font(22), anchor="mm")
19 d.text((450, 295), name, fill="#c92a2a", font=font(48, True), anchor="mm")
20 d.text((450, 365), f"has completed the {course} course with grade {grade}", fill="#0b1f3a", font=font(24), anchor="mm")
21 d.text((450, 470), "Director 08 October 2026", fill="#5b6577", font=font(20), anchor="mm")
22 img.save(filename)
23
24for i, (name, grade) in enumerate([("Priya Kumari", "A+"), ("Aman Oraon", "A")], 1):
25 certificate(name, "Python Programming", grade, f"cert_{i}.png")
26 print("Created", f"cert_{i}.png", "for", name)
Terminal
C:\python-practice> python main.py
Created cert_1.png for Priya Kumari
Created cert_2.png for Aman Oraon
🖼 cert_1.png (image saved by the program)
Module 21File automation

Rename Hundreds of Files in One Go

main.py  PY
1from pathlib import Path
2
3folder = Path("photos")
4photos = sorted(p for p in folder.iterdir() if p.suffix.lower() in (".jpg", ".jpeg"))
5for i, p in enumerate(photos, start=1):
6 new = folder / f"sarhul_trip_{i:03d}{p.suffix.lower()}"
7 print(f"{p.name:<32} -> {new.name}")
8 p.rename(new)
9
10print("Now:", sorted(x.name for x in folder.iterdir()))
Terminal
C:\python-practice> python main.py
IMG_20260915_101233.jpg -> sarhul_trip_001.jpg
IMG_20260915_101245.jpg -> sarhul_trip_002.jpg
IMG_20260916_083010.jpg -> sarhul_trip_003.jpg
WhatsApp Image 2026-09-17.jpeg -> sarhul_trip_004.jpeg
Now: ['notes.txt', 'sarhul_trip_001.jpg', 'sarhul_trip_002.jpg', 'sarhul_trip_003.jpg', 'sarhul_trip_004.jpeg']
  1. 1
    List the files and filter by extension
  2. 2
    Build the new name with a zero-padded counter {i:03d}
  3. 3
    p.rename(new) renames on disk
Common mistakeTest on a copy of the folder first, and print the plan before renaming. A wrong rename on 500 files is painful to undo.
In real projectsPhotographers, schools (student photos by roll number) and offices (scanned documents) automate renaming.
Module 21More automation

PDF Reports and Emails (Code to Run on Your Computer)

pdf_receipt.pyPY
1from reportlab.lib.pagesizes import A4
2from reportlab.pdfgen import canvas
3
4c = canvas.Canvas("fee_receipt.pdf", pagesize=A4)
5c.setFont("Helvetica-Bold", 18)
6c.drawString(72, 780, "BrainBit Infotech Pvt Ltd - Fee Receipt")
7c.setFont("Helvetica", 12)
8c.drawString(72, 750, "Student: Riya Kumari Course: Python")
9c.drawString(72, 730, "Amount paid: Rs 4,500 Date: 08-10-2026")
10c.save()
send_report.pyPY
1import smtplib
2from email.message import EmailMessage
3
4msg = EmailMessage()
5msg["Subject"] = "Monthly report - Class 9A"
6msg["From"] = "office@yourschool.in"
7msg["To"] = "principal@yourschool.in"
8msg.set_content("Please find the report attached.")
9with open("class_9a.xlsx", "rb") as f:
10 msg.add_attachment(f.read(), maintype="application", subtype="octet-stream", filename="class_9a.xlsx")
11
12with smtplib.SMTP_SSL("smtp.gmail.com", 465) as s:
13 s.login("office@yourschool.in", "APP_PASSWORD") # use an app password
14 s.send_message(msg)
Common mistakeNever write real passwords in code you share. Store them in environment variables (Module 22).
Module 21Hands-on

Live Lab: Try It Yourself

lab.py
Output Ready
Output of the starter code (press Run after you change it):
DOC3.pdf -> admission_01.pdf
Doc 2.pdf -> admission_02.pdf
doc1.PDF -> admission_03.pdf
['admission_01.pdf', 'admission_02.pdf', 'admission_03.pdf', 'receipt.jpg']
Your tasks
  1. Change the new name pattern to roll_01.pdf, roll_02.pdf ...
  2. Also rename the .jpg files to photo_01.jpg
  3. Print how many files of each type there are
  4. On your computer: run the Tkinter tip calculator and add a "Clear" button
PracticePractice 1 of 5

Tip Calculator Window

BeginnerScenario. Build the tip calculator from the slides and add a "Clear" button and keyboard support.

Your tasks

  1. Use grid for labels and entries
  2. Calculate on button click or when Enter is pressed: root.bind("<Return>", ...)
  3. Show an error box for bad input
  4. Add a Clear button that empties all entries
🏢 Used in real projects: Desktop utilities for shops and offices (billing, GST, EMI calculators) are small Tkinter-style apps.
tip_app.pyPY
1import tkinter as tk
2from tkinter import messagebox
3
4def calculate():
5 try:
6 bill = float(bill_e.get())
7 pct = float(pct_e.get())
8 people = int(ppl_e.get())
9 except ValueError:
10 messagebox.showerror("Oops", "Please enter numbers only")
11 return
12 tip = bill * pct / 100
13 result.config(text=f"Tip: Rs {tip:.2f}\nTotal: Rs {bill + tip:.2f}\nEach pays: Rs {(bill + tip) / people:.2f}")
14
15root = tk.Tk()
16root.title("Tip Calculator")
17for r, text in enumerate(["Bill (Rs)", "Tip %", "People"]):
18 tk.Label(root, text=text).grid(row=r, column=0, sticky="w", padx=8, pady=4)
19bill_e, pct_e, ppl_e = tk.Entry(root), tk.Entry(root), tk.Entry(root)
20for r, e in enumerate([bill_e, pct_e, ppl_e]):
21 e.grid(row=r, column=1, padx=8)
22pct_e.insert(0, "10")
23ppl_e.insert(0, "1")
24tk.Button(root, text="Calculate", command=calculate).grid(row=3, column=0, columnspan=2, pady=6)
25def clear():
26 for e in (bill_e, pct_e, ppl_e):
27 e.delete(0, tk.END)
28 result.config(text="")
29
30tk.Button(root, text="Clear", command=clear).grid(row=5, column=0, columnspan=2)
31root.bind("<Return>", lambda event: calculate())
32result = tk.Label(root, text="", justify="left")
33result.grid(row=4, column=0, columnspan=2)
34root.mainloop()
PracticePractice 2 of 5

Login Window with 3 Attempts

EasyScenario. A staff login window for a school office app. Lock the button after 3 wrong attempts.

Your tasks

  1. Entries for username and password (show="*")
  2. Check credentials on click
  3. Count attempts; disable the button with state="disabled" after 3
  4. Show success or error text in a Label
🏢 Used in real projects: Desktop software for clinics, shops and school offices starts with exactly this kind of login screen.
login_app.pyPY
1import tkinter as tk
2
3attempts = 0
4
5def login():
6 global attempts
7 if user.get().strip().lower() == "admin" and pwd.get() == "Ranchi@123":
8 msg.config(text="Login successful! Opening dashboard...", fg="green")
9 return
10 attempts += 1
11 if attempts >= 3:
12 msg.config(text="Too many attempts. Locked.", fg="red")
13 btn.config(state="disabled")
14 else:
15 msg.config(text=f"Wrong username or password ({3 - attempts} left)", fg="red")
16
17root = tk.Tk()
18root.title("Staff Login")
19tk.Label(root, text="Username").grid(row=0, column=0, padx=8, pady=6)
20tk.Label(root, text="Password").grid(row=1, column=0, padx=8)
21user = tk.Entry(root)
22pwd = tk.Entry(root, show="*")
23user.grid(row=0, column=1, padx=8)
24pwd.grid(row=1, column=1, padx=8)
25btn = tk.Button(root, text="Login", command=login)
26btn.grid(row=2, column=0, columnspan=2, pady=8)
27msg = tk.Label(root, text="")
28msg.grid(row=3, column=0, columnspan=2)
29root.mainloop()
PracticePractice 3 of 5

Stopwatch App with after()

IntermediateScenario. A stopwatch for timing speed tests in the computer lab. Tkinter uses root.after(ms, func) to update the screen regularly.

Your tasks

  1. A big Label for the time
  2. Start/Stop toggles a running flag
  3. tick() updates the label and schedules itself with after(100, tick)
  4. Reset sets the time back to 0
🏢 Used in real projects: Timers, dashboards and games use the same "update every few milliseconds" loop (after() in Tkinter, setInterval in JavaScript).
stopwatch.pyPY
1import tkinter as tk
2import time
3
4running = False
5start = 0.0
6elapsed = 0.0
7
8def tick():
9 if running:
10 shown = elapsed + (time.time() - start)
11 label.config(text=f"{shown:.1f} s")
12 root.after(100, tick) # call again in 100 ms
13
14def toggle():
15 global running, start, elapsed
16 if running:
17 elapsed += time.time() - start
18 running = False
19 btn.config(text="Start")
20 else:
21 start = time.time()
22 running = True
23 btn.config(text="Stop")
24 tick()
25
26def reset():
27 global running, elapsed
28 running, elapsed = False, 0.0
29 label.config(text="0.0 s")
30 btn.config(text="Start")
31
32root = tk.Tk()
33root.title("Exam Stopwatch")
34label = tk.Label(root, text="0.0 s", font=("Consolas", 40))
35label.pack(padx=20, pady=10)
36btn = tk.Button(root, text="Start", width=8, command=toggle)
37btn.pack(side="left", padx=20, pady=10)
38tk.Button(root, text="Reset", width=8, command=reset).pack(side="right", padx=20)
39root.mainloop()
PracticePractice 4 of 5

Certificates for a Whole Batch

ChallengingScenario. Read the batch list from a CSV and create one certificate image per student, named by roll number.

Your tasks

  1. Read batch.csv with csv.DictReader
  2. Reuse the certificate() function idea from the slides
  3. Save as certificates/roll_XX.png
  4. Print a count at the end
🏢 Used in real projects: Schools, coaching centres and event organisers generate hundreds of certificates and ID cards this way.
practice_21_4.py  PY
1from PIL import Image, ImageDraw, ImageFont
2
3def font(size, bold=False):
4 for name in (["arialbd.ttf", "DejaVuSans-Bold.ttf"] if bold else ["arial.ttf", "DejaVuSans.ttf"]):
5 try:
6 return ImageFont.truetype(name, size)
7 except OSError:
8 pass
9 return ImageFont.load_default()
10
11def certificate(name, course, grade, filename):
12 img = Image.new("RGB", (900, 560), "#fffdf5")
13 d = ImageDraw.Draw(img)
14 d.rectangle([15, 15, 885, 545], outline="#b07d00", width=8)
15 d.rectangle([32, 32, 868, 528], outline="#3776ab", width=2)
16 d.text((450, 95), "BRAINBIT INFOTECH PVT LTD, RANCHI", fill="#3776ab", font=font(30, True), anchor="mm")
17 d.text((450, 165), "Certificate of Completion", fill="#0b1f3a", font=font(44, True), anchor="mm")
18 d.text((450, 235), "This is to certify that", fill="#5b6577", font=font(22), anchor="mm")
19 d.text((450, 295), name, fill="#c92a2a", font=font(48, True), anchor="mm")
20 d.text((450, 365), f"has completed the {course} course with grade {grade}", fill="#0b1f3a", font=font(24), anchor="mm")
21 d.text((450, 470), "Director 08 October 2026", fill="#5b6577", font=font(20), anchor="mm")
22 img.save(filename)
23
24import csv
25from pathlib import Path
26
27out = Path("certificates")
28out.mkdir(exist_ok=True)
29count = 0
30with open("batch.csv", encoding="utf-8") as f:
31 for row in csv.DictReader(f):
32 path = out / f"roll_{int(row['roll']):02d}.png"
33 certificate(row["name"], row["course"], row["grade"], path)
34 count += 1
35 print("Saved", path)
36print(count, "certificates ready to print!")
PracticePractice 5 of 5

Photo Renamer + Excel Log

AdvancedScenario. Rename exam-day photos by roll number and record the old and new names in an Excel log for the office.

Your tasks

  1. A dict maps camera file names to roll numbers
  2. Rename each photo to roll_XX.jpg
  3. Write an Excel sheet with columns Old name, New name
  4. Read the sheet back and print it
🏢 Used in real projects: Admission and exam offices rename thousands of photos and keep an audit log in Excel.
practice_21_5.py  PY
1from pathlib import Path
2from openpyxl import Workbook, load_workbook
3
4mapping = {"IMG_20260915_101233.jpg": 7, "IMG_20260915_101245.jpg": 12, "IMG_20260916_083010.jpg": 3}
5folder = Path("photos")
6wb = Workbook()
7ws = wb.active
8ws.append(["Old name", "New name"])
9for old, roll in mapping.items():
10 src = folder / old
11 if src.exists():
12 dst = folder / f"roll_{roll:02d}.jpg"
13 src.rename(dst)
14 ws.append([old, dst.name])
15wb.save("rename_log.xlsx")
16
17for row in load_workbook("rename_log.xlsx").active.iter_rows(values_only=True):
18 print(f"{row[0]:<26} {row[1]}")
19print("Folder now:", sorted(p.name for p in folder.iterdir()))
Module 21Recap

Module 21 Recap & Quiz

What you learned

  • 1Tkinter: tk.Tk(), widgets (Label, Entry, Button), mainloop()
  • 2Place widgets with pack() or grid(); do not mix in one container
  • 3Events: command=function, bind("<Return>", ...), after(ms, f)
  • 4Read input with entry.get(); update with label.config(text=...)
  • 5openpyxl writes and styles Excel files; Pillow draws images; pathlib renames files
  • 6Automate safely: test on copies, print the plan first, never hard-code passwords

Quick quiz

Which call keeps a Tkinter window open?

mainloop() runs the event loop.

How do you read text typed in an Entry e?

Entry.get() returns the string.

Which library edits .xlsx files?

openpyxl reads and writes Excel workbooks.
MODULE 22

Professional Python: Tools & Habits

⏱ 3.5 hours5 practice examplesStep-through animationsPython Lab

You will learn

  • Virtual environments and requirements.txt
  • Project structure and PEP 8 style
  • Type hints and checking them with mypy
  • Logging instead of print
  • Automated testing with pytest, and Git basics

You will build

  • A typed, tested GST module
  • A bank app with a log file
  • A clean project folder ready for GitHub
pytestdef f(x: int) -> str:git commit -m
Module 22Real-world use

Where Is This Used in Real Projects?

pro habitscode others can trust👥Team projectsreadable code🌐Open sourceGitHub✅CI pipelinestests on every push🔎Code reviewstyle & types🖥Serverslogs, venvs💼Interviewsclean code wins

Real projects that use this

  • Companies expect tests, logs and clean structure, not just working code
  • Every serious project has a requirements.txt and runs in a virtual environment
  • GitHub profiles with tidy, tested projects help freshers from Tier 2/3 cities get noticed
  • These habits separate "it works on my laptop" from software others can trust
Module 22Virtual environments

venv: A Separate Box of Packages for Each Project

Command Prompt (Windows)
C:\python-practice> python main.py
D:\projects\fee_app> python -m venv .venv
D:\projects\fee_app> .venv\Scripts\activate
(.venv) D:\projects\fee_app> pip install flask openpyxl
Successfully installed blinker-1.9.0 click-8.1.8 flask-3.1.0 itsdangerous-2.2.0 jinja2-3.1.5 openpyxl-3.1.5 ...
(.venv) D:\projects\fee_app> pip freeze > requirements.txt
(.venv) D:\projects\fee_app> deactivate
RememberMac / Linux: source .venv/bin/activate. In VS Code, choose the interpreter .venv (bottom-right corner).
  1. 1
    python -m venv .venv creates the environment
  2. 2
    Activate it: the prompt shows (.venv)
  3. 3
    pip install now installs only into this project
  4. 4
    pip freeze > requirements.txt records exact versions
  5. 5
    A teammate runs pip install -r requirements.txt to get the same setup
Common mistakeWithout venvs, Project A needs Flask 2 and Project B needs Flask 3, and they break each other.
Module 22Structure & style

Project Structure and PEP 8 Style

fee_app/project folder└─ .venv/virtual environment (not shared)└─ fee_app/your package└─ __init__.pymakes it a package└─ models.pyclasses: Student, Fee└─ db.pydatabase functions└─ utils.pyhelpers└─ tests/automated tests└─ test_utils.py└─ main.pystarts the app└─ requirements.txtlist of packages└─ README.mdhow to run it└─ .gitignorefiles Git should skip

Messy

def CalcFee(a,b,c):
 x=a*b
 if c==True: x=x-500
 return x

PEP 8

def calculate_fee(months, monthly_fee, has_scholarship):
    fee = months * monthly_fee
    if has_scholarship:
        fee -= 500
    return fee
PEP 8 ruleExample
snake_case namestotal_marks
CapWords classesclass FeeReceipt
UPPER constantsGST_RATE = 18
4-space indent, spaces around =x = a + b
lines under ~80-100 chars
RememberLet tools do it: pip install black ruff, then black . formats and ruff check . finds problems.
Module 22Type hints

Type Hints: Say What Types a Function Expects

main.py  PY
1"""gst.py - GST helpers for the billing app."""
2
3RATES: dict[str, float] = {"food": 5, "books": 0, "electronics": 18, "clothes": 12}
4
5
6def gst_amount(price: float, category: str) -> float:
7 """Return the GST on one item, rounded to 2 decimals."""
8 if price < 0:
9 raise ValueError("price cannot be negative")
10 rate = RATES.get(category)
11 if rate is None:
12 raise KeyError(f"unknown category: {category}")
13 return round(price * rate / 100, 2)
14
15
16def bill_total(items: list[tuple[str, float, int]]) -> float:
17 """items = [(category, price, qty), ...]; returns the grand total with GST."""
18 total = 0.0
19 for category, price, qty in items:
20 total += (price + gst_amount(price, category)) * qty
21 return round(total, 2)
22
23
24print(gst_amount(1299, "electronics"))
25print(bill_total([("food", 120, 3), ("clothes", 899, 1)]))
26print(gst_amount.__annotations__)
Terminal
C:\python-practice> python main.py
233.82
1384.88
{'price': <class 'float'>, 'category': <class 'str'>, 'return': <class 'float'>}
def gst_amount(
price: floatparameter type
, category: str
) -> floatreturn type
:
  1. 1
    Hints do not change how Python runs
  2. 2
    Editors (VS Code) use them for autocomplete and warnings
  3. 3
    mypy checks the whole project for type mistakes
In real projectsModern frameworks like FastAPI and Pydantic read type hints to validate data automatically.
Module 22mypy

Catch Bugs Before Running: mypy

main.py  PY
1from mypy import api
2
3open("buggy.py", "w").write('''
4def area(length: float, breadth: float) -> float:
5 return length * breadth
6
7print(area("12", 10))
8total: int = area(3.5, 2)
9''')
10report, errors, status = api.run(["buggy.py", "--no-error-summary"])
11print(report)
Terminal
C:\python-practice> python main.py
buggy.py:5: error: Argument 1 to "area" has incompatible type "str"; expected "float" [arg-type]
buggy.py:6: error: Incompatible types in assignment (expression has type "float", variable has type "int") [assignment]
  1. 1
    pip install mypy
  2. 2
    Run mypy buggy.py in the terminal (here we call it from Python)
  3. 3
    mypy reads the hints and reports mistakes without running the program
RememberLine 4 passes text "12" where a float is expected; line 5 stores a float in an int variable. Both are caught early.
Module 22Logging

logging: Better Than print for Real Apps

main.py  PY
1import logging
2
3logging.basicConfig(
4 filename="bank.log", level=logging.INFO,
5 format="%(asctime)s %(levelname)-7s %(name)s: %(message)s", datefmt="%d-%m-%Y %H:%M",
6)
7log = logging.getLogger("atm")
8
9def withdraw(balance, amount):
10 log.info("withdraw requested: %s", amount)
11 if amount > balance:
12 log.warning("insufficient balance: has %s, asked %s", balance, amount)
13 return balance
14 log.debug("this DEBUG line is hidden at INFO level")
15 return balance - amount
16
17b = withdraw(5000, 1200)
18b = withdraw(b, 9000)
19try:
20 1 / 0
21except ZeroDivisionError:
22 log.exception("unexpected error while printing receipt")
23print("Done. Check bank.log")
Terminal
C:\python-practice> python main.py
Done. Check bank.log
📄 bank.log (file on disk)TXT
108-10-2026 00:09 INFO atm: withdraw requested: 1200
208-10-2026 00:09 INFO atm: withdraw requested: 9000
308-10-2026 00:09 WARNING atm: insufficient balance: has 3800, asked 9000
408-10-2026 00:09 ERROR atm: unexpected error while printing receipt
5Traceback (most recent call last):
6 File "main.py", line 20, in <module>
7 1 / 0
8 ~~^~~
9ZeroDivisionError: division by zero
LevelUse for
DEBUGdetails while developing
INFOnormal events (login, payment ok)
WARNINGsomething odd but handled
ERRORan operation failed
CRITICALthe app cannot continue
In real projectsLogs have time, level and source, can go to files, and can be switched off without deleting lines. Production servers rely on them.
Module 22Testing

Automated Tests with pytest

main.py  PY
1import pytest
2
3# runs the same as typing "pytest -q" in the terminal
4pytest.main(["-q", "-p", "no:cacheprovider", "test_gst.py"])
Terminal
C:\python-practice> python main.py
..... [100%]
5 passed in 0.01s
test_gst.pyPY
1import pytest
2from gst import gst_amount, bill_total
3
4
5def test_electronics_rate():
6 assert gst_amount(1000, "electronics") == 180
7
8
9def test_books_are_exempt():
10 assert gst_amount(450, "books") == 0
11
12
13def test_bill_total():
14 items = [("food", 100, 2), ("electronics", 500, 1)]
15 assert bill_total(items) == 800.0 # 210 + 590
16
17
18def test_negative_price_rejected():
19 with pytest.raises(ValueError):
20 gst_amount(-5, "food")
21
22
23def test_unknown_category():
24 with pytest.raises(KeyError):
25 gst_amount(100, "gold")
26
RememberWrite small functions, then a test for each rule. Run pytest after every change: if something breaks, you know immediately.
Module 22Git & GitHub

Git: Save Versions and Share Your Code

git commandsCMD
1git init # start tracking this folder
2git add . # stage all changes
3git commit -m "Add GST module" # save a version
4git log --oneline # history
5
6git branch feature-receipts # work on something new
7git switch feature-receipts
8
9git remote add origin https://github.com/yourname/fee_app.git
10git push -u origin main # upload to GitHub
.gitignoreTXT
1.venv/
2__pycache__/
3*.log
4.env # passwords and API keys stay private
edit files➜git add➜git commit➜git push➜GitHub
  1. 1
    A commit is a saved snapshot you can return to
  2. 2
    Branches let you try ideas without breaking main
  3. 3
    GitHub stores your code online: portfolio + teamwork
Common mistakeKeep secrets out of code: put them in a .env file (listed in .gitignore) and read them with os.environ.
In real projectsRecruiters look at GitHub profiles. Five clean, tested projects with READMEs make a strong portfolio.
Module 22Hands-on

Live Lab: Try It Yourself

lab.py
Output Ready
Output of the starter code (press Run after you change it):
All tests passed!
8008.24
Your tasks
  1. Add a test that a longer loan gives a smaller EMI
  2. Break the formula on purpose and watch the assert fail
  3. Add type hints to a function from an earlier module
  4. On your computer: create a venv and pip install pytest
PracticePractice 1 of 5

Typed and Documented Utility Module

BeginnerScenario. Turn loose fee calculations into a clean module with type hints, docstrings and constants.

Your tasks

  1. UPPER_CASE constants for rates
  2. Functions with hints: def late_fee(days: int) -> int
  3. Docstrings for each function
  4. A main() function and if __name__ == "__main__":
🏢 Used in real projects: Every professional codebase looks like this: constants, small typed functions, docstrings and a main() entry point.
fees.py  PY
1"""fees.py - fee rules for BrainBit Infotech Pvt Ltd."""
2from datetime import date
3
4MONTHLY_FEE: int = 1500
5LATE_FEE_PER_DAY: int = 20
6SIBLING_DISCOUNT: float = 0.10
7
8
9def late_fee(days_late: int) -> int:
10 """Late fee for paying after the due date."""
11 return max(0, days_late) * LATE_FEE_PER_DAY
12
13
14def fee_due(months: int, has_sibling: bool = False) -> float:
15 """Fee for a number of months, with sibling discount."""
16 fee = months * MONTHLY_FEE
17 return fee * (1 - SIBLING_DISCOUNT) if has_sibling else float(fee)
18
19
20def days_between(start: date, end: date) -> int:
21 """Whole days from start to end."""
22 return (end - start).days
23
24
25def main() -> None:
26 late = days_between(date(2026, 10, 10), date(2026, 10, 18))
27 total = fee_due(3, has_sibling=True) + late_fee(late)
28 print(f"3 months with sibling discount + {late} days late = Rs {total:,.2f}")
29
30
31if __name__ == "__main__":
32 main()
PracticePractice 2 of 5

Write pytest Tests for a Calculator

EasyScenario. A marks calculator has rules. Write tests that prove the rules work, including the error cases.

Your tasks

  1. calc.py: percentage() and grade()
  2. test_calc.py: one test per rule
  3. Use pytest.raises for invalid input
  4. Use @pytest.mark.parametrize to test many grades at once
test_calc.pyPY
1import pytest
2from calc import percentage, grade
3
4def test_percentage():
5 assert percentage(412, 500) == 82.4
6
7@pytest.mark.parametrize("pct, expected", [(95, "A+"), (90, "A+"), (80, "A"), (61, "B"), (33, "C"), (32.9, "F")])
8def test_grade(pct, expected):
9 assert grade(pct) == expected
10
11@pytest.mark.parametrize("o, t", [(10, 0), (-1, 100), (120, 100)])
12def test_invalid(o, t):
13 with pytest.raises(ValueError):
14 percentage(o, t)
🏢 Used in real projects: GitHub Actions and other CI tools run pytest automatically on every push; failing tests block bad code from release.
run_tests.py  PY
1import pytest
2pytest.main(["-q", "-p", "no:cacheprovider", "test_calc.py"])
PracticePractice 3 of 5

Logging in a Bank App

IntermediateScenario. Add logging to an ATM simulation: INFO for normal actions, WARNING for refused withdrawals, ERROR with traceback for crashes.

Your tasks

  1. Configure basicConfig to write to atm.log
  2. Log each transaction with the account number
  3. Use log.exception inside except
  4. Print the log file at the end
🏢 Used in real projects: Banks and payment apps log every transaction; support teams search logs to answer "where did my money go?"
practice_22_3.py  PY
1import logging
2logging.basicConfig(filename="atm.log", level=logging.INFO, format="%(levelname)-7s %(message)s")
3log = logging.getLogger("atm")
4
5accounts = {"XX4521": 8000, "XX7730": 1200}
6
7def withdraw(acc: str, amount: int) -> None:
8 try:
9 bal = accounts[acc]
10 if amount > bal:
11 log.warning("%s refused: balance %s < %s", acc, bal, amount)
12 return
13 accounts[acc] = bal - amount
14 log.info("%s withdrew %s, balance %s", acc, amount, accounts[acc])
15 except KeyError:
16 log.exception("unknown account %s", acc)
17
18for acc, amt in [("XX4521", 2000), ("XX7730", 5000), ("XX0000", 100), ("XX4521", 500)]:
19 withdraw(acc, amt)
20print(open("atm.log").read())
PracticePractice 4 of 5

Refactor Messy Code

ChallengingScenario. A senior gave you working but messy code. Make it clean without changing what it prints: better names, functions, constants, f-strings.

Your tasks

  1. Read the messy version (below) and its output
  2. Extract a grade() function and constants
  3. Use clear names and f-strings
  4. Check that the output is identical
messy.py  PY
1l=[["Aman",78,85],["Riya",25,40],["Om",90,95]]
2for i in range(len(l)):
3 a=(l[i][1]+l[i][2])/2
4 if a>=90:g="A+"
5 elif a>=75:g="A"
6 elif a>=33:g="PASS"
7 else:g="FAIL"
8 print(l[i][0]+" "+str(a)+" "+g)
🏢 Used in real projects: Refactoring is daily work in companies: keep behaviour the same, make code easier to read, test and change.
clean.py  PY
1PASS_MARK = 33
2STUDENTS = [("Aman", 78, 85), ("Riya", 25, 40), ("Om", 90, 95)]
3
4
5def average(*marks: int) -> float:
6 return sum(marks) / len(marks)
7
8
9def grade(avg: float) -> str:
10 if avg >= 90:
11 return "A+"
12 if avg >= 75:
13 return "A"
14 if avg >= PASS_MARK:
15 return "PASS"
16 return "FAIL"
17
18
19for name, maths, science in STUDENTS:
20 avg = average(maths, science)
21 print(f"{name} {avg} {grade(avg)}")
PracticePractice 5 of 5

Scaffold a Project Folder

AdvancedScenario. Write a script that creates a ready-to-use project skeleton (folders, files, README, .gitignore) and prints its tree.

Your tasks

  1. Use pathlib to create folders and files
  2. Write a README with run instructions
  3. Write a .gitignore with .venv and __pycache__
  4. Print the tree recursively
🏢 Used in real projects: Tools like cookiecutter and "django-admin startproject" generate project skeletons exactly like this.
practice_22_5.py  PY
1from pathlib import Path
2
3def scaffold(name: str) -> Path:
4 root = Path(name)
5 files = {
6 f"{name}/__init__.py": "",
7 f"{name}/utils.py": 'def hello() -> str:\n return "hello"\n',
8 "tests/test_utils.py": f"from {name}.utils import hello\n\ndef test_hello():\n assert hello() == 'hello'\n",
9 "main.py": f"from {name}.utils import hello\nprint(hello())\n",
10 "requirements.txt": "pytest\n",
11 "README.md": f"# {name}\n\npython -m venv .venv\npip install -r requirements.txt\npython main.py\n",
12 ".gitignore": ".venv/\n__pycache__/\n*.log\n.env\n",
13 }
14 for rel, text in files.items():
15 p = root / rel
16 p.parent.mkdir(parents=True, exist_ok=True)
17 p.write_text(text)
18 return root
19
20def tree(p: Path, indent: str = "") -> None:
21 for child in sorted(p.iterdir(), key=lambda c: (c.is_file(), c.name)):
22 print(indent + ("|-- " if indent else "") + child.name + ("/" if child.is_dir() else ""))
23 if child.is_dir():
24 tree(child, indent + " ")
25
26project = scaffold("fee_app")
27print("fee_app/")
28tree(project, " ")
Module 22Recap

Module 22 Recap & Quiz

What you learned

  • 1python -m venv .venv + activate; pip freeze > requirements.txt
  • 2Clear structure (package, tests, main.py, README) and PEP 8 style (black, ruff)
  • 3Type hints def f(x: int) -> str; check with mypy
  • 4logging with levels, formats and files instead of print
  • 5pytest: small test_ functions with assert, raises, parametrize
  • 6Git: add, commit, branch, push; keep secrets out of the repo

Quick quiz

Command to create a virtual environment?

The venv module creates it.

pytest finds tests in functions named...

Functions starting with test_ in test_*.py files.

Which should NOT be pushed to GitHub?

Secrets stay out of the repo (.gitignore).
MODULE 23

Capstone Projects

⏱ 10 hours5 practice examplesStep-through animationsPython Lab

You will learn

  • Plan a project: features, files and flow
  • Combine OOP, files, databases, charts and the web
  • Write menu-driven programs that real users can operate
  • Organise code into functions and classes
  • Present projects in a portfolio

You will build

  • Student Result Management System
  • Expense Tracker with charts
  • Quiz Game, Library System and Weather Dashboard
build!git pushpython main.py
Module 23Real-world use

Five Projects for Your Portfolio

your projects5 complete apps🎓Result systemOOP + CSV💰Expense trackerSQLite + charts❓Quiz gameJSON + scores📚Library systemOOP + SQL + dates🌦Weather siteAPI + Flask💼PortfolioGitHub + README

Real projects that use this

  • Each capstone uses skills from several modules, just like real software
  • All five are useful in schools, offices and homes in Ranchi and Jharkhand
  • Put each project on GitHub with a README, screenshots and tests
  • In interviews, explain why you designed it this way: that is what impresses
CapstoneCapstone 1 of 5

Project 1: Student Result Management System

Goal. A command-line app for class teachers to add students, enter marks, view rank lists and export a report CSV.

Features

  • Load students from students.csv at start
  • Add a student with marks (validated input)
  • Rank list with percentage and grade
  • Topper and subject averages
  • Export report.csv and save on exit

How it flows

load CSV➜menu loop➜Student objects➜save CSV

Project files

FilePurpose
main.pymenu and program flow
students.csvsaved data
report.csvexported results

Skills used

classescsv modulefunctionstry/exceptsorted + keyf-strings
RememberBuild it step by step: get one feature working, test it, commit it to Git, then add the next.
PracticePractice 1 of 5

Result Management System (full code)

IntermediateScenario. Run the complete app. The demo adds one student, shows the rank list and the topper, then exports the report.

Your tasks

  1. Read the Student class and its methods
  2. Follow the menu loop and each choice
  3. Run it in the Lab and try your own inputs
  4. Extension: add "delete student" and "search by name"
🏢 Used in real projects: School ERPs and coaching-centre software are bigger versions of this exact design.
result_system.py  PY
1import csv
2
3SUBJECTS = ["maths", "science", "english"]
4
5class Student:
6 def __init__(self, roll, name, marks):
7 self.roll, self.name, self.marks = int(roll), name, {s: int(marks[s]) for s in SUBJECTS}
8 def percent(self):
9 return sum(self.marks.values()) / len(SUBJECTS)
10 def grade(self):
11 p = self.percent()
12 return "A+" if p >= 90 else "A" if p >= 75 else "B" if p >= 60 else "C" if p >= 33 else "F"
13
14def load(path="students.csv"):
15 with open(path, encoding="utf-8") as f:
16 return [Student(r["roll"], r["name"], r) for r in csv.DictReader(f)]
17
18def save(students, path="students.csv"):
19 with open(path, "w", newline="", encoding="utf-8") as f:
20 w = csv.writer(f)
21 w.writerow(["roll", "name"] + SUBJECTS)
22 for s in students:
23 w.writerow([s.roll, s.name] + [s.marks[x] for x in SUBJECTS])
24
25def ask_mark(subject):
26 while True:
27 try:
28 m = int(input(f" {subject} marks: "))
29 if 0 <= m <= 100:
30 return m
31 except ValueError:
32 pass
33 print(" enter 0-100")
34
35students = load()
36print(f"Loaded {len(students)} students")
37while True:
38 ch = input("\n1 Add 2 Rank list 3 Topper 4 Export & exit: ")
39 if ch == "1":
40 name = input(" Name: ").strip().title()
41 roll = max(s.roll for s in students) + 1
42 students.append(Student(roll, name, {s: ask_mark(s) for s in SUBJECTS}))
43 print(f" Added roll {roll}")
44 elif ch == "2":
45 for i, s in enumerate(sorted(students, key=lambda s: -s.percent()), 1):
46 print(f" {i}. {s.name:<13} {s.percent():5.1f}% {s.grade()}")
47 elif ch == "3":
48 t = max(students, key=lambda s: s.percent())
49 print(f" Topper: {t.name} ({t.percent():.1f}%)")
50 print(" Subject averages:", {x: round(sum(s.marks[x] for s in students) / len(students), 1) for x in SUBJECTS})
51 elif ch == "4":
52 save(students)
53 with open("report.csv", "w", newline="") as f:
54 w = csv.writer(f)
55 w.writerow(["roll", "name", "percent", "grade"])
56 for s in students:
57 w.writerow([s.roll, s.name, round(s.percent(), 1), s.grade()])
58 print(" Saved students.csv and report.csv. Bye!")
59 break
CapstoneCapstone 2 of 5

Project 2: Expense Tracker with Charts

Goal. A personal finance tool that stores expenses in SQLite and shows monthly and category summaries with a chart.

Features

  • Add expenses with date, category and amount
  • Store everything in expenses.db
  • Monthly totals with GROUP BY
  • Category pie chart saved as an image
  • Budget alert when a category crosses its limit

How it flows

add expense➜SQLite➜SQL summaries➜chart PNG

Project files

FilePurpose
tracker.pydatabase + menu
expenses.dbSQLite database
report.pngchart for the month

Skills used

sqlite3datetimematplotlibfunctionsdictsGROUP BY
RememberBuild it step by step: get one feature working, test it, commit it to Git, then add the next.
PracticePractice 2 of 5

Expense Tracker (full code)

ChallengingScenario. The demo loads some sample expenses, adds two from the keyboard, prints the summaries and draws the chart.

Your tasks

  1. Study add(), month_summary() and budget_alerts()
  2. See how SQL does the grouping
  3. Run it in the Lab; matplotlib draws the chart below the output
  4. Extension: export the month to Excel with openpyxl
🏢 Used in real projects: Apps like Money Manager and bank "spend analysis" screens do exactly this: store, group, chart, alert.
tracker.py  PY
1import sqlite3
2import matplotlib.pyplot as plt
3
4con = sqlite3.connect("expenses.db")
5con.execute("DROP TABLE IF EXISTS expenses")
6con.execute("CREATE TABLE expenses (id INTEGER PRIMARY KEY, day TEXT, category TEXT, amount REAL)")
7BUDGET = {"Food": 5000, "Transport": 1500, "Shopping": 2000}
8
9def add(day, category, amount):
10 with con:
11 con.execute("INSERT INTO expenses (day, category, amount) VALUES (?, ?, ?)", (day, category.title(), amount))
12
13def month_summary(month):
14 return con.execute("SELECT category, SUM(amount) FROM expenses WHERE day LIKE ? GROUP BY category ORDER BY 2 DESC", (month + "%",)).fetchall()
15
16def budget_alerts(summary):
17 for cat, total in summary:
18 if cat in BUDGET and total > BUDGET[cat]:
19 print(f" ALERT: {cat} Rs {total:,.0f} is over budget Rs {BUDGET[cat]:,}")
20
21for row in [("2026-10-01", "Food", 1450), ("2026-10-02", "Transport", 300), ("2026-10-03", "Bills", 1180), ("2026-10-04", "Food", 2600),
22 ("2026-10-05", "Shopping", 1899), ("2026-10-06", "Transport", 650), ("2026-10-07", "Food", 1200)]:
23 add(*row)
24for _ in range(2):
25 add("2026-10-08", input("Category: "), float(input("Amount: ")))
26
27summary = month_summary("2026-10")
28print("October 2026")
29for cat, total in summary:
30 print(f" {cat:<10} Rs {total:>8,.2f}")
31print(f" {'TOTAL':<10} Rs {sum(t for _, t in summary):>8,.2f}")
32budget_alerts(summary)
33
34plt.figure(figsize=(5, 3.2))
35plt.pie([t for _, t in summary], labels=[c for c, _ in summary], autopct="%1.0f%%", startangle=90)
36plt.title("October spending")
37plt.tight_layout()
38plt.savefig("report.png", dpi=80)
CapstoneCapstone 3 of 5

Project 3: Quiz Game with High Scores

Goal. A quiz app that loads questions from JSON, shuffles them, scores answers, and remembers the high score.

Features

  • Questions in questions.json (easy to add more)
  • Shuffle questions and options
  • Accept answers by option number
  • Show score, percentage and corrections
  • Save the best score to highscore.json

How it flows

load JSON➜shuffle➜ask & score➜save high score

Project files

FilePurpose
quiz.pygame logic
questions.jsonquestion bank
highscore.jsonbest score so far

Skills used

jsonrandomloopsfunctionstry/exceptfiles
RememberBuild it step by step: get one feature working, test it, commit it to Git, then add the next.
PracticePractice 3 of 5

Quiz Game (full code)

IntermediateScenario. The demo answers 5 questions (one wrong). The high score file is created or updated.

Your tasks

  1. Follow load_questions(), ask() and update_high_score()
  2. Notice how bad input (letters) is handled
  3. Add 5 questions of your own to the JSON file
  4. Extension: a Tkinter version with buttons for options
🏢 Used in real projects: Online test platforms, school quiz apps and trivia games store question banks as JSON or in databases.
quiz.py  PY
1import json, random, os
2
3def load_questions(path="questions.json"):
4 with open(path, encoding="utf-8") as f:
5 qs = json.load(f)
6 random.shuffle(qs)
7 return qs
8
9def ask(n, item):
10 opts = item["options"][:]
11 random.shuffle(opts)
12 print(f"\nQ{n}. {item['q']}")
13 for i, o in enumerate(opts, 1):
14 print(f" {i}) {o}")
15 while True:
16 try:
17 pick = opts[int(input(" Your answer: ")) - 1]
18 break
19 except (ValueError, IndexError):
20 print(" Type an option number")
21 ok = pick == item["answer"]
22 print(" Correct!" if ok else f" Wrong, answer: {item['answer']}")
23 return ok
24
25def update_high_score(score, path="highscore.json"):
26 best = json.load(open(path))["best"] if os.path.exists(path) else 0
27 if score > best:
28 json.dump({"best": score}, open(path, "w"))
29 print(f"NEW HIGH SCORE: {score}!")
30 else:
31 print(f"High score is still {best}")
32
33questions = load_questions()
34score = sum(ask(i, q) for i, q in enumerate(questions, 1))
35print(f"\nScore: {score}/{len(questions)} ({score / len(questions):.0%})")
36update_high_score(score)
CapstoneCapstone 4 of 5

Project 4: Library Management System

Goal. A school library system with members, books, issue/return, due dates and late fines.

Features

  • Books and members in SQLite tables
  • Issue a book for 14 days (max 2 per member)
  • Return with automatic late fine (Rs 2/day)
  • Reports: issued books, overdue list, popular books
  • Classes wrap all database work

How it flows

Library()➜issue / return➜SQL tables➜reports

Project files

FilePurpose
library.pyLibrary class + demo
library.dbSQLite database

Skills used

classessqlite3JOINdatetimetimedeltaexceptions
RememberBuild it step by step: get one feature working, test it, commit it to Git, then add the next.
PracticePractice 4 of 5

Library Management (full code)

AdvancedScenario. The demo issues books on different dates, refuses a third book, returns one late and prints the reports.

Your tasks

  1. Read the Library class methods
  2. See the custom LibraryError for rule breaks
  3. Check the fine calculation with dates
  4. Extension: add a Flask page that lists available books
🏢 Used in real projects: Library software (Koha, school ERPs) is built on the same tables, rules and fine calculations.
library.py  PY
1import sqlite3
2from datetime import date, timedelta
3
4class LibraryError(Exception):
5 pass
6
7class Library:
8 LOAN_DAYS, MAX_BOOKS, FINE_PER_DAY = 14, 2, 2
9
10 def __init__(self, path=":memory:"):
11 self.db = sqlite3.connect(path)
12 self.db.executescript("""
13 CREATE TABLE books (id INTEGER PRIMARY KEY, title TEXT, author TEXT);
14 CREATE TABLE loans (book_id INT, member TEXT, issued TEXT, due TEXT, returned TEXT, fine INT DEFAULT 0);""")
15
16 def add_book(self, title, author):
17 self.db.execute("INSERT INTO books (title, author) VALUES (?, ?)", (title, author))
18
19 def _open_loans(self, where, arg):
20 return self.db.execute(f"SELECT COUNT(*) FROM loans WHERE {where} = ? AND returned IS NULL", (arg,)).fetchone()[0]
21
22 def issue(self, book_id, member, on):
23 if self._open_loans("book_id", book_id):
24 raise LibraryError(f"book {book_id} is already issued")
25 if self._open_loans("member", member) >= self.MAX_BOOKS:
26 raise LibraryError(f"{member} already has {self.MAX_BOOKS} books")
27 due = on + timedelta(days=self.LOAN_DAYS)
28 self.db.execute("INSERT INTO loans (book_id, member, issued, due) VALUES (?, ?, ?, ?)", (book_id, member, on.isoformat(), due.isoformat()))
29 print(f"Issued book {book_id} to {member}, due {due:%d %b}")
30
31 def give_back(self, book_id, on):
32 due = date.fromisoformat(self.db.execute("SELECT due FROM loans WHERE book_id = ? AND returned IS NULL", (book_id,)).fetchone()[0])
33 fine = max(0, (on - due).days) * self.FINE_PER_DAY
34 self.db.execute("UPDATE loans SET returned = ?, fine = ? WHERE book_id = ? AND returned IS NULL", (on.isoformat(), fine, book_id))
35 print(f"Book {book_id} returned on {on:%d %b}. Fine: Rs {fine}")
36
37 def overdue(self, today):
38 q = "SELECT b.title, l.member, l.due FROM loans l JOIN books b ON b.id = l.book_id WHERE l.returned IS NULL AND l.due < ?"
39 return self.db.execute(q, (today.isoformat(),)).fetchall()
40
41lib = Library()
42for t, a in [("Wings of Fire", "A.P.J. Abdul Kalam"), ("Godan", "Premchand"), ("Malgudi Days", "R.K. Narayan"), ("The Discovery of India", "Jawaharlal Nehru")]:
43 lib.add_book(t, a)
44lib.issue(1, "Asha", date(2026, 9, 10))
45lib.issue(2, "Asha", date(2026, 9, 20))
46for action in [lambda: lib.issue(3, "Asha", date(2026, 9, 21)), lambda: lib.issue(1, "Vikas", date(2026, 9, 22))]:
47 try:
48 action()
49 except LibraryError as e:
50 print("Refused:", e)
51lib.issue(3, "Vikas", date(2026, 10, 1))
52lib.give_back(1, date(2026, 10, 2))
53print("Overdue on 08 Oct:", lib.overdue(date(2026, 10, 8)))
54print("Fines collected: Rs", lib.db.execute("SELECT SUM(fine) FROM loans").fetchone()[0])
CapstoneCapstone 5 of 5

Project 5: Jharkhand Weather Dashboard (API + Flask)

Goal. A small website that shows current weather and a 3-day forecast for Jharkhand cities using a free weather API.

Features

  • City drop-down (query string ?city=)
  • Calls the Open-Meteo API with requests
  • Template shows a table with a rain icon
  • Friendly error page if the API fails
  • Ready to deploy on a free hosting service

How it flows

browser➜Flask route➜weather API➜template➜HTML page

Project files

FilePurpose
app.pyFlask routes + API call
templates/HTML pages (inline here)
requirements.txtflask, requests

Skills used

FlaskrequestsJSONJinja2 templatestry/exceptdicts
RememberBuild it step by step: get one feature working, test it, commit it to Git, then add the next.
PracticePractice 5 of 5

Weather Dashboard (full code)

AdvancedScenario. Run python app.py and open http://127.0.0.1:5000. The previews show the pages for Ranchi and Netarhat (sample API data).

Your tasks

  1. Follow the route: read city, call the API, render the template
  2. See how errors show a friendly message
  3. Run on your computer: pip install flask requests
  4. Extension: add a chart of the 3-day temperatures
🏢 Used in real projects: Real dashboards (weather, cricket, stock prices) are Flask/Django apps that call APIs and render templates.
app.pyPY
1import requests
2from flask import Flask, request, render_template_string
3
4app = Flask(__name__)
5CITIES = {"ranchi": (23.34, 85.31), "jamshedpur": (22.80, 86.18), "dhanbad": (23.80, 86.43), "netarhat": (23.47, 84.27)}
6PAGE = """<h2>Jharkhand Weather Dashboard</h2>
7<form><select name="city">{% for c in cities %}<option {{ "selected" if c == city }}>{{ c }}</option>{% endfor %}</select>
8<button>Show</button></form>
9{% if w %}<h3>{{ city.title() }}: {{ w.current.temperature_2m }} &deg;C, humidity {{ w.current.relative_humidity_2m }}%</h3>
10<table border=1 cellpadding=6><tr><th>Day</th><th>Min</th><th>Max</th><th>Rain (mm)</th></tr>
11{% for d, lo, hi, r in days %}<tr><td>{{ d }}</td><td>{{ lo }}</td><td>{{ hi }}</td><td>{{ r }}{{ " &#9748;" if r > 1 }}</td></tr>{% endfor %}</table>
12{% else %}<p style="color:red">{{ error }}</p>{% endif %}"""
13
14def get_weather(city):
15 lat, lon = CITIES[city]
16 r = requests.get("https://api.open-meteo.com/v1/forecast", timeout=10, params={
17 "latitude": lat, "longitude": lon, "current": "temperature_2m,relative_humidity_2m",
18 "daily": "temperature_2m_max,temperature_2m_min,precipitation_sum", "timezone": "Asia/Kolkata"})
19 r.raise_for_status()
20 return r.json()
21
22@app.route("/")
23def home():
24 city = request.args.get("city", "ranchi")
25 try:
26 w = get_weather(city)
27 d = w["daily"]
28 days = zip(d["time"], d["temperature_2m_min"], d["temperature_2m_max"], d["precipitation_sum"])
29 return render_template_string(PAGE, cities=CITIES, city=city, w=w, days=days)
30 except (KeyError, requests.exceptions.RequestException) as e:
31 return render_template_string(PAGE, cities=CITIES, city=city, w=None, error=f"Could not load weather: {e}")
32
33if __name__ == "__main__":
34 app.run(debug=True)
Module 23What next?

Where Python Can Take You

Data Analyst

pandas, SQL, Excel, charts
Builds on: M17-M19

Web Developer

Flask / Django, HTML/CSS/JS, SQL
Builds on: M18, M20 + JS course

Automation Engineer

scripts, Excel, PDFs, APIs, testing
Builds on: M12, M17, M21, M22

AI / ML Engineer

NumPy, pandas, scikit-learn, PyTorch
Builds on: M19 + maths

Python Developer

OOP, testing, Git, databases
Builds on: M14-M16, M22
  1. 1
    Finish all 5 capstones and push them to GitHub with READMEs
  2. 2
    Pick one path and build 2 more projects in it
  3. 3
    Practise problems daily (HackerRank, LeetCode easy, Codewars)
  4. 4
    Learn Git, SQL and English communication well
  5. 5
    Apply for internships; freelance small automation jobs
In real projectsMany companies hire remotely now. A strong GitHub portfolio from Ranchi competes with anyone, anywhere.
Module 23Course complete

Congratulations, Python Programmer!

BRAINBIT INFOTECH PVT LTD · RANCHI
Certificate of Completion
This is to certify that
Your Name
has successfully completed the Complete Python Course (Beginner to Advanced)
23 modules · 110 practice examples · 5 capstone projects
Head of Training🐍Date: ____________
Name on certificate:
Module 23Recap

Module 23 Recap & Quiz

What you learned

  • 1Plan first: features, files, flow, then build one feature at a time
  • 2Result system: OOP + CSV + validated menus
  • 3Expense tracker: SQLite + GROUP BY + matplotlib
  • 4Quiz game: JSON + random + high-score file
  • 5Library: classes + SQL JOIN + date fines; Weather: API + Flask + templates
  • 6Publish on GitHub with README, screenshots and tests

Quick quiz

Best first step for a new project?

Planning saves hours of rewriting.

Where should the quiz questions live?

Data in files can change without editing code.

Which capstone uses an API?

It calls a weather API from Flask.
PART 5 COMPLETE

Well done!

You finished Pro Skills & Capstone Projects: 3 modules and 15 practice examples.

Before moving on

  • Re-type every practice example yourself without looking
  • Save your work in D:\python-practice\part5
  • Try the quizzes again until you get all of them right
  • Change one thing in each example and predict the output before you run it
print("Hi") >>> _
🐍 Python LabReady
Edit the code, then press Run. input() opens a small box to type your answer.
Terminal