200 lines
7.9 KiB
Python
200 lines
7.9 KiB
Python
"""SQLAlchemy ORM models for the SwatiYadav tuition website.
|
|||
|
|
|
||
|
|
Every public section is a table so the teacher can edit it from admin. The
|
||
|
|
public site reads these; the admin panel writes them.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from datetime import date, datetime, timezone
|
||
|
|
|
||
|
|
from sqlalchemy import (
|
||
|
|
Boolean,
|
||
|
|
Date,
|
||
|
|
DateTime,
|
||
|
|
ForeignKey,
|
||
|
|
Integer,
|
||
|
|
String,
|
||
|
|
Text,
|
||
|
|
UniqueConstraint,
|
||
|
|
)
|
||
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||
|
|
|
||
|
|
from app.db import Base
|
||
|
|
|
||
|
|
|
||
|
|
def _now() -> datetime:
|
||
|
|
return datetime.now(timezone.utc)
|
||
|
|
|
||
|
|
|
||
|
|
class AdminUser(Base):
|
||
|
|
__tablename__ = "admin_user"
|
||
|
|
|
||
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||
|
|
username: Mapped[str] = mapped_column(String(80), unique=True, nullable=False)
|
||
|
|
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
|
||
|
|
|
||
|
|
|
||
|
|
class Profile(Base):
|
||
|
|
__tablename__ = "profile"
|
||
|
|
|
||
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||
|
|
name: Mapped[str] = mapped_column(String(160), default="")
|
||
|
|
tagline: Mapped[str] = mapped_column(String(255), default="")
|
||
|
|
bio: Mapped[str] = mapped_column(Text, default="")
|
||
|
|
photo_url: Mapped[str] = mapped_column(Text, default="")
|
||
|
|
qualifications: Mapped[str] = mapped_column(Text, default="")
|
||
|
|
experience_years: Mapped[int] = mapped_column(Integer, default=0)
|
||
|
|
students_taught: Mapped[int] = mapped_column(Integer, default=0)
|
||
|
|
achievements: Mapped[str] = mapped_column(Text, default="")
|
||
|
|
hero_cta: Mapped[str] = mapped_column(String(120), default="Book a free demo")
|
||
|
|
|
||
|
|
|
||
|
|
class Subject(Base):
|
||
|
|
__tablename__ = "subject"
|
||
|
|
|
||
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||
|
|
name: Mapped[str] = mapped_column(String(120), default="")
|
||
|
|
level: Mapped[str] = mapped_column(String(120), default="")
|
||
|
|
description: Mapped[str] = mapped_column(Text, default="")
|
||
|
|
mode: Mapped[str] = mapped_column(String(60), default="Online & Offline")
|
||
|
|
icon: Mapped[str] = mapped_column(String(20), default="📘")
|
||
|
|
sort_order: Mapped[int] = mapped_column(Integer, default=0)
|
||
|
|
|
||
|
|
|
||
|
|
class Course(Base):
|
||
|
|
__tablename__ = "course"
|
||
|
|
|
||
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||
|
|
title: Mapped[str] = mapped_column(String(160), default="")
|
||
|
|
subject: Mapped[str] = mapped_column(String(120), default="")
|
||
|
|
description: Mapped[str] = mapped_column(Text, default="")
|
||
|
|
duration: Mapped[str] = mapped_column(String(120), default="")
|
||
|
|
mode: Mapped[str] = mapped_column(String(60), default="Online & Offline")
|
||
|
|
highlights: Mapped[str] = mapped_column(Text, default="") # newline-separated
|
||
|
|
sort_order: Mapped[int] = mapped_column(Integer, default=0)
|
||
|
|
|
||
|
|
|
||
|
|
class FeePlan(Base):
|
||
|
|
__tablename__ = "fee_plan"
|
||
|
|
|
||
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||
|
|
name: Mapped[str] = mapped_column(String(120), default="")
|
||
|
|
price: Mapped[str] = mapped_column(String(60), default="")
|
||
|
|
period: Mapped[str] = mapped_column(String(60), default="per month")
|
||
|
|
features: Mapped[str] = mapped_column(Text, default="") # newline-separated
|
||
|
|
highlighted: Mapped[bool] = mapped_column(Boolean, default=False)
|
||
|
|
sort_order: Mapped[int] = mapped_column(Integer, default=0)
|
||
|
|
|
||
|
|
|
||
|
|
class Testimonial(Base):
|
||
|
|
__tablename__ = "testimonial"
|
||
|
|
|
||
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||
|
|
author_name: Mapped[str] = mapped_column(String(120), default="")
|
||
|
|
relation: Mapped[str] = mapped_column(String(60), default="Student")
|
||
|
|
quote: Mapped[str] = mapped_column(Text, default="")
|
||
|
|
rating: Mapped[int] = mapped_column(Integer, default=5)
|
||
|
|
sort_order: Mapped[int] = mapped_column(Integer, default=0)
|
||
|
|
|
||
|
|
|
||
|
|
class SiteSettings(Base):
|
||
|
|
__tablename__ = "site_settings"
|
||
|
|
|
||
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||
|
|
address: Mapped[str] = mapped_column(Text, default="")
|
||
|
|
phone: Mapped[str] = mapped_column(String(60), default="")
|
||
|
|
email: Mapped[str] = mapped_column(String(160), default="")
|
||
|
|
hours: Mapped[str] = mapped_column(String(255), default="")
|
||
|
|
whatsapp: Mapped[str] = mapped_column(String(160), default="")
|
||
|
|
instagram: Mapped[str] = mapped_column(String(160), default="")
|
||
|
|
youtube: Mapped[str] = mapped_column(String(160), default="")
|
||
|
|
facebook: Mapped[str] = mapped_column(String(160), default="")
|
||
|
|
footer_text: Mapped[str] = mapped_column(Text, default="")
|
||
|
|
|
||
|
|
|
||
|
|
class Review(Base):
|
||
|
|
__tablename__ = "review"
|
||
|
|
|
||
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||
|
|
author_name: Mapped[str] = mapped_column(String(160), default="")
|
||
|
|
relation: Mapped[str] = mapped_column(String(60), default="Student")
|
||
|
|
rating: Mapped[int] = mapped_column(Integer, default=5)
|
||
|
|
message: Mapped[str] = mapped_column(Text, default="")
|
||
|
|
is_approved: Mapped[bool] = mapped_column(Boolean, default=False)
|
||
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
|
||
|
|
|
||
|
|
|
||
|
|
class Enquiry(Base):
|
||
|
|
__tablename__ = "enquiry"
|
||
|
|
|
||
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||
|
|
name: Mapped[str] = mapped_column(String(160), default="")
|
||
|
|
email: Mapped[str] = mapped_column(String(160), default="")
|
||
|
|
phone: Mapped[str] = mapped_column(String(60), default="")
|
||
|
|
subject_interest: Mapped[str] = mapped_column(String(160), default="")
|
||
|
|
message: Mapped[str] = mapped_column(Text, default="")
|
||
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
|
||
|
|
is_read: Mapped[bool] = mapped_column(Boolean, default=False)
|
||
|
|
|
||
|
|
|
||
|
|
# ---- Student management (admin-only, private) ----
|
||
|
|
|
||
|
|
|
||
|
|
class Student(Base):
|
||
|
|
__tablename__ = "student"
|
||
|
|
|
||
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||
|
|
name: Mapped[str] = mapped_column(String(160), default="")
|
||
|
|
phone: Mapped[str] = mapped_column(String(60), default="")
|
||
|
|
parent_name: Mapped[str] = mapped_column(String(160), default="")
|
||
|
|
parent_phone: Mapped[str] = mapped_column(String(60), default="")
|
||
|
|
address: Mapped[str] = mapped_column(Text, default="")
|
||
|
|
subject: Mapped[str] = mapped_column(String(160), default="")
|
||
|
|
monthly_fee: Mapped[int] = mapped_column(Integer, default=0)
|
||
|
|
start_date: Mapped[date] = mapped_column(Date, default=date.today)
|
||
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
||
|
|
notes: Mapped[str] = mapped_column(Text, default="")
|
||
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
|
||
|
|
|
||
|
|
slots: Mapped[list["ScheduleSlot"]] = relationship(
|
||
|
|
back_populates="student",
|
||
|
|
cascade="all, delete-orphan",
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class ScheduleSlot(Base):
|
||
|
|
__tablename__ = "schedule_slot"
|
||
|
|
|
||
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||
|
|
student_id: Mapped[int] = mapped_column(
|
||
|
|
ForeignKey("student.id", ondelete="CASCADE"), nullable=False, index=True
|
||
|
|
)
|
||
|
|
weekday: Mapped[int] = mapped_column(Integer, default=0) # 0=Mon .. 6=Sun
|
||
|
|
start_time: Mapped[str] = mapped_column(String(5), default="") # "HH:MM"
|
||
|
|
end_time: Mapped[str] = mapped_column(String(5), default="")
|
||
|
|
|
||
|
|
student: Mapped["Student"] = relationship(back_populates="slots")
|
||
|
|
|
||
|
|
|
||
|
|
class Attendance(Base):
|
||
|
|
__tablename__ = "attendance"
|
||
|
|
__table_args__ = (UniqueConstraint("student_id", "date", name="uq_attendance_student_date"),)
|
||
|
|
|
||
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||
|
|
student_id: Mapped[int] = mapped_column(
|
||
|
|
ForeignKey("student.id", ondelete="CASCADE"), nullable=False, index=True
|
||
|
|
)
|
||
|
|
date: Mapped[date] = mapped_column(Date, nullable=False, index=True)
|
||
|
|
status: Mapped[str] = mapped_column(String(10), default="present") # present|absent|off
|
||
|
|
note: Mapped[str] = mapped_column(Text, default="") # why absent / off
|
||
|
|
|
||
|
|
|
||
|
|
class DayMark(Base):
|
||
|
|
__tablename__ = "day_mark"
|
||
|
|
|
||
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||
|
|
date: Mapped[date] = mapped_column(Date, unique=True, nullable=False, index=True)
|
||
|
|
kind: Mapped[str] = mapped_column(String(12), default="holiday") # holiday|class_off
|
||
|
|
note: Mapped[str] = mapped_column(String(255), default="")
|