254 lines
10 KiB
Python
254 lines
10 KiB
Python
"""Create tables and seed the DEVTeacher placeholder content on first run.
|
||||
|
|
|
|||
|
|
Idempotent: if an admin already exists, nothing is seeded. The default admin is
|
|||
|
|
username ``DEVTeacher`` / password ``DEVTeacher`` (the teacher changes it).
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
from datetime import date
|
|||
|
|
|
|||
|
|
from sqlalchemy import inspect, text
|
|||
|
|
from sqlalchemy.orm import Session
|
|||
|
|
|
|||
|
|
from app.db import Base, SessionLocal, engine
|
|||
|
|
from app.models import (
|
|||
|
|
AdminUser,
|
|||
|
|
Attendance,
|
|||
|
|
Course,
|
|||
|
|
DayMark,
|
|||
|
|
Enquiry,
|
|||
|
|
FeePlan,
|
|||
|
|
Profile,
|
|||
|
|
Review,
|
|||
|
|
ScheduleSlot,
|
|||
|
|
SiteSettings,
|
|||
|
|
Student,
|
|||
|
|
Subject,
|
|||
|
|
Testimonial,
|
|||
|
|
)
|
|||
|
|
from app.security import hash_password
|
|||
|
|
|
|||
|
|
DEFAULT_ADMIN_USER = "DEVTeacher"
|
|||
|
|
DEFAULT_ADMIN_PASS = "DEVTeacher"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _seed(db: Session) -> None:
|
|||
|
|
db.add(AdminUser(username=DEFAULT_ADMIN_USER, password_hash=hash_password(DEFAULT_ADMIN_PASS)))
|
|||
|
|
|
|||
|
|
db.add(
|
|||
|
|
Profile(
|
|||
|
|
name="Swati Yadav",
|
|||
|
|
tagline="Making learning joyful, clear and exam-ready",
|
|||
|
|
bio=(
|
|||
|
|
"Hello! I'm Swati Yadav, a passionate tuition teacher with years of experience "
|
|||
|
|
"helping students build strong fundamentals and real confidence. I focus on "
|
|||
|
|
"concept clarity, regular practice and personal attention so every student can "
|
|||
|
|
"reach their full potential — whether the goal is topping the class, cracking "
|
|||
|
|
"board exams, or simply falling in love with a subject."
|
|||
|
|
),
|
|||
|
|
photo_url="",
|
|||
|
|
qualifications="M.Sc. Mathematics · B.Ed. · Certified Online Educator",
|
|||
|
|
experience_years=10,
|
|||
|
|
students_taught=500,
|
|||
|
|
achievements=(
|
|||
|
|
"95% of students improved by 2+ grades\n"
|
|||
|
|
"Mentored 50+ board-exam toppers\n"
|
|||
|
|
"Personalised study plans for every learner"
|
|||
|
|
),
|
|||
|
|
hero_cta="Book a free demo class",
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
subjects = [
|
|||
|
|
("Mathematics", "Class 6–12", "Algebra, geometry, calculus and problem-solving made simple with step-by-step methods.", "📐", 1),
|
|||
|
|
("Physics", "Class 9–12", "Concept-first physics with real-life examples, numericals and exam techniques.", "🔬", 2),
|
|||
|
|
("Chemistry", "Class 9–12", "Organic, inorganic and physical chemistry taught with clarity and easy recall tricks.", "⚗️", 3),
|
|||
|
|
("English", "Class 6–10", "Grammar, comprehension, writing skills and confident communication.", "📖", 4),
|
|||
|
|
]
|
|||
|
|
for name, level, desc, icon, order in subjects:
|
|||
|
|
db.add(Subject(name=name, level=level, description=desc, mode="Online & Offline", icon=icon, sort_order=order))
|
|||
|
|
|
|||
|
|
courses = [
|
|||
|
|
(
|
|||
|
|
"Board Exam Crash Course",
|
|||
|
|
"Mathematics & Science",
|
|||
|
|
"An intensive revision programme covering the complete syllabus with mock tests, doubt-clearing and previous-year papers.",
|
|||
|
|
"3 months",
|
|||
|
|
"Practice tests every week\nPersonal doubt sessions\nComplete syllabus revision",
|
|||
|
|
1,
|
|||
|
|
),
|
|||
|
|
(
|
|||
|
|
"Foundation Builder",
|
|||
|
|
"All Subjects",
|
|||
|
|
"A steady long-term course for younger students that builds strong basics and study habits from the ground up.",
|
|||
|
|
"Full academic year",
|
|||
|
|
"Concept-first teaching\nRegular homework & feedback\nMonthly progress reports",
|
|||
|
|
2,
|
|||
|
|
),
|
|||
|
|
(
|
|||
|
|
"One-on-One Personal Tuition",
|
|||
|
|
"Chosen Subject",
|
|||
|
|
"Fully personalised private tuition tailored to a single student's pace, goals and weak areas.",
|
|||
|
|
"Flexible",
|
|||
|
|
"Completely customised plan\nFlexible timings\nDedicated attention",
|
|||
|
|
3,
|
|||
|
|
),
|
|||
|
|
]
|
|||
|
|
for title, subject, desc, duration, highlights, order in courses:
|
|||
|
|
db.add(
|
|||
|
|
Course(
|
|||
|
|
title=title,
|
|||
|
|
subject=subject,
|
|||
|
|
description=desc,
|
|||
|
|
duration=duration,
|
|||
|
|
mode="Online & Offline",
|
|||
|
|
highlights=highlights,
|
|||
|
|
sort_order=order,
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
fees = [
|
|||
|
|
("Group Batch", "₹1,500", "per month", "Small batches (max 8)\n3 classes a week\nWeekly tests\nStudy material included", False, 1),
|
|||
|
|
("Premium Batch", "₹2,500", "per month", "Mini batches (max 4)\n5 classes a week\nWeekly tests + doubt sessions\nMonthly progress report", True, 2),
|
|||
|
|
("1-on-1 Private", "₹4,000", "per month", "Fully personal tuition\nFlexible timings\nCustom study plan\nUnlimited doubt support", False, 3),
|
|||
|
|
]
|
|||
|
|
for name, price, period, features, highlighted, order in fees:
|
|||
|
|
db.add(FeePlan(name=name, price=price, period=period, features=features, highlighted=highlighted, sort_order=order))
|
|||
|
|
|
|||
|
|
testimonials = [
|
|||
|
|
("Aarav Sharma", "Student", "Swati ma'am made maths so easy to understand. My marks jumped from 62 to 91 in just one year!", 5, 1),
|
|||
|
|
("Meena Gupta", "Parent", "My daughter actually looks forward to her classes now. The personal attention is wonderful.", 5, 2),
|
|||
|
|
("Rohan Verma", "Student", "The best teacher I've ever had. She explains every concept until it truly clicks.", 5, 3),
|
|||
|
|
("Kavita Nair", "Parent", "Very disciplined, caring and result-oriented. Highly recommend to every parent.", 5, 4),
|
|||
|
|
]
|
|||
|
|
for author, relation, quote, rating, order in testimonials:
|
|||
|
|
db.add(Testimonial(author_name=author, relation=relation, quote=quote, rating=rating, sort_order=order))
|
|||
|
|
|
|||
|
|
db.add(
|
|||
|
|
SiteSettings(
|
|||
|
|
address="123 Vidya Nagar, Near City Library, Your City, 000000",
|
|||
|
|
phone="+91 90000 00000",
|
|||
|
|
email="hello@swatiyadav.com",
|
|||
|
|
hours="Mon–Sat, 9:00 AM – 8:00 PM",
|
|||
|
|
whatsapp="https://wa.me/919000000000",
|
|||
|
|
instagram="https://instagram.com/",
|
|||
|
|
youtube="https://youtube.com/",
|
|||
|
|
facebook="https://facebook.com/",
|
|||
|
|
footer_text="Empowering students with knowledge, confidence and care.",
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
db.add(
|
|||
|
|
Enquiry(
|
|||
|
|
name="Sample Parent",
|
|||
|
|
email="parent@example.com",
|
|||
|
|
phone="+91 98765 43210",
|
|||
|
|
subject_interest="Mathematics",
|
|||
|
|
message="Hi, I'd like to know the timings for the Class 10 maths batch. Thank you!",
|
|||
|
|
is_read=False,
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
reviews = [
|
|||
|
|
("Priya Deshmukh", "Parent", 5, "The weekly progress updates keep me informed and my son is far more confident now.", True),
|
|||
|
|
("Ananya Rao", "Student", 5, "Doubts are always cleared patiently. Highly recommend her classes!", False),
|
|||
|
|
]
|
|||
|
|
for author, relation, rating, message, approved in reviews:
|
|||
|
|
db.add(Review(author_name=author, relation=relation, rating=rating, message=message, is_approved=approved))
|
|||
|
|
|
|||
|
|
db.commit()
|
|||
|
|
|
|||
|
|
_seed_students(db)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _seed_students(db: Session) -> None:
|
|||
|
|
"""Sample students, schedules and a little attendance for this month."""
|
|||
|
|
students_data = [
|
|||
|
|
# name, phone, parent_name, parent_phone, address, subject, fee, slots
|
|||
|
|
(
|
|||
|
|
"Aarav Sharma", "+91 98111 22233", "Rakesh Sharma", "+91 98111 00011",
|
|||
|
|
"12 Green Park, Your City", "Mathematics · Class 10", 1500,
|
|||
|
|
[(0, "16:00", "17:00"), (2, "16:00", "17:00"), (4, "16:00", "17:00")],
|
|||
|
|
),
|
|||
|
|
(
|
|||
|
|
"Isha Verma", "+91 98222 33344", "Sunita Verma", "+91 98222 00022",
|
|||
|
|
"45 Lake View, Your City", "Physics · Class 12", 2500,
|
|||
|
|
[(1, "17:30", "18:30"), (3, "17:30", "18:30")],
|
|||
|
|
),
|
|||
|
|
(
|
|||
|
|
"Rohan Gupta", "+91 98333 44455", "Manoj Gupta", "+91 98333 00033",
|
|||
|
|
"78 Rose Villa, Your City", "Chemistry · Class 11", 2500,
|
|||
|
|
[(0, "18:00", "19:00"), (3, "18:00", "19:00"), (5, "10:00", "11:00")],
|
|||
|
|
),
|
|||
|
|
(
|
|||
|
|
"Ananya Nair", "+91 98444 55566", "Kavita Nair", "+91 98444 00044",
|
|||
|
|
"9 Sunrise Apartments, Your City", "English · Class 8", 1500,
|
|||
|
|
[(1, "16:00", "17:00"), (4, "16:00", "17:00")],
|
|||
|
|
),
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
student_objs: list[Student] = []
|
|||
|
|
for name, phone, pname, pphone, addr, subj, fee, slots in students_data:
|
|||
|
|
s = Student(
|
|||
|
|
name=name, phone=phone, parent_name=pname, parent_phone=pphone,
|
|||
|
|
address=addr, subject=subj, monthly_fee=fee, start_date=date.today(),
|
|||
|
|
is_active=True, notes="",
|
|||
|
|
)
|
|||
|
|
db.add(s)
|
|||
|
|
db.flush()
|
|||
|
|
for weekday, start, end in slots:
|
|||
|
|
db.add(ScheduleSlot(student_id=s.id, weekday=weekday, start_time=start, end_time=end))
|
|||
|
|
student_objs.append(s)
|
|||
|
|
|
|||
|
|
# A little attendance for the first few days of the current month.
|
|||
|
|
today = date.today()
|
|||
|
|
statuses = ["present", "present", "absent", "present", "off"]
|
|||
|
|
for day in range(1, 6):
|
|||
|
|
try:
|
|||
|
|
d = date(today.year, today.month, day)
|
|||
|
|
except ValueError:
|
|||
|
|
continue
|
|||
|
|
if d > today:
|
|||
|
|
break
|
|||
|
|
for i, s in enumerate(student_objs):
|
|||
|
|
db.add(Attendance(student_id=s.id, date=d, status=statuses[(day + i) % len(statuses)]))
|
|||
|
|
|
|||
|
|
# A sample holiday on the 1st (kept only if not in the future).
|
|||
|
|
try:
|
|||
|
|
first = date(today.year, today.month, 1)
|
|||
|
|
db.add(DayMark(date=first, kind="holiday", note="Month start holiday"))
|
|||
|
|
except ValueError:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
db.commit()
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _ensure_columns() -> None:
|
|||
|
|
"""Add columns introduced after a table was first created.
|
|||
|
|
|
|||
|
|
``create_all`` never alters existing tables, so on databases that already
|
|||
|
|
have the ``attendance`` table we add the newer ``note`` column by hand.
|
|||
|
|
"""
|
|||
|
|
inspector = inspect(engine)
|
|||
|
|
if "attendance" not in inspector.get_table_names():
|
|||
|
|
return
|
|||
|
|
cols = {c["name"] for c in inspector.get_columns("attendance")}
|
|||
|
|
if "note" not in cols:
|
|||
|
|
default = "''" if engine.dialect.name != "sqlite" else "''"
|
|||
|
|
with engine.begin() as conn:
|
|||
|
|
conn.execute(
|
|||
|
|
text(f"ALTER TABLE attendance ADD COLUMN note TEXT NOT NULL DEFAULT {default}")
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def init_db() -> None:
|
|||
|
|
Base.metadata.create_all(bind=engine)
|
|||
|
|
_ensure_columns()
|
|||
|
|
db = SessionLocal()
|
|||
|
|
try:
|
|||
|
|
if db.query(AdminUser).count() == 0:
|
|||
|
|
_seed(db)
|
|||
|
|
finally:
|
|||
|
|
db.close()
|