KFk3{LK^sA$FkXJ
literal 0
HcmV?d00001
diff --git a/dao/__pycache__/class_dao.cpython-314.pyc b/dao/__pycache__/class_dao.cpython-314.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1e2d449d0f9b507c0d36cf1b3a905527129f53e7
GIT binary patch
literal 8980
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
LJlxeY+ROeIa%CeQ
literal 0
HcmV?d00001
diff --git a/dao/__pycache__/counselor_dao.cpython-314.pyc b/dao/__pycache__/counselor_dao.cpython-314.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0ba5e7e9a031159d8cb64f5f0503c838bb42c589
GIT binary patch
literal 5297
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
literal 0
HcmV?d00001
diff --git a/dao/__pycache__/employments.cpython-314.pyc b/dao/__pycache__/employments.cpython-314.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..c7982129af26c02bf2e7d309b268a931729c8ff0
GIT binary patch
literal 5183
zcmcH-|8Eo5{hfXG**@F(4uk}fHpRgsK=?K?Xc>r=+M!U-MXm}St{Y#7!Lh^Lr75VI
zg2uL3BML~C=(ea739XKl4%$uv71aF$`vGGlTPIK^jga-HIReDApZ58@^W8bY1k`CC
z>E8Rid!PHf&-=X3w|DE@E(Zbl&&}-d^G-tkfddW9W#rLkfQ*u(B*^%QkCB-+#7%ya
z&n%lMZuYZ2i)^7d>$mzinWMPHZ}Zt@yU!sz=-BG#eNNd)anA4Zxn*}73EGyEpnW+>
z)XFu%h9D0kCyo+~TpM)3$W73WMTUoBQCR>wHkyn@
zpm+HW9SXepai2h$_c5>*J_sy4sHsQY(2SB0Icn-TOGpgfwUVDR15A(!!;A_+Q`n^8
z8%-T#%KSZ!6;td(#UZIOMdGn3lZb2;vm}eKy0t$g4n}1Omwff{c4&XLBfKvv$?bar
z{_w-EfA#RQi?d(;X)beV?$4L!&i`iao11etFNI^#R9G;!y-Wh#}>G2U#K|^iVm`5x~kY;OHQhim|LMj|>Ex!WmVGemY#d303
z5KoCHA80(DkOfh+V;DUP@li3tfo7GjE}KQ2mOI-(n9#TsZNT+ZVW8<5<6OpqhS
zS&FUjl4^MmAfu$nyC!M^zP-SA=J?KOcKXQGqfrjirfnhW<3SBET3^ZsqH2fS2
zwt~W{eYt>#KIh})TU{JoTXl9(6E;=4fELCv(=Q$&_A%%W*Wv_B@5t>t%PsLY#Co)
z;&5am?qwNaJW{gb$Wb26^Dv^i@2fOm-3dMc3wW^;80ypvG@zS4NLmJi%1~l8cQELx
z%~rZW08L~ORD2DAe=(HZsO+>umJrifCW&pqz!tJp<*5gVY7zy|RTe>z2{BGcESt@m
zt7<*0X?m$Obt4YcE@BSD=d5XZ?wV$yV~PD4YoXmOf{?ifJg`E3_J82T_WWo8FG~O&
ze-TuKFw-H0S>xr!r83eD4kzSzq%W0>#W4YkAU>f!r~oobO0~q!
zPuSA7%<{=~d7Ed(UY~Bx?7YH#;kfLWK5;cUmCUujp0~d-W3Nds%kYI-Pp;OJWplM%
zdHed3g;=aloy6uSI$aaKv@i4C#lsg4UyNLce10-ppJlUqK7T*ov@!4O9%E)WUZXmK
z6on>luF0F-lZ|CV*EVI><(qz-cWxhJik$lnw{ih#mdllyEIr9PC$^=x{cii{K{Y0z
zpOk%tppDRQ{6h)?uh|HRimeaJ5~v46wCtsV-5Qve@K`a}nVFt>Y%*_^u)Fdu9jSuq
z4hpmw$f%(IrXRj2gBt;3$>620@P7?n`YM4c!Aq?ZG)Jr!0xBGbIq0=5@*43qP>A6#
zibFjI%uV82n9@>z7vKw0zY`roKeUZ7O%KKo2qb=|TeaNDBYCHH#{P9eIz@40DJL
zv3l;R*RUfyz}33jI5v&{d>yAOv`z@+klXO1B7rLg#!S18!C=
z8|!Agl-Y)Ls%p7zq1i+JwCFn6k2(lrc}gL~fTa4qw70OA>N=>t@2YzZlI`XC2+n|1
z%UK~-+hH3y;0j~Dq7Y<)rl5IyS*ih)P}iD9Lx=zsc`FIBA#+mK#et^kyAj|lfYwq;
z3o^AB+%!xJ2W@zbS*5)-`bwEY=p0sX4_dG~QvLmc7)cEYNyVw+qVQ2%7!f`2xrhf4
z&wz~$V=_?hXb*0T&wlyo>}2*|x6b|Z>h;-QkIjz#arWl7>g?>LZ|5$LKKSe9hi9(B
z?Ua|`t~M+|UX2NgH73Z>cw!iDC9~JRp1t3!$7jPHL?-+316{69=rL7|vZjg(9fCUe8bpwah)Swm_80gX2h0~i$YjKaqYPfo_d%Y)
zOEj%p#T|(4ga%3_{Q^$SP(nTG#c7>-U9U8~-7rs;ftmRk(_2lA&gPh~r|swXA|Q^m
zDR5j#f4+3}_6{~z|D4skIX}15%)%&
zOxz9F6ro3}*6X&{Ve#RFuuJsA6lOjN<3q{JFwA|@a-Y025}|nEwK^9CoJw
literal 0
HcmV?d00001
diff --git a/dao/__pycache__/scores.cpython-314.pyc b/dao/__pycache__/scores.cpython-314.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..01790b41f7ad8f40951ac39bf2e60bcfb610358f
GIT binary patch
literal 8719
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
W_vRr%bBComCPx(k$bnNGUl4Z@Jd(a4uf|C
z(|MLP9F9+(`15O+FZL&g4DK91ACD#9{(AlGy<5AFe=e`yFF$!u{&Bhd+w=1C`$^Nt
zCo6Y$&n~!4sg#k+vVA0HziI19l9)iwJcCQzYA3
z^ACOB_pJ_ZXzg2Cq@+cD8h$wXVD#y!Qs=;i_RW?ySkeYJv`g#CB^HJw|7DgijEyke
zZA{w+=c$~LwHU@DN|^?WmmIocg*|X_B;cGF9NVzTCAkS
zHng#IW$aUgF{MNwfP{r~`S*Q5muoekD>eR%0{ua)Aup2FZSs>;Bmlblk6D1F&ZEMr
z1ESI+?{Qk4-^1n6pjcjTv24b#3Zds6){swFHNv}PV2UJJt@mp!30a3`+gs6|RiGQ7
zW*sFIzlu8`HlOPRekBVzzE?*d-GH%?3JBls%Nx5-e|hulPE=t>VDBo`$hZLx-LAkv
zg4RKjn5G*@=d;<2;|7^E%`83xA+ELr)(&c3LIrcyJT?T5EuahNxYsDcX=Y&ut-!!K
zK`a8?15Ap_SgH%gDwSV@qSmj4qC@%x@~K+geL;
zLPUEa8g#>a&Nl4yjFnyJhK}$316s0q)3UpL*fTl!8K5_!KZzt3HmnhGjb~(N01g#w
l>m2(MG!ZL~p6$t$(j9VQhje`iV88zm!ty@|K_ZCLe*ybPQJVk&
literal 0
HcmV?d00001
diff --git a/dao/__pycache__/student_dao.cpython-314.pyc b/dao/__pycache__/student_dao.cpython-314.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..bb6b6dffcff36c6bbb41605338c0c459d54c35ab
GIT binary patch
literal 4959
zcmcH-ZA@F&^}Y}L8Eij{2{>diP`(;N0#iOlpebpZj6l*+=e2RB)GKoFBhVO|zV~R9
zk8NhH7NRx^s6-GcTT%&iL|QARKAJ$$!GGj*FabGM)g-4@MKO{5aZ!#P9^fWCj4r$-tLjiBOi^t#D2Fu?R;ee*%rfMIx
zL3yad-xW}`ngiY*|NXB&y8r9xr7yo&p1QI8)#u9-zg+&^o#i_>{J}uPuNrdb1H5JA
ztZwV;3xvZF4G!MdrMwPa_7HeUKaXV^
z1gNQu=3s*;BX34}84jq|B(OV8^_td^)94zP*SwJ`k-cyYneijMk9j%G6s*Jh@bS->
z6K^vQd`#Y)Yz}>_1b(Ik@ba&vy4Q;OIji7>f}#zDG+EJ$ht$5H9FjFTsMvs@;DK~LG;~^4
zaBZCnghC#ZVnte(fszy#^r?8YjQ}-@6WQ&dAyw;3>!PY`g+8ix6M#g?qD{QgeW`ou
zMBL_1+UjGr`q>l7hPGHk+wDEEhS%b@1JNHY3q>hM`Lu1)cKyVSlh;np?Top1#2qc;
zrtbvn8pVIjpLDmx+%5B^^SkF8Z!>e}du~e1)+vVz{lk9pdz&wGk
z6ZrTXz5*r(d`-Y-%Hc!J%md)706udLUjcIhK2{P&_!F=DxJH=c=rkckq>A{nM&)BA
zvK{Xx4bW!t+H79ilH2y8|IGu5&$tC2Taa#g2Hl*YOGRzym;pvi;F8M?e0}N5&Q*<0
z8x1!PGVX%XQ5o|fX9g1}d-0k#(9Ngha1fk{B^}oyx@kD@enbK1aZ1)LgOOobi<}Af
zQn%p|)LRAL024HwLv7crb8M@K}
zbZWJmXe>-w?a@Pv74Bq3Q=+12yf|eopV^eKx>L@|>88o1O9xYS*US$R_Ls0@`{Z_`
zV{gJzL4wJ~SyrGtyk
zit&S~?^TVTj9WIZ))80pqekMaOj%1N_{$wBd+9{uWq+!qY@++}&r)Ts8QWjW8d9S3
z%HXBJi8FDrI%RX9m!3HO$(yOt^6AZ!n`fHhr45ftEKYOu$cmGggo|BcUE>`Iz9Pk&
zFZPV}e0&s@8$hB}m>qCCZ??gx6-r<$0uRRYD-;T5cA
zIojIjQ_<5=pE?7`i%fBH(-ChGESCovn*^`U4#1wa5{c9iAB37)OfSS_G-Imu%4WpS
z2kNGQAw|_dR%-zp6<)<7WrMU8j3leadw@bE)d2{EXwiX+FolYc6dPk=uaJ{Fg10@P0y{V%ivqWOBQ@D_-bGUQ|s=h;Fu9~aK1<-k!Of@Vh-puuRAHw747
zAg*Rd7ofrDbi{&mmOL$erSYgh^z>zuYv2GKQFg*sASe~E2?eryBd;~$3+_kRhJ^>m
z@as&a4ST7*;DxVcwc;p!PG)VU9jH{DJ_F>LHnTS=cE-fcxcFN1@M6Yh1o7g9u?u&F
z>XfT$`oiReap6-N7}Uk{W9RP*RjCU1^y$gd&}oIvq)-_XD(?ynDR;e*(2ffU%a+A_
z-+Ly(yA}%@V^)Fxm&{hdLCUk-ExwLb6du}F5j>YyX^BZJwq|!3?Ts!M)sPz3zv8|ct!Fp_g
zKSp)K!}a#|dPH4RKL`av{e$xG*%tUKV*Dp@(yXv0^Rr_p3_naz>iQ-XIRk1g2~~>*
z1_ec`;C<6KDb3y#41EgPvVy0*Md8x@Jf691_OD;V<$wDC)-fr>1_W2Mf*`s
literal 0
HcmV?d00001
diff --git a/dao/__pycache__/teachers_dao.cpython-314.pyc b/dao/__pycache__/teachers_dao.cpython-314.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f9e950dc5d5fa1e39a9650ba956221d7ea661830
GIT binary patch
literal 7189
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
literal 0
HcmV?d00001
diff --git a/dao/analysis.py b/dao/analysis.py
new file mode 100644
index 0000000..cc83a45
--- /dev/null
+++ b/dao/analysis.py
@@ -0,0 +1,125 @@
+from fastapi import Query,Depends
+from sqlalchemy import func, case
+from model.student_info import Student
+from model.employments import Employments
+from model.scores import Scores
+from sqlalchemy.orm import Session
+
+#student_info学生基本信息
+#动态年龄范围查询:支持用户输入年龄阈值及比较条件(如大于、小于、等于、区间等),动态查询符合条件的学员信息
+def student_info_age(db: Session,min_age:int ,max_age:int ,operator:str,):
+ if operator not in("大于","大于等于","小于","小于等于","区间"):
+ raise ValueError("输入比较格式不正确,请在规定范围内选择输入")
+ else:
+ q = db.query(Student)
+ if operator == "大于":
+ q = q.filter(Student.age > min_age)
+ elif operator == "大于等于":
+ q = q.filter(Student.age >= min_age)
+ elif operator == "小于":
+ q = q.filter(Student.age < min_age)
+ elif operator == "小于等于":
+ q = q.filter(Student.age <= min_age)
+ else: # 区间
+ q = q.filter(Student.age >= min_age, Student.age <= max_age)
+ print(q.all())
+ return q.all()
+
+#多维度班级统计:统计每个班级的总人数,以及按性别(男、女)细分的人数分布
+def student_info_class(db: Session):
+ s1 = (db.query(Student.class_name,
+ func.count(Student.id).label("总人数"),
+ func.sum(case((Student.gender=="男",1),else_=0)).label("男"),
+ func.sum(case((Student.gender=="女",1),else_=0)).label("女"))
+ .group_by(Student.class_name).all())
+
+ return [
+ {
+ "class_name": row.class_name,
+ "总人数": row.总人数,
+ "男": row.男,
+ "女": row.女,
+ }for row in s1
+ ]
+
+
+
+ #student_score学生成绩
+#查询每次考试成绩都在输入分数线(如80分)以上的学生的编号、姓名和成绩。
+def student_score(db: Session,s_n : int):
+ s1 = db.query(Scores.id).group_by(Scores.id).having(func.min(Scores.score)>=s_n).all()
+ s1_ids = [row[0] for row in s1]
+ if not s1_ids:
+ return []
+ s2 = (db.query(Student.id,Student.name,Scores.exam_round,Scores.score)
+ .join(Student, Scores.id == Student.id)
+ .filter(Scores.id.in_(s1_ids)).all())
+ return [{"id": i.id, "name": i.name, "exam_round": i.exam_round, "score": i.score}
+ for i in s2]
+#查询有输入指定次数(如两次(包含两次))以上不及格的学生的姓名、班级和不及格成绩明细。
+def student_score60(db: Session,n : int ):
+ s1 = (db.query(Scores.id).group_by(Scores.id)
+ .having(func.sum(case((Scores.score < 60, 1), else_=0)) >=n)
+ .all())
+ s1_ids = [row[0] for row in s1]
+ if not s1_ids:
+ return []
+ s2 = (db.query(Student.name,Student.class_name,Scores.exam_round,Scores.score)
+ .join(Student, Scores.id == Student.id)
+ .filter(Scores.id.in_(s1_ids)).all())
+ return [{"id": i.name, "name": i.class_name, "exam_round": i.exam_round, "score": i.score}
+ for i in s2]
+#统计每次考试每个班级的平均分,并支持按分数从高到低或从低到高动态排序。
+def student_score_class(db: Session,order: str = "desc"):
+ s1 = (db.query(Scores.exam_round,
+ Student.class_name,
+ func.avg(Scores.score).label("平均分"),).join(Student, Scores.id == Student.id)
+ .group_by(Student.class_name,Scores.exam_round))
+ if order == "desc":
+ res = s1.order_by(func.avg(Scores.score).desc()).all()
+ else:
+ res = s1.order_by(func.avg(Scores.score).asc()).all()
+ return [row._asdict() for row in res]
+
+
+ #student_employments
+#查询查询就业薪资排名前n的学生
+def student_employments_salary(db: Session,n:int):
+ s1 = db.query(Employments).order_by(Employments.sal.desc()).limit(n).all()
+ return s1
+
+#统计每个学生的就业时长
+def student_employments_time(db: Session):
+ time = []
+ all_ = db.query(Employments).all()
+ for employments in all_:
+ if employments.offer_review is None:
+ time.append({"name":employments.student.name,
+ "class_name": employments.student.class_name,
+ "status": "未就业"})
+ elif employments.offer_open and employments.offer_review:
+ time.append({"name":employments.student.name,
+ "class_name": employments.student.class_name,
+ "time": (employments.offer_review-employments.offer_open).days})
+ return time
+
+#统计每个班级的平均就业时长(仅统计进入就业阶段,即有就业开放时间的学生)
+def student_employments_class_time(db: Session):
+ s1 = (db.query(Student.class_name,
+ func.avg(
+ func.unix_timestamp(Employments.offer_review) -
+ func.unix_timestamp(Employments.offer_open))
+ .label("avg_time"))
+ .join(Student, Employments.id == Student.id)
+ .filter(Employments.offer_review.isnot(None))
+ .group_by(Student.class_name).all())
+ time = []
+ for i in s1:
+ days = round(float(i.avg_time) / 86400, 2) if i.avg_time else 0
+ time.append({
+ "class_name": i.class_name,
+ "平均时长(天)": days
+ })
+ return time
+
+
diff --git a/dao/class_dao.py b/dao/class_dao.py
new file mode 100644
index 0000000..a8bb657
--- /dev/null
+++ b/dao/class_dao.py
@@ -0,0 +1,178 @@
+from sqlalchemy.orm import Session
+from typing import Optional, List, Tuple
+
+from model.classes_model import Classes
+from model.teachers import Teacher
+from schemas.class_scheme import (
+ ClassCreate,
+ ClassUpdateBase,
+ ClassHeadTeacherUpdate,
+ ClassTeachersUpdate,
+ ClassDelete,
+)
+
+
+class ClassDAO:
+
+ # ---------- 创建 ----------
+ @staticmethod
+ def create(db: Session, req: ClassCreate) -> Classes:
+ # 1) 班级名唯一
+ exists = (
+ db.query(Classes)
+ .filter(Classes.class_name == req.class_name)
+ .first()
+ )
+ if exists:
+ raise ValueError("班级名称已存在")
+
+ # 2) 班主任存在
+ head_teacher = (
+ db.query(Teacher).filter(Teacher.t_id == req.head_teacher_id).first()
+ )
+ if not head_teacher:
+ raise ValueError("班主任不存在")
+
+ # 3) 授课老师全部存在
+ teachers: List[Teacher] = []
+ if req.teacher_ids:
+ teacher_ids = set(req.teacher_ids)
+ teachers = (
+ db.query(Teacher).filter(Teacher.t_id.in_(teacher_ids)).all()
+ )
+ if len(teachers) != len(teacher_ids):
+ raise ValueError("部分授课老师不存在")
+
+ data = req.model_dump(
+ exclude={"teacher_ids"},
+ exclude_none=True,
+ )
+ db_class = Classes(**data)
+ db_class.teachers = teachers
+
+ db.add(db_class)
+ db.commit()
+ db.refresh(db_class)
+ return db_class
+
+
+ @staticmethod
+ def get_by_id(db: Session, class_id: int) -> Optional[Classes]:
+ return (
+ db.query(Classes)
+ .filter(
+ Classes.class_id == class_id,
+ Classes.class_delete == False, # noqa: E712
+ )
+ .first()
+ )
+
+ # ---------- 分页 ----------
+ @staticmethod
+ def list_page(
+ db: Session,
+ page_num: int,
+ page_size: int,
+ class_name: Optional[str] = None,
+ head_teacher_id: Optional[int] = None,
+ ) -> Tuple[int, List[Classes]]:
+ query = db.query(Classes).filter(Classes.class_delete == False) # noqa: E712
+
+ if class_name:
+ query = query.filter(Classes.class_name.like(f"%{class_name}%"))
+ if head_teacher_id is not None:
+ query = query.filter(Classes.head_teacher_id == head_teacher_id)
+
+ total = query.count()
+ rows = (
+ query.order_by(Classes.class_id.desc())
+ .offset((page_num - 1) * page_size)
+ .limit(page_size)
+ .all()
+ )
+ return total, rows
+
+
+ @staticmethod
+ def update_base(
+ db: Session, class_id: int, req: ClassUpdateBase
+ ) -> Optional[Classes]:
+ db_class = ClassDAO.get_by_id(db, class_id)
+ if not db_class:
+ return None
+
+ data = req.model_dump(exclude_unset=True, exclude_none=True)
+
+ # 改班级名要查重
+ new_name = data.get("class_name")
+ if new_name and new_name != db_class.class_name:
+ exists = (
+ db.query(Classes)
+ .filter(
+ Classes.class_name == new_name,
+ Classes.class_id != class_id,
+ )
+ .first()
+ )
+ if exists:
+ raise ValueError("班级名称已存在")
+
+ for k, v in data.items():
+ setattr(db_class, k, v)
+
+ db.commit()
+ db.refresh(db_class)
+ return db_class
+
+ @staticmethod
+ def update_head_teacher(
+ db: Session, class_id: int, req: ClassHeadTeacherUpdate
+ ) -> Optional[Classes]:
+ db_class = ClassDAO.get_by_id(db, class_id)
+ if not db_class:
+ return None
+
+ teacher = (
+ db.query(Teacher).filter(Teacher.t_id == req.head_teacher_id).first()
+ )
+ if not teacher:
+ raise ValueError("班主任不存在")
+
+ db_class.head_teacher_id = req.head_teacher_id
+ db.commit()
+ db.refresh(db_class)
+ return db_class
+
+
+ @staticmethod
+ def update_teachers(
+ db: Session, class_id: int, req: ClassTeachersUpdate
+ ) -> Optional[Classes]:
+ db_class = ClassDAO.get_by_id(db, class_id)
+ if not db_class:
+ return None
+
+ teacher_ids = set(req.teacher_ids)
+ teachers = (
+ db.query(Teacher).filter(Teacher.t_id.in_(teacher_ids)).all()
+ ) if teacher_ids else []
+ if len(teachers) != len(teacher_ids):
+ raise ValueError("部分授课老师不存在")
+
+ db_class.teachers = teachers
+ db.commit()
+ db.refresh(db_class)
+ return db_class
+
+ @staticmethod
+ def batch_toggle_delete(db: Session, req: ClassDelete) -> int:
+ count = (
+ db.query(Classes)
+ .filter(Classes.class_id.in_(req.class_ids))
+ .update(
+ {"class_delete": req.deleted},
+ synchronize_session=False,
+ )
+ )
+ db.commit()
+ return count
\ No newline at end of file
diff --git a/dao/counselor_dao.py b/dao/counselor_dao.py
new file mode 100644
index 0000000..520ce1f
--- /dev/null
+++ b/dao/counselor_dao.py
@@ -0,0 +1,82 @@
+#创建方法
+from datetime import date
+
+from sqlalchemy.orm import sessionmaker, Session
+
+from model.counselor import Counselor
+
+
+#增
+def add_counselor(db:Session,id: int,name:str,c_time:date):
+ c1 = Counselor(
+ id=id,
+ name=name,
+ c_time=c_time,
+ c_de=False
+ )
+ try:
+ db.add(c1)
+ db.commit()
+ db.refresh(Counselor)
+ return "添加成功"
+ except Exception as e:
+ print(e)
+
+#删
+def delete_counselor_id(db:Session,id:int):
+ res=db.query(Counselor).filter(Counselor.id == id).first()
+ res.c_de=True
+ db.commit()
+ return "删除成功"
+
+def delete_counselor_name(db:Session,name:str):
+ db.query(Counselor).filter(Counselor.name==name).delete()
+ db.commit()
+ db.refresh(Counselor)
+ return "删除成功"
+
+#改,根据id修改
+def update_counselor(db:Session,id:int,new_name,new_c_time):
+ counselor=db.query(Counselor).filter(Counselor.id==id,Counselor.c_de==False).first()
+ if not counselor:
+ return {"msg":"没找到顾问信息"}
+ counselor.name=new_name
+ counselor.c_time=new_c_time
+ try:
+ db.commit()
+ db.refresh(counselor)
+ return {"msg":"修改成功"}
+ except Exception as e:
+ print(e)
+ return {"msg": "修改失败"}
+
+#改,根据名字修改
+def update_counselor_name(db:Session,id:int,name:str,c_time:date):
+ if Counselor.name==name:
+ Counselor.id==id
+ Counselor.c_time=c_time
+ db.commit()
+ db.refresh(Counselor)
+ return f"修改成功"
+
+#查,根据id查询
+#根据id查
+def query_counselor(db:Session,id:int):
+ counselor=db.query(Counselor).filter(Counselor.id==id,Counselor.c_de==False).first()
+ if not counselor:
+ return None
+ l2={
+ "id":counselor.id,
+ "name":counselor.name,
+ "t_created":counselor.c_time.strftime("%Y-%m-%d")
+ }
+ return l2
+
+
+#根据名字查询
+def query_counselor_name(db:Session,name:str):
+ res=db.query(Counselor).filter(Counselor.name==name).first()
+ db.commit()
+ db.refresh(Counselor)
+ return res
+#筛选低于60的同学
\ No newline at end of file
diff --git a/dao/employments.py b/dao/employments.py
new file mode 100644
index 0000000..410089f
--- /dev/null
+++ b/dao/employments.py
@@ -0,0 +1,87 @@
+from typing import Optional
+from sqlalchemy.orm import Session
+from model.employments import Employments
+from model.student_info import Student
+from schemas.employments import Emp_Class,Emp_add_update
+from sqlalchemy import and_
+from fastapi import HTTPException
+
+#根据学生id查看信息
+def get_student_id(db: Session, student_id: str):
+ return db.query(Employments).filter(and_(Employments.id == student_id,Employments.is_deleted==True)).first()
+#根据学生id、公司名称、工资范围多条件查询
+def get_student_multi_condition(
+ db: Session,
+ id: Optional[str] = None,
+ name: Optional[str] = None,
+ min_sal: Optional[float] = None,
+ max_sal: Optional[float] = None,
+):
+ # LEFT JOIN student + student_employment
+ q = db.query(Student, Employments)\
+ .outerjoin(Employments, Student.id == Employments.id)\
+ .filter(Employments.is_deleted==True)
+ # if min_sal>max_sal:
+ # raise HTTPException(status_code=404, detail="学生不存在,请先创建学生基础信息")
+ # 条件动态拼接
+ if id:
+ q = q.filter(Student.id.like(f"%{id}%"))#like模糊查询
+ if name:
+ q = q.filter(Employments.emp.like(f"%{name}%"))
+ if min_sal is not None:
+ q = q.filter(Employments.sal >= min_sal)
+ if max_sal is not None:
+ q = q.filter(Employments.sal <= max_sal)
+ return q.all()
+#根据学生班级查看信息
+def get_class_id(db:Session,class_id:str):
+ return (db.query(Student,Employments)
+ .outerjoin(Student,Student.id==Employments.id))\
+ .filter(Student.class_name==class_id).all()
+#增加修改学生信息
+#首先判断是否是已存在的学生,如果不是就增加,反之就修改
+def add_update_employment(db: Session,student_id:str,emp_data: Emp_add_update,student:Student ):
+ emp = get_student_id(db, student_id)
+ if emp:
+ # 更新
+ emp.offer_open = emp_data.offer_open
+ emp.offer_review = emp_data.offer_review
+ emp.emp = emp_data.emp
+ emp.sal = emp_data.sal
+ else:
+ # 新增
+ stu = db.query(Student).filter(Student.id == student_id)
+ if stu:
+ emp = Employments(
+ id=student_id,
+ offer_open=emp_data.offer_open,
+ offer_review=emp_data.offer_review,
+ emp=emp_data.emp,
+ sal=emp_data.sal
+ )
+ db.add(emp)
+ else:
+ raise HTTPException(status_code=404, detail="学生不存在,请先创建学生基础信息")
+
+ # 业务状态更新 offer优先级更高
+ if emp_data.offer_review is not None:
+ student.status = "已就业"
+ elif emp_data.offer_open is not None:
+ student.status = "就业开放"
+ db.commit()
+ db.refresh(emp)
+ return emp
+
+def delete_employment(db: Session,student_id:str ):
+ emp = get_student_id(db, student_id)
+ if emp:
+ # 删除
+ emp.is_deleted = False
+ db.commit()
+ db.refresh(emp)
+ # else:
+ # raise HTTPException(status_code=404, detail="学生不存在,请先创建学生基础信息")
+ return 1
+
+
+
diff --git a/dao/scores.py b/dao/scores.py
new file mode 100644
index 0000000..5cbed77
--- /dev/null
+++ b/dao/scores.py
@@ -0,0 +1,123 @@
+from sqlalchemy.orm import Session
+
+from model.scores import Scores
+
+def add_score(db: Session, id: str, exam_round: int, score: int): #1.按考核序次录入考核成绩(包含字段:学生ID、考核序次、成绩)
+ try:
+ judgement_id = (db.query(Scores).
+ filter(Scores.id == id,Scores.is_deleted == False).
+ first()) # 通过外键class Student校验学生ID是否存在
+ if not judgement_id:
+ return {"code": 404, "msg": f"学生ID:{id}不存在,请去录入学生信息!"}
+
+ judgement_id_er = (db.query(Scores).
+ filter(Scores.id == id,
+ Scores.exam_round == exam_round,
+ Scores.is_deleted == False).
+ first()) # 本表校验同一学生、同一考核序次是否已录入
+ if judgement_id_er:
+ return {"code": 400, "msg": f"学生ID:{id}第{exam_round}次考核成绩已存在,请勿重复录入!"}
+
+ if score < 60: # 创建分数小于等于60分的记录并警告
+ record = Scores(id=id,
+ exam_round=exam_round,
+ score=score,
+ is_warning=True) #写入学生ID,考核序次,成绩,标记警告
+ db.add(record)
+ db.commit()
+ db.refresh(record)
+ return {"code": 200, "msg": f"学生ID:{id}第{exam_round}次考核成绩{score}分录入成功!成绩小于60分,需要多关注!"}
+ else: # 创建分数大于等于60分的记录
+ record = Scores(id=id,
+ exam_round=exam_round,
+ score=score) #写入学生ID,考核序次,成绩
+ db.add(record)
+ db.commit()
+ db.refresh(record)
+ return {"code": 200, "msg": f"学生ID:{id}第{exam_round}次考核成绩{score}分录入成功!"}
+ except Exception as e:
+ db.rollback()
+ return {"code": 500, "msg": f"考核成绩录入失败: {e}"}
+
+
+def query_id(db: Session, id: str): #2.按学生ID过滤,成绩降序,考核序次升序,返回全部信息
+ try:
+ query_id = (db.query(Scores).
+ filter(Scores.id == id,Scores.is_deleted == False).
+ order_by(Scores.score.desc(),Scores.exam_round.asc()).
+ all())
+ if not query_id:
+ return {"code": 404, "msg": "未查询到相关结果"}
+ data = [{"id": o.id, "exam_round": o.exam_round, "score": o.score}for o in query_id]
+ return {"code": 200, "msg": data}
+ except Exception as e:
+ db.rollback()
+ return {"code": 500, "msg": f"未查询到相关结果:{e}"}
+
+
+def query_er(db: Session, exam_round: int): #3.按考核序次过滤,成绩降序,学生ID升序,返回全部信息
+ try:
+ query_er = (db.query(Scores).
+ filter(Scores.exam_round == exam_round,Scores.is_deleted == False).
+ order_by(Scores.score.desc(),Scores.id.asc()).
+ all())
+ if not query_er:
+ return {"code": 404, "msg": "未查询到相关结果"}
+ data = [{"id": p.id, "exam_round": p.exam_round, "score": p.score}for p in query_er]
+ return {"code": 200, "msg": data}
+ except Exception as e:
+ db.rollback()
+ return {"code": 500, "msg": f"未查询到相关结果: {e}"}
+
+def query_sc(db: Session, score: int): #4.按成绩过滤,学生ID升序,考核序次升序,返回全部信息
+ try:
+ query_sc = (db.query(Scores).
+ filter(Scores.score == score,Scores.is_deleted == False).
+ order_by(Scores.id.asc(),Scores.exam_round.asc()).
+ all())
+ if not query_sc:
+ return {"code": 404, "msg": "未查询到相关结果"}
+ data = [{"id": q.id, "exam_round": q.exam_round, "score": q.score}for q in query_sc]
+ return {"code": 200, "msg": data}
+ except Exception as e:
+ db.rollback()
+ return {"code": 500, "msg": f"未查询到相关结果: {e}"}
+
+
+def update_sc(db: Session, id: str, exam_round: int, score: int): #5.根据学生ID、考核序次修改成绩
+ try:
+ update_sc = (db.query(Scores).
+ filter(Scores.id == id,
+ Scores.exam_round == exam_round,
+ Scores.is_deleted == False).
+ first())
+ if not update_sc:
+ return {"code": 404, "msg": "该成绩记录不存在"}
+ update_sc.score = score
+ update_sc.is_warning = score < 60
+ db.commit()
+ db.refresh(update_sc)
+ return {"code": 200, "msg": f"学生ID:{id} 第{exam_round}次成绩已修改为{score}分!"}
+ except Exception as e:
+ db.rollback()
+ return {"code": 500, "msg": f"考核成绩修改失败: {e}"}
+
+
+def delete_sc(db: Session, id: str, exam_round: int): #6.根据学生ID、考核序次删除成绩
+ try:
+ delete_sc = (db.query(Scores).
+ filter(Scores.id == id,
+ Scores.exam_round == exam_round,
+ Scores.is_deleted == False).
+ first())
+ if not delete_sc:
+ return {"code": 404, "msg": "成绩记录不存在"}
+ delete_sc.is_deleted = True # 逻辑删除
+ db.commit()
+ db.refresh(delete_sc)
+ return {"code": 200, "msg": f"学生ID:{id}第{exam_round}次成绩已删除!"}
+ except Exception as e:
+ db.rollback()
+ return {"code": 500, "msg": f"考核成绩删除失败: {e}"}
+
+
diff --git a/dao/student.py b/dao/student.py
new file mode 100644
index 0000000..f576282
--- /dev/null
+++ b/dao/student.py
@@ -0,0 +1,18 @@
+from sqlalchemy.orm import Session
+from model.student_info import Student
+
+# 根据学生id查询学生
+def get_student_by_id(db: Session, student_id: str):
+ return db.query(Student).filter(Student.id == student_id).first()
+
+# 根据班级名称查询该班级全部学生
+def get_student_by_class(db: Session, class_name: str):
+ return db.query(Student).filter(Student.class_name == class_name).all()
+
+# 创建学生
+def create_student(db: Session, student_id: str, student_name: str, class_name: str):
+ new_stu = Student(id=student_id, name=student_name, class_name=class_name, status="待就业")
+ db.add(new_stu)
+ db.commit()
+ db.refresh(new_stu)
+ return new_stu
\ No newline at end of file
diff --git a/dao/student_dao.py b/dao/student_dao.py
new file mode 100644
index 0000000..fb2f914
--- /dev/null
+++ b/dao/student_dao.py
@@ -0,0 +1,104 @@
+
+from sqlalchemy.orm import Session
+from model.student_info import Student
+from schemas.student_info import StudentCreate, StudentUpdate,StudentDate, StudentResponse
+
+
+
+#查询 查询学生信息(按编号、姓名、班级条件筛选)
+#orm动态查询
+def list_students(db: Session,id: str = None,name: str = None,class_name: str = None):
+ # 已逻辑删除的学生
+ query = db.query(Student).filter(Student.is_deleted == False)
+ # 查询学生信息(支持按编号、姓名、班级等条件筛选)。
+ if id:
+ query = query.filter(Student.id == id)
+ if name:
+ query = query.filter(Student.name.like(f"%{name}%"))
+ if class_name:
+ query = query.filter(Student.class_name == class_name)
+ # 执行查询并返回
+ return query.all()
+
+
+#2创建新的学生
+def create_student(db: Session,student: StudentCreate):
+ data = {}
+ #遍历
+ for field_name in student.model_fields:
+ if field_name == "id":
+ val = student.id
+ elif field_name == "name":
+ val = student.name
+ elif field_name == "gender":
+ val = student.gender
+ elif field_name == "class_no":
+ val = student.class_name
+ elif field_name == "major":
+ val = student.major
+ elif field_name == "age":
+ val = student.age
+ elif field_name == "hometown":
+ val = student.hometown
+ else:
+ val = None
+ data[field_name] = val
+ # 赋值给ORM
+ db_student = Student(
+ id=data["id"],
+ name=data["name"],
+ gender=data["gender"],
+ class_name=data["class_name"],
+ major=data.get("major"),
+ age=data.get("age"),
+ hometown=data.get("hometown")
+ )
+ db.add(db_student)
+ db.commit()
+ db.refresh(db_student)
+ return db_student
+
+# 获取特定学生信息
+# 严格按照需求文档:GET /students/{id},从URL路径中获取学生学号
+def get_student(db: Session,id: str):
+ # 查询:id字段等于url传过来的id
+ student = db.query(Student).filter(Student.id == id,).first()
+ if student is None:
+ return None
+ return student
+
+
+# 更新学生信息 /students/{id} id为学号
+def update_student( db: Session,id: str, payload: StudentUpdate):
+ # 根据学号查询学生
+ student = db.query(Student).filter(Student.id == id).first()
+ # 学生是否存在
+ if student is None:
+ return None
+ if payload.name is not None:
+ student.name = payload.name
+ if payload.age is not None:
+ student.age = payload.age
+ if payload.class_name is not None:
+ student.class_name = payload.class_name
+ # 保存到数据库
+ db.commit()
+ db.refresh(student)
+ # 返回更新后的学生
+ return student
+
+
+# 逻辑删除学生。逻辑上删除,实际上未删除
+def delete_student(db: Session,id: str):
+ # 根据学号查询
+ student = db.query(Student).filter(Student.id == id, Student.is_deleted == False).first()
+ # 判断学生是否存在
+ if student is None:
+ return None
+ # 逻辑删除:把is_deleted改成True,不是真的删掉这条记录
+ student.is_deleted = True
+ db.commit()
+ return {"message": f"学生 {id} 已逻辑删除,数据仍保留在库中"}
+
+
+
diff --git a/dao/teachers_dao.py b/dao/teachers_dao.py
new file mode 100644
index 0000000..4cc17bd
--- /dev/null
+++ b/dao/teachers_dao.py
@@ -0,0 +1,141 @@
+from datetime import date
+
+from sqlalchemy.orm import sessionmaker, declarative_base, Session, Query, selectinload
+
+from model.teachers import Teacher
+
+#添加方法
+def add_teachers(db:Session ,t_name:str, t_gender:str, t_phone:str, t_subject:str,t_created:date):
+ t1=Teacher(
+ t_name=t_name,
+ t_gender=t_gender,
+ t_phone=t_phone,
+ t_subject=t_subject,
+ t_created=t_created,
+ t_delete=False
+ )
+ try:
+ db.add(t1)
+ db.commit()
+ db.refresh(t1)
+ return {"msg":"添加成功"}
+ except Exception as e:
+ print(e)
+ return {"msg":"删除成功"}
+
+
+#删除方法
+def del_teachers(db:Session,t_id:int,t_name:str):
+ teacher=db.query(Teacher).filter(Teacher.t_id==t_id,
+ Teacher.t_name==t_name,
+ Teacher.t_delete==False
+ ).first()
+ if not teacher:
+ return {"msg":"没找到老师信息"}
+ #逻辑删除
+ try:
+ teacher.t_delete=True
+ db.commit()
+ return {"msg":"删除成功"}
+ except Exception as e:
+ print("删除异常",e)
+ return {"msg":"删除失败"}
+
+#修改老师
+def update_teachers(db:Session,t_id:int,new_name,new_gender,new_phone,new_subject,new_created):
+ teacher=db.query(Teacher).filter(Teacher.t_id==t_id,Teacher.t_delete==False).first()
+ if not teacher:
+ return {"msg":"没找到老师信息"}
+ teacher.t_name=new_name
+ teacher.t_gender=new_gender
+ teacher.t_phone=new_phone
+ teacher.t_subject=new_subject
+ teacher.t_created=new_created
+ try:
+ db.commit()
+ db.refresh(teacher)
+ return {"msg":"修改成功"}
+ except Exception as e:
+ print(e)
+ return {"msg": "修改失败"}
+
+# 查询所有老师信息
+# 分页
+def search_teachers(db:Session,page:int,size:int):
+ start = (page - 1) * size
+ teachers=db.query(Teacher.t_id,
+ Teacher.t_name,
+ Teacher.t_gender,
+ Teacher.t_phone,
+ Teacher.t_subject,
+ Teacher.t_created).filter(Teacher.t_delete==False)
+ teach=teachers.offset(start).limit(size).all() #跳过多少行,查询当前页数据
+ total=teachers.count()
+ l1=[]
+ #将查出来的值存入列表
+ for i in teach:
+ bb={
+ "t_id":i[0],
+ "t_name":i[1],
+ "t_gender":i[2],
+ "t_phone":i[3],
+ "t_subject":i[4],
+ "t_created":i[5].strftime("%Y-%m-%d") # 将字符串转时间
+ }
+ l1.append(bb)
+ return {
+ "total":total,
+ "page":page,
+ "size":size,
+ "list":l1
+ }
+
+
+
+#查询单个老师信息
+#根据id查
+def search_teachers_by_id(db:Session,t_id:int):
+ teachers=db.query(Teacher).filter(Teacher.t_id==t_id,Teacher.t_delete==False).first()
+ if not teachers:
+ return None
+ l2={
+ "t_id":teachers.t_id,
+ "t_name":teachers.t_name,
+ "t_gender":teachers.t_gender,
+ "t_phone":teachers.t_phone,
+ "t_subject":teachers.t_subject,
+ "t_created":teachers.t_created.strftime("%Y-%m-%d")
+ }
+ return l2
+
+#查看带班信息
+def get_teacher_classes(db:Session,t_id:int):
+ teacher=(db.query(Teacher).filter(Teacher.t_id==t_id,Teacher.t_delete==False)
+ .options(selectinload(Teacher.classes)).first())
+ if not teacher:
+ return None
+
+ l3=[]
+ for i in teacher.classes:
+ l3.append({
+ "class_id":i.class_id,
+ "class_name":i.class_name,
+ "create_time":i.create_time.strftime("%Y-%m-%d"),
+ })
+
+ res={
+ "tid":teacher.t_id,
+ "t_id": teacher.t_id,
+ "t_name": teacher.t_name,
+ "t_gender": teacher.t_gender,
+ "t_phone": teacher.t_phone,
+ "t_subject": teacher.t_subject,
+ "t_created": teacher.t_created.strftime("%Y-%m-%d"),
+ "class_list": l3
+ }
+ return res
+
+
+
+
+
diff --git a/database.py b/database.py
new file mode 100644
index 0000000..1e5ff2a
--- /dev/null
+++ b/database.py
@@ -0,0 +1,35 @@
+import os
+
+from sqlalchemy import create_engine
+from sqlalchemy.orm import declarative_base, sessionmaker
+
+# 优先读取环境变量(Docker 部署用);默认值与原硬编码配置一致,本地开发不受影响
+MYSQL_USER = os.getenv("MYSQL_USER", "root")
+MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD", "123456")
+MYSQL_HOST = os.getenv("MYSQL_HOST", "localhost")
+MYSQL_PORT = os.getenv("MYSQL_PORT", "3306")
+MYSQL_DB = os.getenv("MYSQL_DB", "student_manager")
+
+db_url = f"mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DB}?charset=utf8mb4"
+engine = create_engine(db_url, pool_size=5, pool_pre_ping=True)
+Base = declarative_base()
+SessionLocal = sessionmaker(bind=engine)
+
+def get_db():
+ db = SessionLocal()
+ try:
+ yield db
+ finally:
+ db.close()
+
+def init_db():
+ """导入所有模型并建表,避免任何入口文件漏导入"""
+ import model # noqa: F401 —— 触发 model/__init__.py 注册所有类
+ Base.metadata.create_all(bind=engine)
+
+
+def reset_db():
+ """开发环境重建所有表"""
+ import model # noqa: F401
+ Base.metadata.drop_all(bind=engine)
+ Base.metadata.create_all(bind=engine)
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..4dec57f
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,66 @@
+# ============================================
+# 沃林学生管理系统 - Docker 编排
+# MySQL 8.0 虚拟镜像 + Backend (FastAPI)
+# ============================================
+version: "3.9"
+
+services:
+ # ---------- MySQL 虚拟镜像 ----------
+ mysql:
+ image: mysql:8.0
+ container_name: wolin-sms-mysql
+ restart: unless-stopped
+ environment:
+ # 与 database.py 中的默认凭证保持一致
+ MYSQL_ROOT_PASSWORD: "123456"
+ MYSQL_DATABASE: student_manager
+ TZ: Asia/Shanghai
+ command:
+ - --character-set-server=utf8mb4
+ - --collation-server=utf8mb4_unicode_ci
+ - --default-authentication-plugin=mysql_native_password
+ ports:
+ # 宿主机 3307 -> 容器 3306
+ # (用 3307 是为了避免与本机已安装的 MySQL 冲突)
+ - "3308:3306"
+ volumes:
+ - mysql_data:/var/lib/mysql
+ healthcheck:
+ test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-p123456"]
+ interval: 10s
+ timeout: 5s
+ retries: 10
+ start_period: 30s
+ networks:
+ - wolin-net
+
+ # ---------- 后端应用 ----------
+ backend:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ container_name: wolin-sms-backend
+ restart: unless-stopped
+ depends_on:
+ mysql:
+ condition: service_healthy
+ environment:
+ # 容器内通过服务名 mysql 访问数据库(Docker 内置 DNS)
+ MYSQL_HOST: mysql
+ MYSQL_PORT: "3306"
+ MYSQL_USER: root
+ MYSQL_PASSWORD: "123456"
+ MYSQL_DB: student_manager
+ TZ: Asia/Shanghai
+ ports:
+ - "8001:8001"
+ networks:
+ - wolin-net
+
+networks:
+ wolin-net:
+ driver: bridge
+
+volumes:
+ mysql_data:
+ driver: local
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..7d50830
--- /dev/null
+++ b/main.py
@@ -0,0 +1,62 @@
+import os
+import uvicorn
+from fastapi import FastAPI
+from fastapi.staticfiles import StaticFiles
+from fastapi.middleware.cors import CORSMiddleware
+from database import engine, Base
+
+#导入模型
+from model.classes_model import Classes
+from model.student_info import Student
+from model.scores import Scores
+from model.employments import Employments
+from model.teachers import Teacher
+from model.counselor import Counselor
+
+#导入路由
+from api import student as student_api
+from api import score as score_api
+from api import employment as employment_api
+from api import analysis as analysis_api
+from api import teachers as teachers_api
+from api import class_api
+from api import counselor_api
+
+#创建 FastAPI 应用
+app = FastAPI(title="学生管理系统API", version="1.0")
+
+#配置跨域
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["*"],
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
+
+#注册路由
+app.include_router(student_api.router)
+app.include_router(score_api.router)
+app.include_router(employment_api.router)
+app.include_router(analysis_api.router)
+app.include_router(teachers_api.router)
+app.include_router(class_api.router)
+app.include_router(counselor_api.router)
+
+#挂载前端静态文件
+if not os.path.exists("static"):
+ os.makedirs("static")
+app.mount("/ui", StaticFiles(directory="static", html=True), name="static")
+
+
+@app.get("/")
+def root():
+ return {"message": "欢迎使用沃林学生管理系统,前端请访问 http://127.0.0.1:8001/ui/"}
+
+if __name__ == "__main__":
+ # 建表
+ Base.metadata.create_all(bind=engine)
+ # host 必须为 0.0.0.0 才能在 Docker 容器外访问;本地运行 127.0.0.1:8001 依然有效
+ host = os.getenv("HOST", "0.0.0.0")
+ port = int(os.getenv("PORT", "8001"))
+ uvicorn.run("main:app", host=host, port=port, reload=False)
\ No newline at end of file
diff --git a/model/__init__.py b/model/__init__.py
new file mode 100644
index 0000000..e112641
--- /dev/null
+++ b/model/__init__.py
@@ -0,0 +1,6 @@
+from .classes_model import Classes
+from .student_info import Student
+from .scores import Scores
+from .employments import Employments
+
+__all__ = ["Classes", "Student", "Scores", "Employments"]
\ No newline at end of file
diff --git a/model/__pycache__/__init__.cpython-314.pyc b/model/__pycache__/__init__.cpython-314.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1032efdb5573b1670e555d1bb6f225fcec09a8bc
GIT binary patch
literal 379
zcmdPq@OqG}&J=0u9k*yv6RElUQ7wS`1OKd{8vrSC55y9lLY;LE?wtKtD
z?CeAGbfVPaQw6~*f^A+jK2(an1Pao}KDOkbxq%`gHu$Ebi1;9WvwL@m;2bym&F}j&
z-~7IB=9NrZK`{P#@XZxfMCcd3=!|4rIQS8SI$A=SP(~$z3Z)2*l%h0R5~)~<(O4-?
z<0Xko!`voPmP-km2;^v4DJ5xg7**mUNE0i`xxr5G!*4|ZU92L(94`tXu1T8wMl_TI
zeIn2+|EDiP-h9w>9zl%c0Mb&GWU=i{Gg|rtDrA1+CIyMbCtcgGIZP@$6t7^y;+H6~
zoE0XWb6p!74il#gidk&lShg`!rd@)q73VzOC?Xs_Q!*wp}u6nM}GweG@`}MP}KkUe(v!H6N-Db(QABLKh+|;o++5m{3PD
z#KR^gJ2AdRvxNaB>$+pqu&y&j*K4ln+gwiR`nqq}ZH=nymo4H^+j6kuf;z41l;;}i
z3GZOSo10|$l;HcQtJpC0!~P-?N0V@D$WW(N4U-s0l(3C>B*L4Ar9!~OHiqs#JTmki
z;ZFdQz3|)~Nd?RH9v(|{>0x1I2^zqJ$nZ>vda2catnvQVmAfzOzC7_@tl59D{%)&xu+ewhZ}y(5
zpKtXH+>AHKm&*2gJH=+tME#vsZm4l`d#sszt$rbpx5l@f-4k!^=FZkH{H#9TDBrc3
z>I6g%AMhR2g~)sv
z)Gc#pKI)QMn{v~DdczY!_*2W|8m0z^f(nFOaB9PiPX#7t!d^N{PQ#KvmfiwPzefc@
z_zBHDMsttQ+!J*22^#uCM!CT~F~29upUiwX(-M{IXKp@sdvc&YWs<^_aNYb9@o%vCzW{lu$^ZZW
literal 0
HcmV?d00001
diff --git a/model/__pycache__/counselor.cpython-314.pyc b/model/__pycache__/counselor.cpython-314.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..77b34a7823a07055727389f27641031a5641b69f
GIT binary patch
literal 1262
zcmZ8g&2Jk;6rWx1r`I2(AuUlsZa}oLf^tBWB2aA6#7gZ0Rkc(-pg5z=deSak?>e)y
z2ZsYIA+<-0Kx$5Hdg;dj6yQLm{1ZI{iHyVvwN`JfQUOQa?AoScEdBhx_RahK-tI!F
zm`8BFI(YEKH!?zh3TJ%Ok#T$(j09~UTWX=E#H6Oo<-|VY`l|0l5s4sH+H~9-xEJ)3J
zh+fJ->=-04j+enmkd3Y)vF-n#0tg405$)HrkdZ%Bflh9xv^?Q6?{#WB`UhtMr%P%(
zavIERN_rGyeqwU>;y5-9jiU-cf_|T>>?sfQd-`twp}w#D
zYW`$C{POX;Ps+6?M(r3%&?9uIIxYH%387yeW4JH&gTxh2GSvyyyzWR?f|
z-j&D7p;=2-j;yI~&+bltx3CxQHx8|jlEzVSdeGQ=|FAfptU-2g4s2^aX}r`>advlh
zf8nrDPcHtgNNW8B0-&eImtCq_JRfcQj_+P4-TrHOq*4gtnG~h}iWWoK&2%t24u}cg
zH5Ef=6y6qA+F#J(#&WiqI$Z!)@QvO9c#)GNX^3jSq1qql+*36B%CMyRK=}*7U;6mJ
D0%1n9
literal 0
HcmV?d00001
diff --git a/model/__pycache__/employments.cpython-314.pyc b/model/__pycache__/employments.cpython-314.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..7832be5cc0472084f49c72d3ed0f6e4fe68218ec
GIT binary patch
literal 1282
zcmZWoU1%It6uz^6Gdr_C*_gE`LX3^bQV2ezl!_G0UuUBhnf5`#ZZ4DEn`G?#Z13EO
zY@xDZEm{#*EKT*nMvxlCO6pGuR7xLxthi|Riujoc
zJ?Bs+ts?k-x_Iri-(v{wbcr>ibQbyt+D##B+Sde+TLW#);Chn5u
z5}ug!?7(q(e8%=n#uFzQwcI7HOabPUW7o}$DyndQ0u
zvQ>jPHevdt5*``a#CEXtD>w~QMocmgi_9x5YDg?rj*p;nVg$_(jY}c2ECc(%}DK@F;LC{tF)0Z
zvPU8zPc~=_ut$s>s5;2CX{Tm;b%(gj=Nj1y&T2jjsv6iPGii(
zWx{m|t&gba3z`(iN31H9g`@VbhrJqcc`EcNS+U6LJPO39m=GTIO`9rxwB_R}u?Ztp
zD#nV-+e-61#y*3ttYDKtA1?&J9%K3_fVSX=9v%fV^;~Jj^x2ayyizQ6zkRFw>1yZt
zwce$xy>CA2H81qOyVbjOwFDO=c4xgY1rL2f`-2W<^sAmINW%-oy>liXwr|=
zk7w7;{qn@&JL9dS-k4}-^UdY;*xkd$R(7^A+s+-hqO6Z@oNMK#8!xq$%)2Yih4;>`
zjo*ECs-;XfCfn*jGru;p{zgkZ1`)vJ_55b0l{*fbGml?6x>0InCe9zHqdP=S6w+Mv
z&)BA2Std@Mig^iD7Mg`p3x)f_g`#Kn;A0+jLIsto3GW0GOhvPW7Zs|7Cp`@wSDp_a
u{TMAkC`Q(Q3d}!INs|6VbHAavpV8bO=$Q_B?4hDalTvf^UnKtfHU0yp-FN)}
literal 0
HcmV?d00001
diff --git a/model/__pycache__/scores.cpython-314.pyc b/model/__pycache__/scores.cpython-314.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d9c59857520e9e4551d1c5b39d1ea87a0624d869
GIT binary patch
literal 1446
zcmZuxU1%It6uvY2GqbaQ**2S44YaAhi!~OB2Bj>Trg68@AEqG{lg(wab2piEc4m9$
z4mB0&KtT&4t|(Zg*yN?QU`6z$8bKe$r257j7kC%N+J_W5f&*$S+o>mv7Ou}
zmg6OfNr4_I%cTTM>_mAqjMV5LQe&0GXe7+X6gB=ZDoB5Fvk>#+`)$*$TfS7b7+E0H
zm&R?|B%0+blQt#Bg7pen_T$GGHLQh#;HM}tHD=hBvuHHHj*e>%sfM{j8SfgKKY^k_
z=@rBT6)|ycfJFw74UMp9B{qyIaaDw0WUN=36rl1jn(H4G!Yq{pZ=q+6iXqCB?*5n>
zAM0Vs5bMRVRA8som?~|>Wc~-sR{G!!Sryd8R$NY11`*q)CfW9B#QN0~P?iQ3sz8b3
z$DQCiRS^ATgBo>>F5?rxQd)DFZCF)G>cnEFlMZurP@EA%p9HF8%Qa1H-Xx$)I;m-{
z$$aTp7z9{mUaOwK4ZGn2!^Ej}DF|AI=|Dh}yg3buhg39;_+0#VrGQ-UbSb&t3my?X
z(fhGb2T$Y~2^x&L+X2}ZHu=P@tCu<-UMx@i_1!0K4)8s@{_xJWSFYXq_PzG!s~k-R
zX!~5VvvvjS(_*1mi1?zR`wDqot7B@rmhQ&_viPJp#H=~IsmJb&f}D6^c2aZLu9uFM
zXWL($ZGZYvcx2~`)lT!R&ev<5wTrWLTPNmh$oEKN*_SbvR^;)e}TWn>GmTYXo9&`mA
zF64Q3ORE#eu3!vDyC&CD7@u@Cv+K!XTr;S{Ov56U4em6?jNfYLr+kndhN=y@DARG$Y0%03x{zdu#NXl<65WEJ5g81ic&RhSGcj#u{K=ah)
zi65fteFwbqMk4!uyh%S$K0dhe=JlaxuP2`K#y4_<=cUV!uIKi82RBmNnq%)A@eYJw
zem(azfI&d0-^@IC{`oIoUC$J~m+wY}*phG?aY-NQ6>+$b^cCl%shQP9QeVEuV8BjD
z8z<1u+zT49X+7je)-|9X{)A|vmyix|Ey&N?_2qrxT^8vcFgXtnpO)JZK@jflM#6;f
a3)+7JJ$?i2xD!tcOTxLSzY+fiIsOHJ1B6`w
literal 0
HcmV?d00001
diff --git a/model/__pycache__/student_info.cpython-314.pyc b/model/__pycache__/student_info.cpython-314.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..092ced2e181e40a96404e1faf8d8f2b74a2df00d
GIT binary patch
literal 2319
zcmZuzYfKbZ6u$eKU515ak%ywZ+Qz!YKx0f*qE-RvF0EDvBxwaF!|t%G&d%V>owo36
znm*JfrV_L+Y7}BESgYV8N{cb=kEZ>(NjGVk#5AVvF8rzurtQz3JF^U%wv%w??D@WX
z&OMKNhlb)J8o?ueKY8Q|h0t&0$MWOKDN9|TXy`CvY#|i1VO!9S?Lh~21fAF!bYWMJ
z!c;B6?ICy2gFS}s2o(frOxL1vRF4>EC1PB?^k#?Y6Bjbn^C;k6BtHX`PPIyc8W(k{
z6U#9%s=GrX=A*o+Tjw5_nG3>2^uxV^Q&Sekfqmy)rLwI3|h8Kx`0xJ6kw~
z51SE82`L%R(o-8k93;_;u#G|3ewf0J3M4^F*xBoCMM
zVr;S)E{0;<7QTZNCw2wb1Q%RT_rQV9IntE5_!W6&C(fp?TsOD!(N(hI+`Ywfck{gD
z3`Rm1rxvE?)Bp+g$-RvBKI|%Udm?l3q!|#Lewm)R1(BN4_ivjTB;}6q{hTV`m7Er}
zxrK)#=@a9NW1nZnk1yPu87>VAoTBhbqw!`%F3y|jpVZQ~E@eNxk^N>e``|MuD4gFg
z98g7f=gJL-vbc{6A7uxnK^4v_K!tnagMu_f>Z}CpGRa$as|qi~6s)^j%~0XoK?D#X
zH~hhnc$T_B(GY{W(SBn8*W-o53`p*9<%59O0deNrOdSx%CO3?VbJ)8*H!B-NX~(d8
zV;#wJ$FOH(osssAVS8X_z@gh?5#323on9#ixqBdkeuZp%{j+9}ZOuLHoPz6i?+^84
zZhw%ubPhTQ^OU_gkv;u!_Rd^(?s89Dits{Du2yVJ?3Wq`hjb6i%KV_rvU*8wOO4Bx
zYVfQyfY8t{{^~^Ja=3cxya56`77*i%lJx^?Y0QL+y99$z2DM2Rf6!uB4}H3Fzn=
z+83xLp2^n1GtIIPTos7!W!YmYC*%+x%l5})1q(5e7bSp;SQeAHH*uL9SDBPjUPD9`
z2%U=ZVuY7v^2N{{T$I;615%vF(z~MWi^^O?h1Ro5cmS?(-4*AKNU~nQN7S%!d&^{i
zbssOvk|2<|U?b$xUtS3JyJ?^l%wbiL$vo(EOktsTJmw>^2MqFBA_@pZDdrH;yftZX
zw9UbiYY3y9Wr?p?nB_2haQajjOvu#FvIZ>@3*#^^2rLVYh4d{WEF&G>C6hkL4dw22usDYAufo{UhrWrK2y6t21J%aF5nDU$knZ
zJ+W!DW2!v4^^H`~UafteE;(H}z9B_7Xsz?U@{#^T=jhR?ttsDTEjVAXZmfH}^}?Z<
z##F^_?ag^_>BuXI=SG{y-%EL4*7nSoRE)fn=o=kQHtbH7v}hq%km#D)nW|_8a=vcG
z^(345R8c>~^;ag`<~IC$wT{RB$}uV-U!W&Cl1;5Yz0j82*O&5#wT?e1R9uzVFtaUH
zw1cdwPQ+(JKdLGJL9JsRkZm(>r;2xJul?n;xenT%A|mCws|Nl
zTciXIDeXay-k5fua`6hbUi2(xfOWMU!0A|o6N
z6{7`-NxmK~%7qw_|n
z`p-MlzApp5=;J#2O8r2oq{vE4i9fFX2_-Rv*fAx^29^-ZDk&g1?MaJ7)z*PgiFK95
ze8@}GXr-#sO?;JXdMVXmR>jmPsS=aT1E;d-7=~IlINy>^R#nGfo^-)bZJXF$CP;*}
zz##_?8#2eB8J4L-ZZBO{wX3*h)f{LTv9*9vpGyfOur41gf#Mtx6|eiShe{LUU>8}$hlXR=T)1HzIv&+{NTF}9(?}U{jb05e0sg}
z&5h3Xhn?H|o&D>}RZAzva*v#e+NLLCOi7Jm?DYkeP4qlj$ZDSe;i8}VNA|>