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

Advanced Python & Data

Power tools from the standard library, SQLite databases, data analysis with charts, and web apps with APIs and Flask.

4 modules~17 hours20 practice examplesNeeds Parts 1-3

By the end of this part you can build

  • A file organiser and a vote counter
  • A student database with SQL
  • A sales dashboard with Pandas and charts
  • A Flask website and a JSON API

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

ROADMAP FOR THIS PART

Module 17Standard Library Power Tools4 h
Module 18Databases with SQLite4 h
Module 19Data Analysis: NumPy, Pandas & Matplotlib5 h
Module 20APIs, the Web & Flask4 h
print("Hi") >>> _
PythonBefore we begin

How to Use This Part

Suggested teaching hours per module (Part 4)0244 hM174 hM185 hM194 hM20

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 17

Standard Library Power Tools

⏱ 4 hours5 practice examplesStep-through animationsPython Lab

You will learn

  • datetime: dates, times, durations and formats
  • pathlib, os and shutil: work with folders and files
  • collections: Counter, defaultdict, namedtuple, deque
  • re: regular expressions for patterns
  • Combining tools to automate real tasks

You will build

  • A fee due-date reminder
  • A Downloads folder organiser
  • A form validator with regex
stdlibCounter(votes)re.findall()
Module 17Real-world use

Where Is This Used in Real Projects?

stdlibbatteries included📅Remindersdue dates, fines📁File organisersort by type🗳Vote countingCounter✅Form checksregex for email, phone📜Log reportsfind errors fast🔄Backupscopy & zip files

Real projects that use this

  • Office automation scripts organise thousands of files using pathlib and shutil
  • Library software calculates late fines with datetime differences
  • Sign-up forms validate mobile numbers, emails and PIN codes with regular expressions
  • All of this is built in: no pip install needed
Module 17datetime

datetime: Parse, Format and Calculate with Dates

main.py  PY
1from datetime import datetime, date, timedelta
2
3issued = datetime.strptime("15-09-2026", "%d-%m-%Y").date()
4due = issued + timedelta(days=14)
5returned = date(2026, 10, 8)
6late_days = (returned - due).days
7
8print("Issued :", issued.strftime("%d %b %Y (%a)"))
9print("Due :", due.strftime("%d %b %Y (%a)"))
10print("Returned:", returned.strftime("%d %b %Y (%a)"))
11print("Late by :", max(0, late_days), "days, fine Rs", max(0, late_days) * 2)
12
13start = datetime(2026, 10, 8, 9, 45)
14end = datetime(2026, 10, 8, 13, 10)
15print("Exam duration:", end - start)
16print("Is due before Diwali?", due < date(2026, 11, 8))
Terminal
C:\python-practice> python main.py
Issued : 15 Sep 2026 (Tue)
Due : 29 Sep 2026 (Tue)
Returned: 08 Oct 2026 (Thu)
Late by : 9 days, fine Rs 18
Exam duration: 3:25:00
Is due before Diwali? True
NeedUse
text -> datedatetime.strptime(text, fmt)
date -> textd.strftime(fmt)
add / subtract daysd + timedelta(days=7)
difference(d2 - d1).days
compared1 < d2
nowdatetime.now(), date.today()
In real projectsLibrary fines, EMI due dates, attendance percentages and subscription expiry all depend on date maths.
Module 17pathlib

pathlib: List, Create and Inspect Files and Folders

main.py  PY
1from pathlib import Path
2
3downloads = Path("Downloads")
4files = sorted(p for p in downloads.iterdir() if p.is_file())
5print(len(files), "files in", downloads)
6for p in files[:4]:
7 print(f" {p.name:<20} stem={p.stem:<16} type={p.suffix}")
8
9pdfs = list(downloads.glob("*.pdf"))
10print("PDFs:", [p.name for p in pdfs])
11
12report = Path("Reports") / "2026" / "october"
13report.mkdir(parents=True, exist_ok=True)
14print("Created:", report, report.exists())
Terminal
C:\python-practice> python main.py
10 files in Downloads
admit_card.pdf stem=admit_card type=.pdf
fee_receipt.pdf stem=fee_receipt type=.pdf
marks_term1.xlsx stem=marks_term1 type=.xlsx
marks_term2.xlsx stem=marks_term2 type=.xlsx
PDFs: ['fee_receipt.pdf', 'admit_card.pdf']
Created: Reports/2026/october True
CodeGives
p.name"photo.jpg"
p.stem"photo"
p.suffix".jpg"
p.parentthe folder
p.iterdir()items in a folder
p.glob("*.pdf")matching files
p.mkdir(...)make folders
RememberPath("a") / "b" / "c.txt" joins paths correctly on Windows, Mac and Linux.
Module 17shutil

shutil: Copy, Move and Back Up

main.py  PY
1import shutil
2from pathlib import Path
3
4src = Path("project")
5src.mkdir(exist_ok=True)
6(src / "main.py").write_text("print('hi')")
7(src / "data.csv").write_text("a,b\n1,2")
8
9shutil.copy(src / "main.py", src / "main_backup.py") # copy one file
10shutil.copytree(src, "project_copy", dirs_exist_ok=True) # copy a folder
11shutil.make_archive("project_backup", "zip", src) # zip it
12
13print(sorted(p.name for p in src.iterdir()))
14print(sorted(p.name for p in Path("project_copy").iterdir()))
15print("Zip created:", Path("project_backup.zip").exists())
Terminal
C:\python-practice> python main.py
['data.csv', 'main.py', 'main_backup.py']
['data.csv', 'main.py', 'main_backup.py']
Zip created: True
FunctionDoes
shutil.copy(a, b)copy a file
shutil.move(a, b)move / rename
shutil.copytree(a, b)copy a whole folder
shutil.rmtree(p)delete a folder (careful!)
shutil.make_archive()create a .zip
Common mistakeshutil.rmtree and os.remove delete permanently, with no Recycle Bin. Test on copies first.
Module 17collections

collections: Counter, defaultdict, namedtuple, deque

main.py  PY
1from collections import Counter, defaultdict, namedtuple, deque
2
3votes = ["Sarhul", "Karma", "Diwali", "Karma", "Karma", "Sarhul", "Chhath", "Karma"]
4c = Counter(votes)
5print(c.most_common(2))
6
7by_city = defaultdict(list)
8for name, city in [("Aman", "Ranchi"), ("Riya", "Dhanbad"), ("Om", "Ranchi")]:
9 by_city[city].append(name) # no KeyError, list made automatically
10print(dict(by_city))
11
12Point = namedtuple("Point", "lat lon")
13ranchi = Point(23.34, 85.31)
14print(ranchi.lat, ranchi)
15
16recent = deque(maxlen=3) # keeps only the last 3
17for page in ["home", "results", "fees", "contact"]:
18 recent.append(page)
19print(list(recent))
Terminal
C:\python-practice> python main.py
[('Karma', 4), ('Sarhul', 2)]
{'Ranchi': ['Aman', 'Om'], 'Dhanbad': ['Riya']}
23.34 Point(lat=23.34, lon=85.31)
['results', 'fees', 'contact']
ToolBest for
Countercounting things, top-N
defaultdict(list)grouping without checking keys
namedtuplesmall records with field names
deque(maxlen=n)recent history, queues
RememberA favourite-festival poll. Counter replaces the manual counting loop from Module 9 with one line.
Module 17Regex

Regular Expressions: Find Patterns in Text

pattern r" "

Type your own pattern. Yellow = matches. (The browser uses JavaScript regex, which matches Python for these common patterns.)

SymbolMeansExample
\da digit\d\d = 42
\wletter, digit or _
\sspace
.any character
[6-9]one of 6,7,8,9
{10}exactly 10 times\d{10}
+ / * / ?1+ / 0+ / 0 or 1
^ $start / end
\bword boundary
( )group to extract
Module 17re module

Using re in Python: search, findall, fullmatch, sub

main.py  PY
1import re
2
3text = "Order 1043 by aman@gmail.com, call 9835011111. Order 1051 by riya@yahoo.in"
4
5print(re.findall(r"\d{4}", text)) # all 4-digit numbers
6print(re.findall(r"[\w.]+@[\w.]+", text)) # emails
7m = re.search(r"call (\d{10})", text)
8print("Phone:", m.group(1))
9
10for mobile in ["9835011111", "1234567890", "98350"]:
11 ok = re.fullmatch(r"[6-9]\d{9}", mobile) is not None
12 print(mobile, "valid" if ok else "invalid")
13
14print(re.sub(r"\d(?=\d{4})", "X", "9835011111")) # mask all but last 4
Terminal
C:\python-practice> python main.py
['1043', '9835', '0111', '1051']
['aman@gmail.com', 'riya@yahoo.in']
Phone: 9835011111
9835011111 valid
1234567890 invalid
98350 invalid
XXXXXX1111
FunctionReturns
re.search(p, s)first match (or None)
re.findall(p, s)list of all matches
re.fullmatch(p, s)match only if the WHOLE string fits
re.sub(p, new, s)replace matches
m.group(1)text inside the first ( )
RememberAlways write patterns as raw strings r"..." so backslashes stay as they are.
Module 17Hands-on

Live Lab: Try It Yourself

