Python Final Mini Project — Student Progress Manager
Ab tak ke concepts ko ek real project me combine karte hain. Hum ek command-line Student Progress Manager banayenge jo student records save karega, marks validate karega, SQLite database use karega, reports show karega, JSON export karega aur basic automated tests ke saath reliable rahega.
students.db file use karta hai. BrounStack Playground ke liye neeche separate in-memory demo diya gaya hai, kyunki online runs isolated ho sakte hain.Project goal
App ka purpose sirf CRUD banana nahi hai. Goal hai clean program structure, input validation, safe SQL, reusable functions, predictable errors, JSON export aur tests ko ek hi practical workflow me use karna.
Is project ko course ka revision samjho. Variables se lekar functions, exceptions, files, modules, testing aur SQLite tak ka real combination yaha milega.
Features we will build
- Add a student with name, email and score.
- List all students in score order.
- Search one student by ID.
- Update a student score.
- Delete a student safely.
- Show class statistics: count, average, highest and lowest score.
- Export records to JSON.
- Validate input and handle expected errors.
- Use parameterized SQL queries.
- Test core validation and grading logic.
Course concepts used
- Variables, strings, conditions, loops: menu and input processing.
- Lists/dictionaries: reports and JSON-friendly records.
- Functions: each responsibility separate.
- Exceptions: invalid input and database failures.
- Modules:
sqlite3,json,pathlib. - File handling: JSON export.
- Comprehensions/built-ins: small transformations and summaries.
- Testing: validation and grade rules.
- SQLite: persistent data and parameterized CRUD.
Recommended project structure
student-progress-manager/
├── app.py
├── students.db
├── exports/
│ └── students.json
├── test_app.py
└── README.mdBeginner project ke liye one main module enough hai. Jab project grow kare, database, services and CLI ko separate modules me split kar sakte ho.
Database schema
CREATE TABLE IF NOT EXISTS students (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
score REAL NOT NULL CHECK(score BETWEEN 0 AND 100)
);Database constraints invalid state ko reduce karte hain. Application validation bhi rakhenge taaki user ko readable error message mile.
Complete app.py
Ye complete local version hai. Isse file me save karke terminal se run kar sakte ho.
import json
import sqlite3
from pathlib import Path
DB_PATH = Path("students.db")
EXPORT_DIR = Path("exports")
def connect_db():
connection = sqlite3.connect(DB_PATH)
connection.row_factory = sqlite3.Row
return connection
def setup_database():
with connect_db() as connection:
connection.execute("""
CREATE TABLE IF NOT EXISTS students (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
score REAL NOT NULL CHECK(score BETWEEN 0 AND 100)
)
""")
def clean_name(value):
name = value.strip()
if len(name) < 2:
raise ValueError("Name must contain at least 2 characters")
return name
def clean_email(value):
email = value.strip().lower()
if "@" not in email or email.startswith("@") or email.endswith("@"):
raise ValueError("Enter a valid email address")
return email
def clean_score(value):
try:
score = float(value)
except (TypeError, ValueError) as error:
raise ValueError("Score must be a number") from error
if not 0 <= score <= 100:
raise ValueError("Score must be between 0 and 100")
return score
def grade_for(score):
if score >= 90:
return "A"
if score >= 75:
return "B"
if score >= 60:
return "C"
if score >= 40:
return "D"
return "F"
def add_student(name, email, score):
name = clean_name(name)
email = clean_email(email)
score = clean_score(score)
with connect_db() as connection:
cursor = connection.execute(
"INSERT INTO students (name, email, score) VALUES (?, ?, ?)",
(name, email, score),
)
return cursor.lastrowid
def list_students():
with connect_db() as connection:
return connection.execute(
"SELECT id, name, email, score FROM students ORDER BY score DESC, name"
).fetchall()
def get_student(student_id):
with connect_db() as connection:
return connection.execute(
"SELECT id, name, email, score FROM students WHERE id = ?",
(student_id,),
).fetchone()
def update_score(student_id, score):
score = clean_score(score)
with connect_db() as connection:
cursor = connection.execute(
"UPDATE students SET score = ? WHERE id = ?",
(score, student_id),
)
return cursor.rowcount == 1
def delete_student(student_id):
with connect_db() as connection:
cursor = connection.execute(
"DELETE FROM students WHERE id = ?",
(student_id,),
)
return cursor.rowcount == 1
def class_stats():
with connect_db() as connection:
return connection.execute("""
SELECT
COUNT(*) AS total,
AVG(score) AS average,
MAX(score) AS highest,
MIN(score) AS lowest
FROM students
""").fetchone()
def export_json():
rows = list_students()
records = [
{
"id": row["id"],
"name": row["name"],
"email": row["email"],
"score": row["score"],
"grade": grade_for(row["score"]),
}
for row in rows
]
EXPORT_DIR.mkdir(exist_ok=True)
path = EXPORT_DIR / "students.json"
path.write_text(
json.dumps(records, indent=2, ensure_ascii=False),
encoding="utf-8",
)
return path
def print_student(row):
print(
f'#{row["id"]} | {row["name"]} | {row["email"]} | '
f'{row["score"]:.1f} | Grade {grade_for(row["score"])}'
)
def read_student_id():
try:
return int(input("Student ID: ").strip())
except ValueError as error:
raise ValueError("Student ID must be an integer") from error
def menu():
print("\nStudent Progress Manager")
print("1. Add student")
print("2. List students")
print("3. Find student")
print("4. Update score")
print("5. Delete student")
print("6. Class statistics")
print("7. Export JSON")
print("0. Exit")
def run():
setup_database()
while True:
menu()
choice = input("Choose: ").strip()
try:
if choice == "1":
student_id = add_student(
input("Name: "),
input("Email: "),
input("Score: "),
)
print(f"Student added with ID {student_id}")
elif choice == "2":
rows = list_students()
if not rows:
print("No students found")
for row in rows:
print_student(row)
elif choice == "3":
row = get_student(read_student_id())
print_student(row) if row else print("Student not found")
elif choice == "4":
updated = update_score(read_student_id(), input("New score: "))
print("Score updated" if updated else "Student not found")
elif choice == "5":
deleted = delete_student(read_student_id())
print("Student deleted" if deleted else "Student not found")
elif choice == "6":
stats = class_stats()
if stats["total"] == 0:
print("No data available")
else:
print("Students:", stats["total"])
print("Average:", round(stats["average"], 2))
print("Highest:", stats["highest"])
print("Lowest:", stats["lowest"])
elif choice == "7":
print("Exported to", export_json())
elif choice == "0":
print("Goodbye")
break
else:
print("Choose a valid menu option")
except ValueError as error:
print("Input error:", error)
except sqlite3.IntegrityError as error:
print("Database rule failed:", error)
except sqlite3.Error as error:
print("Database error:", error)
if __name__ == "__main__":
run()How the app works
setup_database()table ensure karta hai.run()menu loop start karta hai.- User input validation functions se pass hota hai.
- CRUD functions parameterized SQL execute karte hain.
- Database rows
sqlite3.Rowobjects ke form me readable field access dete hain. - Report SQL aggregates use karta hai.
- Export function rows ko dictionaries me convert karke JSON file likhta hai.
- Expected failures user-friendly messages me handle hote hain.
Why SQL parameters are mandatory
connection.execute(
"SELECT * FROM students WHERE email = ?",
(email,),
)User data ko SQL f-string me directly insert mat karo. Placeholders data aur SQL structure ko separate rakhte hain and SQL injection risk ko reduce karte hain.
JSON export
Database row JSON directly nahi hota. Hum records ko normal dictionaries me transform karte hain, derived grade add karte hain, then json.dumps() se serialize karte hain.
[
{
"id": 1,
"name": "Aman",
"email": "aman@example.com",
"score": 88.0,
"grade": "B"
}
]Add automated tests
Validation and grading deterministic hain, so ye unit tests ke liye perfect starting points hain.
import unittest
from app import clean_email, clean_score, grade_for
class StudentProgressTests(unittest.TestCase):
def test_grade_boundaries(self):
cases = [
(95, "A"),
(90, "A"),
(75, "B"),
(60, "C"),
(40, "D"),
(39.9, "F"),
]
for score, expected in cases:
with self.subTest(score=score):
self.assertEqual(grade_for(score), expected)
def test_clean_score(self):
self.assertEqual(clean_score("88.5"), 88.5)
def test_invalid_score(self):
with self.assertRaises(ValueError):
clean_score("150")
def test_email_normalization(self):
self.assertEqual(
clean_email(" Aman@Example.COM "),
"aman@example.com",
)
if __name__ == "__main__":
unittest.main()python -m unittest -vRun the project locally
- New folder create karo.
- Optional but recommended:
python -m venv .venv. - Virtual environment activate karo.
- Code ko
app.pyme save karo. - Terminal me
python app.pyrun karo. - Kuch students add karo and list/report/export test karo.
test_app.pyadd karkepython -m unittest -vrun karo.
Playground-friendly in-memory version
Ye short demo persistence ke bina SQLite workflow, functions, report and JSON output demonstrate karta hai.
import json
import sqlite3
def grade(score):
if score >= 90:
return "A"
if score >= 75:
return "B"
if score >= 60:
return "C"
if score >= 40:
return "D"
return "F"
db = sqlite3.connect(":memory:")
db.row_factory = sqlite3.Row
db.execute("CREATE TABLE students (name TEXT, score REAL CHECK(score BETWEEN 0 AND 100))")
db.executemany(
"INSERT INTO students VALUES (?, ?)",
[("Aman", 88), ("Riya", 94), ("Kabir", 72)],
)
rows = db.execute("SELECT name, score FROM students ORDER BY score DESC").fetchall()
records = [
{"name": row["name"], "score": row["score"], "grade": grade(row["score"])}
for row in rows
]
print(json.dumps(records, indent=2))
print("Average:", round(sum(item["score"] for item in records) / len(records), 2))
db.close()Stretch improvements
- Split code into
database.py,services.pyandcli.py. - Add courses and enrollments with foreign keys.
- Add pagination and search.
- Add CSV import/export.
- Add timestamps for created/updated records.
- Add transaction tests with temporary databases.
- Add logging to a file without storing sensitive data.
- Add a REST API later using a web framework.
- Add authentication only when you understand secure password hashing and session/token handling.
Common project mistakes
- One giant function me complete application likhna.
- Validation only database error aane ke baad karna.
- SQL values ko f-strings se interpolate karna.
- Every exception ko broad
except Exceptionse silently hide karna. - Database connection close/transaction behavior ignore karna.
- Tests sirf happy path ke liye likhna.
- Generated database/export files ko unnecessarily Git me commit karna.
- Secrets or passwords plain text me store karna.
From mini project to real software
Production app ke liye authentication, authorization, migrations, backups, concurrency, audit logging, stronger email validation, secure configuration, deployment and monitoring jaise concerns separately design karne padte hain. Mini project ka goal in sab ko fake karna nahi, balki clean foundations build karna hai.
Final course checklist
- Python syntax, variables, conditions and loops confidently use kar sakte ho?
- Strings, lists, tuples, sets and dictionaries ka correct use clear hai?
- Functions, scope, modules and exceptions samajh aate hain?
- Files, JSON and standard library tools use kar sakte ho?
- Classes, inheritance, iterators, generators and decorators ka purpose clear hai?
- Comprehensions and useful built-ins readable way me use kar sakte ho?
- Virtual environment, pip and basic tests ka workflow samajh aaya?
- SQLite me safe parameterized CRUD aur transactions use kar sakte ho?
- Ek complete mini project ko requirements → code → test → improve cycle me build kar sakte ho?
Final Challenge — Make It Yours
Project copy karke sirf run mat karo. Isme apne changes add karo.
- Student me
cityfield add karo. - Search by email feature add karo.
- Pass/fail report add karo.
- Top 3 students query add karo.
- Duplicate email error ko friendly custom message me map karo.
- JSON export filename me current date add karo.
- At least 8 unit tests likho.
- README me setup, features and sample output document karo.
- Fresh environment me project run/test karke verify karo.
- Git branch + commit + PR workflow me project publish karo.