lab.py
Output Ready
Output of the starter code (press Run after you change it):
[('Ranchi', 3), ('Dhanbad', 2)]
Revision starts: 2026-12-17
['9876543210', '7004022222']
Your tasks
  1. Count letters (not words) in "jharkhand" with Counter
  2. Print the weekday of 26 January 2027
  3. Write a regex that finds all 6-digit PIN codes in a text
  4. Use re.sub to replace every digit in a phone number with "*"
PracticePractice 1 of 5

Fee Due-Date Reminder

BeginnerScenario. A coaching centre sends reminders: fee is due 10 days after the joining date; late fee is Rs 50 per day after that.

Your tasks

  1. Store students with joining dates as text
  2. Convert with strptime and add timedelta(days=10)
  3. Compare with today to find days left or days late
  4. Print reminder messages
🏢 Used in real projects: School and coaching ERPs, EMI apps and subscription services send due-date reminders computed this way.
practice_17_1.py  PY
1from datetime import datetime, date, timedelta
2
3today = date(2026, 10, 8)
4students = {"Aman": "20-09-2026", "Priya": "01-10-2026", "Rahul": "27-09-2026", "Sneha": "05-10-2026"}
5
6for name, joined in students.items():
7 due = datetime.strptime(joined, "%d-%m-%Y").date() + timedelta(days=10)
8 diff = (due - today).days
9 if diff > 0:
10 msg = f"due in {diff} day(s) on {due:%d %b}"
11 elif diff == 0:
12 msg = "due TODAY"
13 else:
14 msg = f"LATE by {-diff} days, late fee Rs {-diff * 50}"
15 print(f"{name:<6} {msg}")
PracticePractice 2 of 5

Downloads Folder Organiser

EasyScenario. A messy Downloads folder has PDFs, photos, Excel files and more. Sort every file into a sub-folder by type.

Your tasks

  1. Map extensions to folder names with a dict
  2. Loop through Path("Downloads").iterdir()
  3. Create the folder and shutil.move the file
  4. Print a summary with Counter
🏢 Used in real projects: Office automation scripts and tools like Hazel / File Juggler organise thousands of files automatically.
practice_17_2.py  PY
1from pathlib import Path
2from collections import Counter
3import shutil
4
5TYPES = {".pdf": "Documents", ".txt": "Documents", ".jpg": "Images", ".png": "Images",
6 ".xlsx": "Spreadsheets", ".mp3": "Music", ".zip": "Archives"}
7folder = Path("Downloads")
8moved = Counter()
9for f in list(folder.iterdir()):
10 if f.is_file():
11 target = folder / TYPES.get(f.suffix.lower(), "Others")
12 target.mkdir(exist_ok=True)
13 shutil.move(str(f), str(target / f.name))
14 moved[target.name] += 1
15
16for sub in sorted(p for p in folder.iterdir() if p.is_dir()):
17 print(f"{sub.name:<13}", sorted(x.name for x in sub.iterdir()))
18print("Moved:", dict(moved))
PracticePractice 3 of 5

School Captain Election Counter

IntermediateScenario. Votes from 3 classes are collected as lists. Count the totals, show the winner, and the class-wise breakdown.

Your tasks

  1. Use Counter for each class and add them together
  2. Use most_common() to rank candidates
  3. Use defaultdict(dict) for class-wise counts
  4. Print a bar for each candidate
🏢 Used in real projects: Polling apps, surveys and election result dashboards count and rank with Counter-like logic.
practice_17_3.py  PY
1from collections import Counter, defaultdict
2
3ballots = {
4 "9A": ["Anjali", "Rohit", "Anjali", "Imran", "Anjali", "Rohit"],
5 "9B": ["Rohit", "Rohit", "Imran", "Anjali", "Rohit"],
6 "10A": ["Imran", "Anjali", "Imran", "Imran", "Rohit", "Imran", "Anjali"],
7}
8total = Counter()
9breakdown = defaultdict(dict)
10for cls, votes in ballots.items():
11 c = Counter(votes)
12 total += c
13 for cand, n in c.items():
14 breakdown[cand][cls] = n
15
16for rank, (cand, n) in enumerate(total.most_common(), 1):
17 print(f"{rank}. {cand:<7}{'#' * n:<10}{n:>3} {breakdown[cand]}")
18print("Winner:", total.most_common(1)[0][0])
PracticePractice 4 of 5

Admission Form Validator with Regex

ChallengingScenario. Validate admission form fields before saving: name, mobile, email, PIN code and Aadhaar-style 12-digit number (format only).

Your tasks

  1. Make a dict of field -> regex pattern
  2. Use re.fullmatch for each field
  3. Collect errors in a list
  4. Print "Form OK" or the list of errors
🏢 Used in real projects: Every web form (admissions, banking KYC, job portals) validates fields with regular expressions before saving.
practice_17_4.py  PY
1import re
2
3RULES = {
4 "name": (r"[A-Za-z ]{3,40}", "letters only, 3-40 chars"),
5 "mobile": (r"[6-9]\d{9}", "10 digits starting 6-9"),
6 "email": (r"[\w.+-]+@[\w-]+\.[\w.]+", "like name@site.com"),
7 "pin": (r"8[0-5]\d{4}", "Bihar/Jharkhand PIN, 6 digits"),
8 "id_number": (r"\d{4} ?\d{4} ?\d{4}", "12 digits"),
9}
10
11def validate(form):
12 errors = [f"{f}: {msg}" for f, (pat, msg) in RULES.items() if not re.fullmatch(pat, form.get(f, "").strip())]
13 return errors
14
15forms = [
16 {"name": "Riya Kumari", "mobile": "9835011111", "email": "riya@gmail.com", "pin": "834001", "id_number": "1234 5678 9012"},
17 {"name": "R2", "mobile": "5835011111", "email": "riya.gmail.com", "pin": "110001", "id_number": "12345"},
18]
19for i, f in enumerate(forms, 1):
20 errs = validate(f)
21 print(f"Form {i}:", "OK" if not errs else "")
22 for e in errs:
23 print(" x", e)
PracticePractice 5 of 5

Server Log Summary

AdvancedScenario. A school website writes a log. Summarise it: count levels, list failed payments with their error codes, and find the slowest pages.

Your tasks

  1. Read server.log line by line
  2. Use Counter for log levels
  3. Use re.search to extract order numbers, codes and page times
  4. Use defaultdict(list) to group orders by error code
New syntaxif m := re.search(...) uses the "walrus" operator := (Python 3.8+): it stores the match and tests it in one step.
🏢 Used in real projects: DevOps engineers write scripts like this to find errors in production logs.
practice_17_5.py  PY
1import re
2from collections import Counter, defaultdict
3
4levels = Counter()
5failed = defaultdict(list)
6slow = []
7with open("server.log") as f:
8 for line in f:
9 levels[line.split()[2]] += 1
10 if m := re.search(r"order=(\d+) code=(\w+)", line):
11 failed[m.group(2)].append(m.group(1))
12 if m := re.search(r"page=(\S+) time=([\d.]+)s", line):
13 slow.append((float(m.group(2)), m.group(1)))
14
15print("Levels:", dict(levels))
16for code, orders in failed.items():
17 print(f"Payment error {code}: orders {orders}")
18print("Slowest page:", max(slow))
Module 17Recap

Module 17 Recap & Quiz

What you learned

  • 1datetime: strptime (text to date), strftime (date to text), timedelta
  • 2pathlib.Path: iterdir, glob, mkdir, name, suffix; join with /
  • 3shutil: copy, move, copytree, make_archive (delete carefully)
  • 4Counter counts, defaultdict groups, namedtuple names fields, deque keeps recent items
  • 5re: findall, search, fullmatch, sub with raw-string patterns
  • 6Combine them to automate real office tasks

Quick quiz

Which gives the number of days between two dates?

Subtracting dates gives a timedelta.

Counter("banana").most_common(1)?

a appears 3 times.

Regex for exactly 6 digits?

{6} means exactly six times.
MODULE 18

Databases with SQLite

⏱ 4 hours5 practice examplesStep-through animationsPython Lab

You will learn

  • What a database is and why apps use one
  • Tables, rows, columns and primary keys
  • SQL: CREATE, INSERT, SELECT, UPDATE, DELETE
  • sqlite3 in Python with safe ? placeholders
  • WHERE, ORDER BY, LIKE, GROUP BY and JOIN

You will build

  • A student database
  • A shop inventory with low-stock alerts
  • A monthly sales report
SQLSELECT * FROMcon.commit()
Module 18Real-world use

Where Is This Used in Real Projects?

databasestore data properly🎓School ERPstudents, fees📚Librarybooks, issues🏦Bankstransactions🛒Shopsinventory, sales📱Mobile appsAndroid uses SQLite🌐Websitesusers, posts

Real projects that use this

  • Every serious app stores data in a database: IRCTC bookings, bank transactions, Instagram posts
  • SQLite is built into Python and into every Android and iPhone; WhatsApp stores chats on your phone in SQLite
  • Big websites use MySQL or PostgreSQL, but the SQL language you learn here is the same
  • Databases search millions of rows in milliseconds and keep data safe when many users write at once
Module 18The idea

A Database Table Is Like a Smart Spreadsheet

TABLE studentsroll 🔑nameclasscitymarks1Aman9ARanchi782Priya9ADhanbad923Rahul9BRanchi654Sneha9BBokaro88one ROW = one studentone COLUMN = one field🔑 PRIMARY KEY: unique id for each row
CSV fileDatabase
Search 1 million rowsslow (read everything)fast (indexes)
Many users writingfiles get corruptedsafe (transactions)
Relations between tablesmanualJOIN
Rules (unique, not null)noneenforced
RememberSQL (Structured Query Language) is how you talk to databases. It is used with MySQL, PostgreSQL, Oracle, SQL Server and SQLite.
Module 18SQL basics

SQL in 5 Commands (CRUD)

school.sqlSQL
1-- Create a table
2CREATE TABLE students (
3 roll INTEGER PRIMARY KEY,
4 name TEXT NOT NULL,
5 class TEXT, city TEXT, marks INTEGER
6);
7-- Create rows
8INSERT INTO students (name, class, city, marks)
9VALUES ('Aman', '9A', 'Ranchi', 78);
10-- Read
11SELECT name, marks FROM students WHERE city = 'Ranchi';
12-- Update
13UPDATE students SET marks = 80 WHERE roll = 1;
14-- Delete
15DELETE FROM students WHERE roll = 5;
CRUDSQLMeaning
CreateINSERTadd rows
ReadSELECTget rows
UpdateUPDATEchange rows
DeleteDELETEremove rows

Result of SELECT name, marks FROM students WHERE city = 'Ranchi'

namemarks
Aman78
Rahul65
Kiran45
Common mistakeUPDATE or DELETE without WHERE changes every row in the table!
Module 18sqlite3

Python + SQLite: Connect, Create, Insert, Commit

main.py  PY
1import sqlite3
2
3con = sqlite3.connect("school.db") # creates the file if missing
4cur = con.cursor()
5cur.execute("""CREATE TABLE IF NOT EXISTS students (
6 roll INTEGER PRIMARY KEY,
7 name TEXT NOT NULL, class TEXT, city TEXT, marks INTEGER)""")
8
9new = [("Aman", "9A", "Ranchi", 78), ("Priya", "9A", "Dhanbad", 92), ("Rahul", "9B", "Ranchi", 65)]
10cur.executemany("INSERT INTO students (name, class, city, marks) VALUES (?, ?, ?, ?)", new)
11con.commit() # save changes!
12
13for row in cur.execute("SELECT roll, name, marks FROM students"):
14 print(row)
15print("Rows:", cur.execute("SELECT COUNT(*) FROM students").fetchone()[0])
16con.close()
Terminal
C:\python-practice> python main.py
(1, 'Aman', 78)
(2, 'Priya', 92)
(3, 'Rahul', 65)
Rows: 3
📄 school.db (file on disk)TXT
1(SQLite database file, 8192 bytes)
  1. 1
    connect() opens (or creates) the database file
  2. 2
    execute() runs SQL; executemany() runs it for many rows
  3. 3
    commit() saves changes permanently
  4. 4
    Loop over a SELECT or use fetchone() / fetchall()
Common mistakeForgot commit()? Your inserts disappear when the program ends.
Module 18Security

Always Use ? Placeholders (Avoid SQL Injection)

✘ Dangerous

name = input("Name: ")
cur.execute(f"SELECT * FROM users
 WHERE name = '{name}'")


If a user types x' OR '1'='1 the query returns every user!

✔ Safe

cur.execute("SELECT * FROM users
 WHERE name = ?", (name,))


sqlite3 treats the input as data only, never as SQL code.
Common mistakeSQL injection has caused real data leaks worldwide. Never build SQL with f-strings or + from user input.
injection_demo.py  PY
1import sqlite3
2from school_db import make_db
3con = make_db() # school.db with 6 students (helper file)
4
5evil = "x' OR '1'='1"
6bad = con.execute(f"SELECT name FROM students WHERE name = '{evil}'").fetchall()
7safe = con.execute("SELECT name FROM students WHERE name = ?", (evil,)).fetchall()
8print("f-string query returned:", len(bad), "rows!")
9print("? placeholder returned :", len(safe), "rows")
Terminal
C:\python-practice> python injection_demo.py
f-string query returned: 6 rows!
? placeholder returned : 0 rows
Module 18Querying

SELECT with WHERE, ORDER BY, LIMIT and LIKE

main.py  PY
1import sqlite3
2from school_db import make_db
3con = make_db() # school.db with 6 students (helper file)
4
5con.row_factory = sqlite3.Row # rows act like dicts
6
7print("Top 3:")
8for r in con.execute("SELECT name, marks FROM students ORDER BY marks DESC LIMIT 3"):
9 print(f" {r['name']:<6} {r['marks']}")
10
11print("From Ranchi with 60+:")
12for r in con.execute("SELECT name FROM students WHERE city = ? AND marks >= ?", ("Ranchi", 60)):
13 print(" ", r["name"])
14print("Names with S/I:", [r[0] for r in con.execute("SELECT name FROM students WHERE name LIKE 'S%' OR name LIKE 'I%'")])
Terminal
C:\python-practice> python main.py
Top 3:
Priya 92
Sneha 88
Imran 81
From Ranchi with 60+:
Aman
Rahul
Names with S/I: ['Sneha', 'Imran']
ClauseDoes
WHEREfilter rows
AND / ORcombine conditions
ORDER BY x DESCsort (DESC = high to low)
LIMIT nfirst n rows
LIKE 'S%'pattern: % = anything
Remembercon.row_factory = sqlite3.Row lets you use r["name"] instead of r[1].
Module 18Summaries

Aggregates and GROUP BY: Reports from Data

main.py  PY
1import sqlite3
2from school_db import make_db
3con = make_db() # school.db with 6 students (helper file)
4
5q = """SELECT class, COUNT(*) AS students, ROUND(AVG(marks), 1) AS avg_marks,
6 MAX(marks) AS top, MIN(marks) AS lowest
7 FROM students GROUP BY class ORDER BY class"""
8print(f"{'Class':<6}{'Count':>6}{'Avg':>7}{'Top':>5}{'Low':>5}")
9for row in con.execute(q):
10 print(f"{row[0]:<6}{row[1]:>6}{row[2]:>7}{row[3]:>5}{row[4]:>5}")
11
12print(con.execute("SELECT city, COUNT(*) FROM students GROUP BY city HAVING COUNT(*) > 1").fetchall())
Terminal
C:\python-practice> python main.py
Class Count Avg Top Low
9A 3 71.7 92 45
9B 3 78.0 88 65
[('Ranchi', 3)]
FunctionGives
COUNT(*)number of rows
SUM(x)total
AVG(x)average
MAX / MINhighest / lowest
GROUP BY colone result row per group
HAVINGfilter groups
In real projectsSales per month, students per class, orders per city: every dashboard starts with GROUP BY.
Module 18JOIN

JOIN: Combine Two Related Tables

main.py  PY
1import sqlite3
2con = sqlite3.connect(":memory:")
3con.executescript("""
4CREATE TABLE students (roll INTEGER PRIMARY KEY, name TEXT);
5CREATE TABLE fees (id INTEGER PRIMARY KEY, roll INTEGER, month TEXT, amount INTEGER);
6INSERT INTO students VALUES (1, 'Aman'), (2, 'Priya'), (3, 'Rahul');
7INSERT INTO fees (roll, month, amount) VALUES (1, 'Aug', 1500), (1, 'Sep', 1500), (2, 'Aug', 1500), (3, 'Aug', 750);
8""")
9q = """SELECT s.name, COUNT(f.id) AS payments, SUM(f.amount) AS paid
10 FROM students s JOIN fees f ON s.roll = f.roll
11 GROUP BY s.roll ORDER BY paid DESC"""
12for name, n, paid in con.execute(q):
13 print(f"{name:<6} {n} payment(s) Rs {paid}")
Terminal
C:\python-practice> python main.py
Aman 2 payment(s) Rs 3000
Priya 1 payment(s) Rs 1500
Rahul 1 payment(s) Rs 750
studentsroll, name, classfeesid, roll, amount, monthstudents.roll = fees.roll
RememberData is split into tables to avoid repeating it (the student's name is stored once). JOIN brings it back together.
In real projectsAn online shop has customers, orders and products tables, joined for every invoice.
Module 18Hands-on

Live Lab: Try It Yourself

lab.py
Output Ready
Output of the starter code (press Run after you change it):
('Priya', 'Dhanbad', 92)
('Sneha', 'Bokaro', 88)
('Imran', 'Jamshedpur', 81)
('Aman', 'Ranchi', 78)
[('Rahul', 70), ('Sneha', 93), ('Imran', 86)]
Your tasks
  1. Show only students from Ranchi
  2. Insert a new student with ? placeholders and commit
  3. Find the average marks of the whole school
  4. Delete students with marks below 50, then count rows
PracticePractice 1 of 5

Student Database with Functions

BeginnerScenario. Wrap database actions in functions so the rest of the program never writes raw SQL.

Your tasks

  1. init_db() creates the table
  2. add_student(name, cls, marks) inserts with ? placeholders
  3. find(name) returns a row or None
  4. top(n) returns the best n students
🏢 Used in real projects: Real apps keep SQL inside a "data access" layer of functions, so screens and APIs just call add_student() or find().
practice_18_1.py  PY
1import sqlite3
2con = sqlite3.connect("students.db")
3con.row_factory = sqlite3.Row
4
5def init_db():
6 con.execute("DROP TABLE IF EXISTS students")
7 con.execute("CREATE TABLE students (roll INTEGER PRIMARY KEY, name TEXT, cls TEXT, marks INTEGER)")
8
9def add_student(name, cls, marks):
10 with con: # auto-commit
11 con.execute("INSERT INTO students (name, cls, marks) VALUES (?, ?, ?)", (name, cls, marks))
12
13def find(name):
14 return con.execute("SELECT * FROM students WHERE name = ?", (name,)).fetchone()
15
16def top(n=3):
17 return con.execute("SELECT name, marks FROM students ORDER BY marks DESC LIMIT ?", (n,)).fetchall()
18
19init_db()
20for s in [("Aman", "9A", 78), ("Priya", "9A", 92), ("Rahul", "9B", 65), ("Sneha", "9B", 88)]:
21 add_student(*s)
22r = find("Sneha")
23print("Found:", dict(r) if r else None)
24print("Missing:", find("Zara"))
25print("Top 2:", [tuple(x) for x in top(2)])
PracticePractice 2 of 5

Library Database: Books and Issues

EasyScenario. Track books and who borrowed them. A book cannot be issued if it is already out.

Your tasks

  1. Tables books and issues
  2. issue(book_id, member) checks availability first
  3. return_book(book_id) sets returned = 1
  4. List currently issued books with a JOIN
🏢 Used in real projects: Library, rental and equipment-booking systems use exactly these two tables and an availability check.
practice_18_2.py  PY
1import sqlite3
2con = sqlite3.connect(":memory:")
3con.executescript("""
4CREATE TABLE books (id INTEGER PRIMARY KEY, title TEXT);
5CREATE TABLE issues (id INTEGER PRIMARY KEY, book_id INTEGER, member TEXT, returned INTEGER DEFAULT 0);
6INSERT INTO books (title) VALUES ('Wings of Fire'), ('Godan'), ('Malgudi Days');
7""")
8
9def is_out(book_id):
10 return con.execute("SELECT 1 FROM issues WHERE book_id = ? AND returned = 0", (book_id,)).fetchone() is not None
11
12def issue(book_id, member):
13 if is_out(book_id):
14 return print(f"Book {book_id} is already issued")
15 con.execute("INSERT INTO issues (book_id, member) VALUES (?, ?)", (book_id, member))
16 print(f"Book {book_id} issued to {member}")
17
18def return_book(book_id):
19 con.execute("UPDATE issues SET returned = 1 WHERE book_id = ? AND returned = 0", (book_id,))
20 print(f"Book {book_id} returned")
21
22issue(1, "Asha"); issue(1, "Vikas"); issue(2, "Vikas"); return_book(1); issue(1, "Vikas")
23print("Currently issued:")
24for title, member in con.execute("SELECT b.title, i.member FROM issues i JOIN books b ON b.id = i.book_id WHERE i.returned = 0"):
25 print(f" {title:<15} -> {member}")
PracticePractice 3 of 5

Shop Inventory with Low-Stock Alerts

IntermediateScenario. A kirana store updates stock after each sale and reorders items that fall below a minimum level.

Your tasks

  1. Table items(name, qty, min_qty, price)
  2. sell(name, n) reduces qty only if enough stock
  3. Show items where qty < min_qty
  4. Show total stock value with SUM(qty * price)
🏢 Used in real projects: Inventory and POS systems update stock in the database on every sale and trigger reorder alerts.
practice_18_3.py  PY
1import sqlite3
2con = sqlite3.connect(":memory:")
3con.execute("CREATE TABLE items (name TEXT PRIMARY KEY, qty INTEGER, min_qty INTEGER, price REAL)")
4con.executemany("INSERT INTO items VALUES (?, ?, ?, ?)", [("Rice 5kg", 20, 5, 320), ("Dal 1kg", 8, 6, 128), ("Oil 1L", 12, 4, 175), ("Sugar 1kg", 15, 5, 46)])
5
6def sell(name, n):
7 cur = con.execute("UPDATE items SET qty = qty - ? WHERE name = ? AND qty >= ?", (n, name, n))
8 print(f"{'Sold' if cur.rowcount else 'NOT enough'}: {n} x {name}")
9
10for name, n in [("Dal 1kg", 3), ("Oil 1L", 9), ("Rice 5kg", 30), ("Sugar 1kg", 11)]:
11 sell(name, n)
12
13print("Reorder list:")
14for name, qty, mn in con.execute("SELECT name, qty, min_qty FROM items WHERE qty < min_qty"):
15 print(f" {name:<10} only {qty} left (min {mn})")
16value = con.execute("SELECT SUM(qty * price) FROM items").fetchone()[0]
17print(f"Stock value: Rs {value:,.2f}")
PracticePractice 4 of 5

Search Contacts with LIKE

ChallengingScenario. A school phone directory lets the office search by part of a name or by area.

Your tasks

  1. Insert 8 parent contacts
  2. Ask a search word
  3. Use LIKE ? with f"%{word}%" as the parameter (safe!)
  4. Print matches or "No match"
🏢 Used in real projects: Search boxes in contact apps, school ERPs and e-commerce filters run LIKE (or full-text) queries.
practice_18_4.py  PY
1import sqlite3
2con = sqlite3.connect(":memory:")
3con.execute("CREATE TABLE parents (name TEXT, area TEXT, phone TEXT)")
4con.executemany("INSERT INTO parents VALUES (?, ?, ?)", [
5 ("Sunil Mahto", "Kanke", "98350 10001"), ("Rekha Devi", "Hinoo", "98350 10002"), ("Mohan Lal", "Doranda", "98350 10003"),
6 ("Sunita Kujur", "Bariatu", "98350 10004"), ("Irfan Ansari", "Hinoo", "98350 10005"), ("Sunny Singh", "Lalpur", "98350 10006"),
7 ("Anita Tirkey", "Kanke", "98350 10007"), ("Rakesh Oraon", "Ratu", "98350 10008")])
8
9for word in [input("Search name or area: "), input("Search again: ")]:
10 rows = con.execute("SELECT name, area, phone FROM parents WHERE name LIKE ? OR area LIKE ? ORDER BY name",
11 (f"%{word}%", f"%{word}%")).fetchall()
12 print(f"Results for '{word}':" if rows else f"No match for '{word}'")
13 for r in rows:
14 print(f" {r[0]:<14} {r[1]:<8} {r[2]}")
PracticePractice 5 of 5

Monthly Sales Report (GROUP BY + JOIN)

AdvancedScenario. A mobile shop wants revenue per month and the best-selling product, from orders and products tables.

Your tasks

  1. Tables products(id, name, price) and orders(product_id, qty, month)
  2. JOIN them to get revenue = qty x price
  3. GROUP BY month for monthly totals
  4. GROUP BY product and ORDER BY revenue for the best seller
🏢 Used in real projects: Business dashboards (Power BI, Metabase) run JOIN + GROUP BY queries like this behind every chart.
practice_18_5.py  PY
1import sqlite3
2con = sqlite3.connect(":memory:")
3con.executescript("""
4CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT, price INTEGER);
5CREATE TABLE orders (product_id INTEGER, qty INTEGER, month TEXT);
6INSERT INTO products VALUES (1,'Earphones',799),(2,'Charger',549),(3,'Phone cover',299),(4,'Power bank',1299);
7INSERT INTO orders VALUES (1,12,'Jul'),(2,20,'Jul'),(3,35,'Jul'),(4,6,'Jul'),(1,18,'Aug'),(2,15,'Aug'),
8 (3,40,'Aug'),(4,11,'Aug'),(1,25,'Sep'),(2,22,'Sep'),(3,28,'Sep'),(4,14,'Sep');
9""")
10JOIN = "FROM orders o JOIN products p ON p.id = o.product_id"
11print("Month Revenue")
12for m, rev in con.execute(f"SELECT month, SUM(o.qty * p.price) {JOIN} GROUP BY month ORDER BY MIN(o.rowid)"):
13 print(f"{m:<6} Rs {rev:>8,} {'#' * (rev // 5000)}")
14name, units, rev = con.execute(f"SELECT p.name, SUM(o.qty), SUM(o.qty * p.price) AS r {JOIN} GROUP BY p.id ORDER BY r DESC LIMIT 1").fetchone()
15print(f"Best seller: {name} ({units} units, Rs {rev:,})")
Module 18Recap

Module 18 Recap & Quiz

What you learned

  • 1A database stores data in tables of rows and columns; a primary key identifies each row
  • 2SQL CRUD: INSERT, SELECT, UPDATE, DELETE (always use WHERE with update/delete)
  • 3sqlite3.connect(), execute(), commit(), fetchall()
  • 4Always pass values with ? placeholders, never f-strings
  • 5WHERE, ORDER BY, LIMIT, LIKE filter and sort; GROUP BY with COUNT/SUM/AVG summarises
  • 6JOIN ... ON combines related tables

Quick quiz

Which SQL command reads data?

SELECT returns rows.

What happens if you forget con.commit() after INSERT?

Uncommitted changes are lost.

Safest way to use user input in SQL?

Placeholders prevent SQL injection.
MODULE 19

Data Analysis: NumPy, Pandas & Matplotlib

⏱ 5 hours5 practice examplesStep-through animationsPython Lab

You will learn

  • NumPy arrays and fast maths on whole columns
  • Pandas DataFrames: read CSV, select, filter, sort
  • New columns, groupby and summaries
  • Cleaning messy data: missing values, duplicates, types
  • Charts with Matplotlib: line, bar, pie

You will build

  • A class result analysis
  • A shop sales dashboard
  • Charts saved as images for reports
pandasdf.groupby()plt.bar()
Module 19Real-world use

Where Is This Used in Real Projects?

pandasturn data into answers🏓Cricket statsaverages, strike rates📈Sales dashboardsmonthly revenue🌦Weatherrainfall trends🎓Board resultspass % by district🏥Health datahospital records🤖Machine learningprepare training data

Real projects that use this

  • Data analysts in banks, e-commerce and government use pandas daily: it is like Excel with superpowers
  • Cricket websites compute averages and charts from match data with Python
  • NumPy is the engine under pandas, scikit-learn, TensorFlow and PyTorch
  • Charts made with Matplotlib appear in research papers, reports and dashboards
Module 19NumPy

NumPy Arrays: Maths on Whole Lists at Once

main.py  PY
1import numpy as np
2
3marks = np.array([78, 92, 65, 88, 45, 81])
4print(marks + 5) # add 5 to every item
5print(marks / 100 * 80) # convert out of 80
6print(marks[marks >= 80]) # filter with a condition
7print("mean", marks.mean(), "max", marks.max(), "std", round(marks.std(), 1))
8
9grid = np.array([[78, 85, 72], [95, 91, 88]])
10print(grid.shape, grid.sum(axis=1)) # total per student (row)
11
12print(np.arange(0, 1, 0.25), np.zeros(3), np.random.default_rng(1).integers(1, 7, 5))
Terminal
C:\python-practice> python main.py
[83 97 70 93 50 86]
[62.4 73.6 52. 70.4 36. 64.8]
[92 88 81]
mean 74.83333333333333 max 92 std 15.8
(2, 3) [235 274]
[0. 0.25 0.5 0.75] [0. 0. 0.] [3 4 5 6 1]

Python list

[m + 5 for m in marks]
a loop, item by item

NumPy array

marks + 5
whole array at once, much faster
CodeMeans
np.array(list)make an array
a + 5, a * 2maths on every item
a[a > 80]filter (boolean mask)
a.mean() .max() .sum()statistics
a.shapesize (rows, cols)
RememberInstall with pip install numpy pandas matplotlib. In the Lab they load automatically (first time takes a while).
Module 19DataFrame

Pandas DataFrame: A Table in Python

main.py  PY
1import pandas as pd
2
3df = pd.read_csv("results.csv")
4print(df.head(4))
5print("Shape:", df.shape)
6print(df["maths"].mean())
7print(df.describe().loc[["mean", "max"]].round(1))
Terminal
C:\python-practice> python main.py
name section maths science english hindi
0 Aman A 78 85 72 80
1 Priya A 95 91 88 84
2 Rahul B 65 58 70 62
3 Sneha B 88 92 79 90
Shape: (8, 6)
72.75
maths science english hindi
mean 72.8 73.5 74.9 73.2
max 95.0 92.0 94.0 91.0
📄 results.csv (file on disk)CSV
1name,section,maths,science,english,hindi
2Aman,A,78,85,72,80
3Priya,A,95,91,88,84
4Rahul,B,65,58,70,62
5Sneha,B,88,92,79,90
6Kiran,A,45,52,61,58
7Imran,B,81,77,85,74
8Ritu,A,92,89,94,91
9Deepak,B,38,44,50,47
CodeShows
df.head(n)first n rows
df.shape(rows, columns)
df["col"]one column (a Series)
df.describe()count, mean, min, max...
df.info()column types, missing values
Module 19Select and filter

Selecting, Filtering and Sorting Rows

main.py  PY
1import pandas as pd
2df = pd.read_csv("results.csv")
3
4print(df[["name", "maths"]].head(3))
5print()
6toppers = df[df["maths"] >= 85]
7print(toppers[["name", "section", "maths"]])
8print()
9sec_b_pass = df[(df["section"] == "B") & (df["english"] >= 60)]
10print(list(sec_b_pass["name"]))
11print(df.sort_values("science", ascending=False)["name"].head(3).tolist())
Terminal
C:\python-practice> python main.py
name maths
0 Aman 78
1 Priya 95
2 Rahul 65
 
name section maths
1 Priya A 95
3 Sneha B 88
6 Ritu A 92
 
['Rahul', 'Sneha', 'Imran']
['Sneha', 'Priya', 'Ritu']
TaskCode
some columnsdf[["name", "maths"]]
filter rowsdf[df["maths"] >= 85]
two conditionsdf[(cond1) & (cond2)]
sortdf.sort_values("col", ascending=False)
one celldf.loc[0, "name"]
Common mistakeUse & and | (not and / or) and put each condition in brackets.
Module 19New columns

New Columns, groupby and Summaries

main.py  PY
1import pandas as pd
2df = pd.read_csv("results.csv")
3
4subjects = ["maths", "science", "english", "hindi"]
5df["total"] = df[subjects].sum(axis=1)
6df["percent"] = df["total"] / 4
7df["result"] = df["percent"].apply(lambda p: "PASS" if p >= 50 else "RETEST")
8print(df[["name", "total", "percent", "result"]].sort_values("total", ascending=False).to_string(index=False))
9print()
10print(df.groupby("section")[subjects].mean().round(1))
11print(df["result"].value_counts().to_dict())
Terminal
C:\python-practice> python main.py
name total percent result
Ritu 366 91.50 PASS
Priya 358 89.50 PASS
Sneha 349 87.25 PASS
Imran 317 79.25 PASS
Aman 315 78.75 PASS
Rahul 255 63.75 PASS
Kiran 216 54.00 PASS
Deepak 179 44.75 RETEST
 
maths science english hindi
section
A 77.5 79.2 78.8 78.2
B 68.0 67.8 71.0 68.2
{'PASS': 7, 'RETEST': 1}
  1. 1
    Make a column from other columns: df["total"] = ...
  2. 2
    apply() runs a function on each value
  3. 3
    groupby("section") + mean() = one row per section
  4. 4
    value_counts() counts each category
In real projectsThis is exactly what a result-processing cell in a school or board does, in 6 lines.
Module 19Cleaning

Cleaning Messy Data: Spaces, Case, Blanks, Duplicates, Types

main.py  PY
1import pandas as pd
2df = pd.read_csv("admissions.csv")
3print("Before:", df.shape)
4
5df["name"] = df["name"].str.strip().str.title()
6df["city"] = df["city"].str.strip().str.title()
7df["age"] = pd.to_numeric(df["age"], errors="coerce") # "abc" -> NaN
8df["fee_paid"] = df["fee_paid"].fillna(0)
9df = df.drop_duplicates()
10df = df.dropna(subset=["age"])
11
12print("After:", df.shape)
13print(df.to_string(index=False))
14print("Unpaid:", df[df["fee_paid"] == 0]["name"].tolist())
Terminal
C:\python-practice> python main.py
Before: (7, 4)
After: (4, 4)
name age city fee_paid
Aman 15.0 Ranchi 4500.0
Priya 14.0 Dhanbad 4500.0
Sneha 15.0 Bokaro 4500.0
Kiran 16.0 Ranchi 3000.0
Unpaid: []
📄 admissions.csv (file on disk)CSV
1name,age,city,fee_paid
2Aman,15,Ranchi,4500
3priya ,14,dhanbad,4500
4Rahul,,Ranchi,
5Sneha,15, Bokaro ,4500
6Aman,15,Ranchi,4500
7Kiran,16,RANCHI,3000
8Imran,abc,Jamshedpur,4500
ProblemFix
extra spaces / mixed case.str.strip().str.title()
text in a number columnpd.to_numeric(..., errors="coerce")
blank valuesfillna() or dropna()
repeated rowsdrop_duplicates()
In real projectsData scientists say 80% of the work is cleaning. Real forms always contain messy entries.
Module 19Charts

Matplotlib: Line and Bar Charts

main.py  PY
1import matplotlib.pyplot as plt
2
3months = ["Jun", "Jul", "Aug", "Sep", "Oct"]
4rain_mm = [210, 320, 300, 230, 70] # sample data
5visitors = [12, 26, 30, 22, 15] # thousands at Hundru Falls (sample)
6
7fig, (a, b) = plt.subplots(1, 2, figsize=(9, 3.2))
8a.plot(months, rain_mm, marker="o", color="#1971c2")
9a.set_title("Ranchi monthly rainfall (mm)")
10b.bar(months, visitors, color="#2f9e44")
11b.set_title("Tourists (thousands)")
12for ax in (a, b):
13 ax.grid(alpha=0.3)
14plt.tight_layout()
15plt.savefig("charts.png", dpi=80)
16print("Saved charts.png")
Terminal
C:\python-practice> python main.py
Saved charts.png
🖼 charts.png (image saved by the program)
ChartFunctionBest for
lineplt.plot(x, y)change over time
barplt.bar(x, h)compare categories
pieplt.pie(values)parts of a whole
histogramplt.hist(data)distribution
scatterplt.scatter(x, y)relationship
RememberSample numbers for practice, not official data. Use plt.show() on your computer to open a window, or savefig to save an image for a report.
Module 19pandas + charts

Plot Directly from a DataFrame and Export to Excel

main.py  PY
1import pandas as pd
2import matplotlib.pyplot as plt
3
4df = pd.read_csv("sales.csv", parse_dates=["date"])
5df["revenue"] = df["qty"] * df["price"]
6monthly = df.groupby(df["date"].dt.strftime("%b"), sort=False)["revenue"].sum()
7by_branch = df.pivot_table(index="branch", columns="product", values="revenue", aggfunc="sum", fill_value=0)
8print(monthly)
9print(by_branch)
10
11ax = by_branch.plot(kind="bar", stacked=True, figsize=(6, 3.2), title="Revenue by branch")
12ax.set_ylabel("Rs")
13plt.tight_layout()
14plt.savefig("branch.png", dpi=80)
15by_branch.to_excel("branch_report.xlsx") # needs: pip install openpyxl
Terminal
C:\python-practice> python main.py
date
Jul 16073
Aug 20161
Sep 22710
Name: revenue, dtype: int64
product Charger Earphones Phone cover Power bank
branch
Hinoo 6039 3995 3588 5196
Kanke 4392 4794 2691 2598
Lalpur 5490 8789 7475 3897
🖼 branch.png (image saved by the program)
Module 19Hands-on

Live Lab: Try It Yourself

lab.py
Output Ready
Output of the starter code (press Run after you change it):
student maths science average
1 Priya 92 91 91.5
3 Sneha 88 92 90.0
0 Aman 78 85 81.5
2 Rahul 65 58 61.5
4 Kiran 45 52 48.5
Class average: 74.6
Your tasks
  1. Add an english column and include it in the average
  2. Show only students with average above 70
  3. Add a grade column using apply
  4. Import matplotlib and draw a bar chart of averages (it shows below the output)
PracticePractice 1 of 5

Class Result Analysis

BeginnerScenario. The class teacher has results.csv for 8 students. Produce the analysis the principal asked for.

Your tasks

  1. Load the CSV and add total and percentage columns
  2. Rank students with rank(ascending=False)
  3. Find the subject with the highest class average
  4. Count students per grade band with pd.cut
🏢 Used in real projects: Board result analysis, school report generation and HR performance reviews are pandas tasks.
practice_19_1.py  PY
1import pandas as pd
2df = pd.read_csv("results.csv")
3subs = ["maths", "science", "english", "hindi"]
4df["percent"] = df[subs].mean(axis=1)
5df["rank"] = df["percent"].rank(ascending=False).astype(int)
6print(df.sort_values("rank")[["rank", "name", "section", "percent"]].to_string(index=False))
7print("Best subject:", df[subs].mean().idxmax(), round(df[subs].mean().max(), 1))
8bands = pd.cut(df["percent"], bins=[0, 50, 75, 90, 100], labels=["Retest", "Good", "Very good", "Excellent"])
9print(bands.value_counts().sort_index().to_dict())
PracticePractice 2 of 5

Rainfall Trend Chart

EasyScenario. A Class 10 geography project needs a chart of monthly rainfall in Ranchi with the monsoon months highlighted (sample data).

Your tasks

  1. Make a DataFrame with months and rainfall (mm)
  2. Mark monsoon months (Jun-Sep) with a boolean column
  3. Bar chart: monsoon bars blue, others grey
  4. Print the monsoon share of the yearly rain
🏢 Used in real projects: Weather services, agriculture apps and research reports chart seasonal data like this.
practice_19_2.py  PY
1import pandas as pd
2import matplotlib.pyplot as plt
3
4months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]
5rain = [18, 22, 25, 30, 65, 220, 330, 305, 240, 85, 12, 8] # sample values (mm)
6df = pd.DataFrame({"month": months, "rain": rain})
7df["monsoon"] = df["month"].isin(["Jun", "Jul", "Aug", "Sep"])
8
9colors = ["#1971c2" if m else "#adb5bd" for m in df["monsoon"]]
10plt.figure(figsize=(7, 3))
11plt.bar(df["month"], df["rain"], color=colors)
12plt.title("Ranchi monthly rainfall (sample data)")
13plt.ylabel("mm")
14plt.tight_layout()
15plt.savefig("rainfall.png", dpi=80)
16
17share = df[df["monsoon"]]["rain"].sum() / df["rain"].sum() * 100
18print(f"Yearly total: {df['rain'].sum()} mm, monsoon share: {share:.0f}%")
19print("Wettest month:", df.loc[df["rain"].idxmax(), "month"])
PracticePractice 3 of 5

Mobile Shop Sales Dashboard

IntermediateScenario. A shop with 3 branches in Ranchi wants a quick dashboard from sales.csv: revenue by month, branch and product.

Your tasks

  1. Read with parse_dates and add a revenue column
  2. Group by month, branch and product
  3. Find the best branch and best product
  4. Draw a 2-chart figure: monthly line + product bar
🏢 Used in real projects: Retail owners and analysts build exactly these summaries in pandas, Power BI or Tableau.
practice_19_3.py  PY
1import pandas as pd
2import matplotlib.pyplot as plt
3
4df = pd.read_csv("sales.csv", parse_dates=["date"])
5df["revenue"] = df["qty"] * df["price"]
6df["month"] = df["date"].dt.strftime("%b")
7monthly = df.groupby("month", sort=False)["revenue"].sum()
8branch = df.groupby("branch")["revenue"].sum().sort_values(ascending=False)
9product = df.groupby("product")["revenue"].sum().sort_values()
10
11print("Monthly:", monthly.to_dict())
12print("Best branch:", branch.index[0], f"Rs {branch.iloc[0]:,}")
13print("Best product:", product.index[-1], f"Rs {product.iloc[-1]:,}")
14
15fig, (a, b) = plt.subplots(1, 2, figsize=(9, 3))
16monthly.plot(ax=a, marker="o", title="Revenue by month")
17product.plot(ax=b, kind="barh", color="#2f9e44", title="Revenue by product")
18plt.tight_layout()
19plt.savefig("dashboard.png", dpi=80)
PracticePractice 4 of 5

Clean an Admission Sheet

ChallengingScenario. The admission desk exported a messy CSV. Clean it and produce a fee-defaulter list.

Your tasks

  1. Strip spaces and fix the case of names and cities
  2. Convert age to numbers (bad values become NaN)
  3. Fill missing fees with 0 and remove duplicate rows
  4. Print the clean table, the city counts and the defaulters
🏢 Used in real projects: Every data project starts by cleaning: CRM exports, survey results and government datasets are never perfect.
practice_19_4.py  PY
1import pandas as pd
2df = pd.read_csv("admissions.csv")
3rows_before = len(df)
4for col in ["name", "city"]:
5 df[col] = df[col].str.strip().str.title()
6df["age"] = pd.to_numeric(df["age"], errors="coerce")
7df["fee_paid"] = df["fee_paid"].fillna(0).astype(int)
8df = df.drop_duplicates()
9bad_age = df[df["age"].isna()]["name"].tolist()
10
11print(f"Rows: {rows_before} -> {len(df)}")
12print(df.to_string(index=False))
13print("City counts:", df["city"].value_counts().to_dict())
14print("Check age for:", bad_age)
15print("Fee defaulters:", df[df["fee_paid"] < 4500]["name"].tolist())
PracticePractice 5 of 5

Family Expense Pie Chart

AdvancedScenario. Neha tracks monthly family expenses. Show category totals, percentages and a pie chart.

Your tasks

  1. Make a DataFrame of expense entries
  2. Group by category and sum
  3. Calculate the percentage of each category
  4. Draw a pie chart with labels and percentages
🏢 Used in real projects: Budgeting apps (Walnut, Money Manager) and bank statement analysers show category pie charts.
practice_19_5.py  PY
1import pandas as pd
2import matplotlib.pyplot as plt
3
4entries = [("Rent", 8500), ("Groceries", 3200), ("Groceries", 1450), ("School fees", 2500), ("Electricity", 1180),
5 ("Transport", 900), ("Groceries", 980), ("Mobile & net", 699), ("Transport", 650), ("Medicines", 540)]
6df = pd.DataFrame(entries, columns=["category", "amount"])
7totals = df.groupby("category")["amount"].sum().sort_values(ascending=False)
8pct = (totals / totals.sum() * 100).round(1)
9print(pd.DataFrame({"Rs": totals, "%": pct}))
10print("Total spent: Rs", totals.sum())
11
12plt.figure(figsize=(5, 3.4))
13plt.pie(totals, labels=totals.index, autopct="%1.0f%%", startangle=90)
14plt.title("Where the money goes")
15plt.tight_layout()
16plt.savefig("expenses.png", dpi=80)
Module 19Recap

Module 19 Recap & Quiz

What you learned

  • 1NumPy arrays do maths on whole columns: marks + 5, a[a > 80]
  • 2pandas: read_csv, head, df["col"], filter with df[cond], sort_values
  • 3New columns, apply, groupby().mean(), value_counts(), pivot_table
  • 4Clean: str.strip/title, to_numeric, fillna, drop_duplicates
  • 5Matplotlib: plot, bar, pie, hist; savefig or show
  • 6Export with to_csv / to_excel for reports

Quick quiz

Rows where maths > 80?

Put a boolean condition inside df[ ].

Average marks per section?

groupby then an aggregate.

Best chart for parts of a whole?

Pie (or a stacked bar) shows parts of a total.
MODULE 20

APIs, the Web & Flask

⏱ 4 hours5 practice examplesStep-through animationsPython Lab

You will learn

  • How the web works: requests, responses, status codes
  • Calling JSON APIs with the requests library
  • Reading web pages with BeautifulSoup (scraping basics)
  • Building a website with Flask: routes and templates
  • Forms and your own JSON API

You will build

  • A weather report for Jharkhand cities
  • A Flask student result website
  • A tiny books API
HTTPrequests.get(url)@app.route("/")
Module 20Real-world use

Where Is This Used in Real Projects?

APIsprograms talking to programs🌦Weather appsforecast APIs💳UPI & paymentspayment APIs🏏Cricket scoreslive data APIs🗺Mapslocation APIs🤖AI chatbotsmodel APIs🏛Govt dataopen data portals

Real projects that use this

  • Apps rarely store everything themselves: a weather app asks a weather API and shows the answer
  • Payment gateways, SMS services and maps are all used through APIs
  • Flask and Django let you build your own websites and APIs in Python (Pinterest and Instagram used them)
  • An API speaks JSON, which Python reads as dicts and lists (Module 9)
Module 20How the web works

Request and Response: The Language of the Web

Your Pythonrequests.get()Server / APIweather, UPI, cricketREQUEST: GET /forecast?city=ranchiRESPONSE: 200 OK + JSON{"city": "Ranchi", "temp": 24.1, "humidity": 82}
URL➜request (GET/POST)➜server code➜response (HTML or JSON)
MethodUsed to
GETread data (open a page, fetch weather)
POSTsend new data (submit a form, place an order)
PUT / PATCHupdate data
DELETEremove data
StatusMeans
200 OKsuccess
201 Creatednew item saved
400 Bad Requestyour data was wrong
401 / 403login needed / not allowed
404 Not Foundno such page or item
500server error
Module 20requests

Calling a JSON API with requests

main.py  PY
1import requests
2
3url = "https://api.open-meteo.com/v1/forecast"
4params = {"latitude": 23.34, "longitude": 85.31,
5 "current": "temperature_2m,relative_humidity_2m,wind_speed_10m", "timezone": "Asia/Kolkata"}
6r = requests.get(url, params=params, timeout=10)
7print("Status:", r.status_code)
8
9data = r.json() # JSON text -> Python dict
10now = data["current"]
11print(f"Ranchi now: {now['temperature_2m']} C, humidity {now['relative_humidity_2m']}%, wind {now['wind_speed_10m']} km/h")
12print("Keys in the response:", list(data.keys()))
Terminal
C:\python-practice> python main.py
Status: 200
Ranchi now: 24.1 C, humidity 82%, wind 9.4 km/h
Keys in the response: ['latitude', 'longitude', 'timezone', 'current', 'daily']
Sample response (live values differ).
  1. 1
    pip install requests (once)
  2. 2
    requests.get(url, params=...) sends the request
  3. 3
    r.status_code: did it work?
  4. 4
    r.json() turns the JSON into a dict you can index
RememberOpen-Meteo is a free weather API that needs no key. The output shown is a sample response; on your computer you get live values.
Common mistakeAlways set a timeout. Without it, a slow server can freeze your program.
Module 20Errors on the web

Handling Status Codes, Timeouts and Bad Data

main.py  PY
1import requests
2
3def get_user(uid):
4 try:
5 r = requests.get(f"https://api.example.com/users/{uid}", timeout=5)
6 r.raise_for_status() # 4xx/5xx -> exception
7 return r.json()
8 except requests.exceptions.Timeout:
9 print("Server too slow, try later")
10 except requests.exceptions.HTTPError as e:
11 print("HTTP problem:", e)
12 except requests.exceptions.RequestException as e:
13 print("Network problem:", e)
14 return None
15
16print(get_user(1))
17print(get_user(99))
18try:
19 requests.get("https://api.example.com/slow", timeout=2)
20except requests.exceptions.Timeout as e:
21 print("Timeout:", e)
Terminal
C:\python-practice> python main.py
{'id': 1, 'name': 'Aarav Kumar', 'city': 'Ranchi'}
HTTP problem: 404 Error for url: https://api.example.com/users/99
None
Timeout: Read timed out. (read timeout=2)
!

raise_for_status()

Turns 4xx/5xx responses into exceptions so you can handle them with try / except (Module 13).
📶

Network is unreliable

Mobile data drops, servers go down. Professional code expects it and shows a friendly message.
RememberExample API address and sample responses for practice.
Module 20Scraping basics

Reading Web Pages with BeautifulSoup

main.py  PY
1from bs4 import BeautifulSoup
2
3html = """
4<html><body>
5 <h1>Jharkhand Tourism</h1>
6 <ul class="spots">
7 <li data-km="45">Hundru Falls</li>
8 <li data-km="40">Dassam Falls</li>
9 <li data-km="156">Netarhat</li>
10 </ul>
11 <a href="/booking">Book a tour</a>
12</body></html>
13"""
14soup = BeautifulSoup(html, "html.parser")
15print(soup.h1.text)
16for li in soup.select("ul.spots li"):
17 print(f"{li.text:<14} {li['data-km']} km from Ranchi")
18print("Link:", soup.find("a")["href"])
Terminal
C:\python-practice> python main.py
Jharkhand Tourism
Hundru Falls 45 km from Ranchi
Dassam Falls 40 km from Ranchi
Netarhat 156 km from Ranchi
Link: /booking
  1. 1
    Get the page HTML (with requests.get(url).text)
  2. 2
    Parse it: BeautifulSoup(html, "html.parser")
  3. 3
    Find elements with find, find_all or CSS select
  4. 4
    Read .text and attributes like ["href"]
Common mistakeAlways check a website's terms and robots.txt before scraping, and prefer an official API if one exists.
In real projectsPrice-comparison sites and research projects collect public data this way. (pip install beautifulsoup4)
Module 20Flask

Your First Website with Flask

app.pyPY
1from flask import Flask
2
3app = Flask(__name__)
4
5@app.route("/")
6def home():
7 return "<h1>Welcome to BrainBit Infotech Pvt Ltd</h1><p>Python batch starts 15 October.</p>"
8
9@app.route("/about")
10def about():
11 return "<h2>About us</h2><p>Computer training centre in Ranchi since 2010.</p>"
12
13@app.route("/student/<name>")
14def student(name):
15 return f"<h2>Hello, {name.title()}!</h2><p>Your profile page.</p>"
16
17if __name__ == "__main__":
18 app.run(debug=True)
Terminal
D:\python-practice> python app.py
* Serving Flask app 'app'
* Debug mode: on
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
http://127.0.0.1:5000/
http://127.0.0.1:5000/student/riya
Remember@app.route("/") connects a URL to a function. Whatever the function returns is sent to the browser. Open http://127.0.0.1:5000 after running.
Module 20Templates

Templates: Fill HTML with Python Data (Jinja2)

app.pyPY
1from flask import Flask, render_template_string
2
3app = Flask(__name__)
4RESULTS = {"aman": 412, "priya": 468, "rahul": 301}
5
6PAGE = """
7<h2>Class 9A Results</h2>
8<table border="1" cellpadding="6">
9 <tr><th>Name</th><th>Marks</th><th>Result</th></tr>
10 {% for name, m in results.items() %}
11 <tr><td>{{ name.title() }}</td><td>{{ m }}</td>
12 <td>{{ "PASS" if m >= 165 else "FAIL" }}</td></tr>
13 {% endfor %}
14</table>
15<p>Total students: {{ results|length }}</p>
16"""
17
18@app.route("/results")
19def results():
20 return render_template_string(PAGE, results=RESULTS)
http://127.0.0.1:5000/results
Template codeMeans
{{ value }}print a value
{% for x in list %}loop
{% if cond %}condition
|lengtha filter
RememberIn real projects templates live in a templates/ folder and you call render_template("results.html", ...).
Module 20Forms

Handling Forms: GET Shows the Form, POST Receives Data

app.pyPY
1from flask import Flask, request
2
3app = Flask(__name__)
4MARKS = {"101": ("Aman", 412), "102": ("Priya", 468)}
5
6@app.route("/", methods=["GET", "POST"])
7def lookup():
8 msg = ""
9 if request.method == "POST":
10 roll = request.form.get("roll", "").strip()
11 if roll in MARKS:
12 name, m = MARKS[roll]
13 msg = f"<p><b>{name}</b> scored {m}/500 ({m / 5:.1f}%)</p>"
14 else:
15 msg = "<p style='color:red'>Roll number not found</p>"
16 return f"""<h3>Result Lookup</h3>
17 <form method="post"><input name="roll" placeholder="Roll no">
18 <button>Check</button></form>{msg}"""
http://127.0.0.1:5000/ [POST 200 OK]
http://127.0.0.1:5000/ [POST 200 OK]
  1. 1
    methods=["GET", "POST"] allows both
  2. 2
    request.form.get("roll") reads the typed value
  3. 3
    Validate it, then build the response
Common mistakeIn production, also protect forms against CSRF (Flask-WTF) and never trust user input.
Module 20Your own API

Build a JSON API with Flask

api.pyPY
1from flask import Flask, jsonify, request
2
3app = Flask(__name__)
4books = [{"id": 1, "title": "Wings of Fire", "available": True},
5 {"id": 2, "title": "Godan", "available": False}]
6
7@app.get("/api/books")
8def list_books():
9 return jsonify(books)
10
11@app.get("/api/books/<int:book_id>")
12def one_book(book_id):
13 for b in books:
14 if b["id"] == book_id:
15 return jsonify(b)
16 return jsonify({"error": "not found"}), 404
17
18@app.post("/api/books")
19def add_book():
20 data = request.get_json()
21 book = {"id": len(books) + 1, "title": data["title"], "available": True}
22 books.append(book)
23 return jsonify(book), 201
GET /api/books → 200 OK
[{"available":true,"id":1,"title":"Wings of Fire"},{"available":false,"id":2,"title":"Godan"}]
GET /api/books/9 → 404 NOT FOUND
{"error":"not found"}
POST /api/books → 201 CREATED
{"available":true,"id":3,"title":"Malgudi Days"}
In real projectsMobile apps and JavaScript front-ends (your JS course!) call APIs like this one with fetch(). Status 201 = created, 404 = not found.
Module 20Hands-on

Live Lab: Try It Yourself

lab.py
Output Ready
Output of the starter code (press Run after you change it):
Forecast for Ranchi
Thu: 20-29 C, no rain
Fri: 21-30 C, take an umbrella
Sat: 19-28 C, take an umbrella
Your tasks
  1. Print the hottest day using max(..., key=...)
  2. Add another day to the JSON text and run again
  3. Convert the dict back to JSON with json.dumps(data, indent=2)
  4. On your own computer: pip install requests flask and try the slides' code
PracticePractice 1 of 5

Weather Report for Jharkhand Cities

BeginnerScenario. Call a weather API for 4 cities and print a neat report with a travel tip.

Your tasks

  1. Store city coordinates in a dict
  2. Loop and call the API with params and timeout
  3. Read temperature and humidity from the JSON
  4. Print a tip: "carry a jacket" below 20 C
🏢 Used in real projects: Travel, farming and delivery apps call weather APIs for many locations.
practice_20_1.py  PY
1import requests
2
3CITIES = {"Ranchi": (23.34, 85.31), "Jamshedpur": (22.80, 86.18), "Dhanbad": (23.80, 86.43), "Netarhat": (23.47, 84.27)}
4URL = "https://api.open-meteo.com/v1/forecast"
5
6print(f"{'City':<11}{'Temp':>6}{'Hum':>6} Tip")
7for city, (lat, lon) in CITIES.items():
8 r = requests.get(URL, params={"latitude": lat, "longitude": lon, "current": "temperature_2m,relative_humidity_2m"}, timeout=10)
9 if r.status_code != 200:
10 print(city, "unavailable")
11 continue
12 cur = r.json()["current"]
13 t, h = cur["temperature_2m"], cur["relative_humidity_2m"]
14 tip = "carry a jacket" if t < 20 else "umbrella!" if h > 85 else "nice weather"
15 print(f"{city:<11}{t:>5}C{h:>5}% {tip}")
PracticePractice 2 of 5

Currency Converter Using a Rates API

EasyScenario. A study-abroad counsellor needs rupee amounts converted into several currencies from a rates API.

Your tasks

  1. GET the rates JSON (base INR)
  2. Ask the amount in rupees
  3. Convert to each currency in the response
  4. Handle network errors with try / except
🏢 Used in real projects: Banking, travel and e-commerce sites fetch live exchange rates through APIs.
practice_20_2.py  PY
1import requests
2
3try:
4 r = requests.get("https://api.example-rates.com/latest", params={"base": "INR"}, timeout=10)
5 r.raise_for_status()
6 rates = r.json()["rates"]
7except requests.exceptions.RequestException as e:
8 print("Could not get rates:", e)
9 rates = {}
10
11amount = float(input("Amount in rupees: "))
12print(f"Rs {amount:,.0f} on {r.json()['date']} =")
13for code, rate in sorted(rates.items()):
14 print(f" {code} {amount * rate:>12,.2f}")
PracticePractice 3 of 5

Coaching Centre Website

IntermediateScenario. Make a 2-page Flask site: a home page listing courses and a page for each course.

Your tasks

  1. Store courses in a dict
  2. Home route builds a list of links
  3. Dynamic route /course/<key>
  4. Return a 404 page for unknown courses
🏢 Used in real projects: Small business and school websites are often built with Flask or Django exactly like this, then deployed online.
app.pyPY
1from flask import Flask
2
3app = Flask(__name__)
4COURSES = {"python": "Python Programming (3 months)", "web": "HTML, CSS & JavaScript (3 months)", "excel": "Advanced Excel (1 month)"}
5
6@app.route("/")
7def home():
8 links = "".join(f'<li><a href="/course/{k}">{v}</a></li>' for k, v in COURSES.items())
9 return f"<h1>BrainBit Infotech Pvt Ltd</h1><h3>Our courses</h3><ul>{links}</ul>"
10
11@app.route("/course/<key>")
12def course(key):
13 if key not in COURSES:
14 return "<h3>Course not found</h3>", 404
15 return f"<h2>{COURSES[key]}</h2><p>Batches every month. Call 98350 00000.</p><a href='/'>Back</a>"
16
17if __name__ == "__main__":
18 app.run(debug=True)
PracticePractice 4 of 5

Student Search Page (Query String + Template)

ChallengingScenario. A search box on a school website: typing part of a name or a class shows matching students.

Your tasks

  1. Read request.args.get("q") from the URL (?q=...)
  2. Filter the list in Python
  3. Render a template with a loop and the result count
  4. Keep the search text in the box after searching
🏢 Used in real projects: Search pages in school portals, job sites and shops read query strings exactly like this.
app.pyPY
1from flask import Flask, request, render_template_string
2
3app = Flask(__name__)
4STUDENTS = [("Aman", "9A", 78), ("Priya", "9A", 92), ("Rahul", "9B", 65), ("Sneha", "9B", 88), ("Arif", "9A", 71)]
5PAGE = """<h3>Find a student</h3>
6<form><input name="q" value="{{ q }}"> <button>Search</button></form>
7{% if q %}<p>{{ rows|length }} result(s) for "{{ q }}"</p>
8<table border=1 cellpadding=5>{% for n, c, m in rows %}<tr><td>{{ n }}</td><td>{{ c }}</td><td>{{ m }}</td></tr>{% endfor %}</table>{% endif %}"""
9
10@app.route("/")
11def search():
12 q = request.args.get("q", "").strip()
13 rows = [s for s in STUDENTS if q and (q.lower() in s[0].lower() or q.upper() == s[1])]
14 return render_template_string(PAGE, q=q, rows=rows)
PracticePractice 5 of 5

To-Do JSON API (GET, POST, PATCH)

AdvancedScenario. Build a small REST API that a mobile app could use to add tasks, mark them done and filter them.

Your tasks

  1. GET /api/tasks with optional ?done=true/false
  2. POST creates a task (400 if title missing)
  3. PATCH /api/tasks/<id> marks it done (404 if missing)
  4. Return the right status codes
🏢 Used in real projects: Every mobile app talks to a REST API like this. FastAPI and Django REST Framework build the same thing at scale.
api.pyPY
1from flask import Flask, jsonify, request
2
3app = Flask(__name__)
4tasks = []
5
6@app.get("/api/tasks")
7def all_tasks():
8 done = request.args.get("done")
9 result = tasks if done is None else [t for t in tasks if str(t["done"]).lower() == done]
10 return jsonify(result)
11
12@app.post("/api/tasks")
13def add_task():
14 data = request.get_json(silent=True) or {}
15 if not data.get("title"):
16 return jsonify({"error": "title is required"}), 400
17 task = {"id": len(tasks) + 1, "title": data["title"], "done": False}
18 tasks.append(task)
19 return jsonify(task), 201
20
21@app.patch("/api/tasks/<int:tid>")
22def finish(tid):
23 for t in tasks:
24 if t["id"] == tid:
25 t["done"] = True
26 return jsonify(t)
27 return jsonify({"error": "no such task"}), 404
Module 20Recap

Module 20 Recap & Quiz

What you learned

  • 1The web is request and response: GET reads, POST sends; status 200/201 good, 4xx/5xx problems
  • 2requests.get(url, params=..., timeout=...) then r.json()
  • 3Handle Timeout, HTTPError, RequestException; use raise_for_status()
  • 4BeautifulSoup parses HTML: find, select, .text, ["href"]
  • 5Flask: @app.route("/path") functions return pages; templates use {{ }} and {% %}
  • 6request.form / request.args / get_json() read input; jsonify returns JSON

Quick quiz

Which HTTP method submits a form to save data?

POST sends data to the server.

Status code for "not found"?

404 = Not Found.

In Flask, what connects a URL to a function?

The route decorator maps URLs to view functions.
PART 4 COMPLETE

Well done!

You finished Advanced Python & Data: 4 modules and 20 practice examples.

Before moving on

  • Re-type every practice example yourself without looking
  • Save your work in D:\python-practice\part4
  • 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