Compare commits

97 Commits
Author SHA1 Message Date
Orangeeee 9bc57bcbba Merge remote-tracking branch 'origin/cyf_class_management' into lon_students_merge_code_test
# Conflicts:
#	class_management/database.py
#	class_management/main.py
2026-09-22 19:48:25 +08:00
Orangeeee d4d1d4fe2d 统计分析模块更新,待测试,优化所有模块main.py文件,修复bug。 2026-09-22 19:47:02 +08:00
Anhoa 5881afd5cc 增加了num的唯一键,修改了dao和api 2026-09-22 19:33:36 +08:00
Orangeeee 91d86d4ea9 统计分析模块更新,待测试,试运行通过,外键已绑定完毕。 2026-09-22 19:19:09 +08:00
Orangeeee d7ac0027af Merge remote-tracking branch 'origin/geyuan_hhy_teacher' into lon_students_merge_code_test 2026-09-22 19:14:36 +08:00
geyuan be1edae34c 增加科目表的id 2026-09-22 19:14:04 +08:00
Orangeeee 7839178625 统计分析模块更新,待测试,模块更新80分以上成绩学生信息查询。 2026-09-22 19:04:39 +08:00
Orangeeee c88c6bc27e 统计分析模块更新,待测试,模块更新。 2026-09-22 18:29:31 +08:00
Orangeeee 35bfdb56eb Merge remote-tracking branch 'origin/lon_students_merge_code_test_coffee' into lon_students_merge_code_test
# Conflicts:
#	stu_jiuye/model.py
2026-09-22 18:29:07 +08:00
Coffee b497e6e2a1 更新id 2026-09-22 18:26:40 +08:00
Orangeeee 6775da7f89 统计分析模块更新,待测试,成绩模块更新。 2026-09-22 18:23:13 +08:00
Orangeeee 6a39e1d56f Merge remote-tracking branch 'origin/xiaoyang_score' into lon_students_merge_code_test 2026-09-22 18:02:33 +08:00
chen 1558103dfa 外键 2026-09-22 18:00:55 +08:00
chen 5c671020d7 外键 2026-09-22 17:55:57 +08:00
Orangeeee 657f97cc38 Merge remote-tracking branch 'origin/lon_students_merge_code_test_coffee' into lon_students_merge_code_test 2026-09-22 17:32:42 +08:00
Coffee 5020eeb640 statcalc就业统计模块更新 2026-09-22 17:02:59 +08:00
Orangeeee 0cdecadd79 Merge remote-tracking branch 'origin/coffee_all' into lon_students_merge_code_test 2026-09-22 16:58:57 +08:00
Coffee 10cf1b64e8 修改外键 2026-09-22 16:55:05 +08:00
Orangeeee eb4e4c1cc0 Merge remote-tracking branch 'origin/cyf_class_management' into lon_students_merge_code_test
# Conflicts:
#	class_management/database.py
#	class_management/main.py
#	class_management/model/class_management_model.py
#	pyproject.toml
#	uv.lock
2026-09-22 16:52:49 +08:00
Orangeeee 214d15251e Merge remote-tracking branch 'origin/coffee_all' into lon_students_merge_code_test
# Conflicts:
#	stu_jiuye/database.py
#	stu_jiuye/main.py
2026-09-22 16:48:37 +08:00
Orangeeee c90c9a06e8 Merge remote-tracking branch 'origin/geyuan_hhy_teacher' into lon_students_merge_code_test
# Conflicts:
#	README.md
2026-09-22 16:45:53 +08:00
Orangeeee c654e7d972 Merge remote-tracking branch 'origin/xiaoyang_score' into lon_students_merge_code_test
# Conflicts:
#	scores/main.py
#	scores/model/score_model.py
2026-09-22 16:45:12 +08:00
Orangeeee f6aa668139 新增StatCalc文件夹,统计分析模块,初步框架搭建。 2026-09-22 16:38:38 +08:00
chen de85f8ef06 外键 2026-09-22 15:58:34 +08:00
geyuan daed5971ea 增加科目表的id 2026-09-22 15:33:09 +08:00
Orangeeee e2ccae757c 新增databases.py把所有建表基类统一成一个,才可以链接外键。 2026-09-22 15:32:34 +08:00
Coffee a6b1e3b0de 修正注释信息 2026-09-22 15:19:43 +08:00
geyuan 6de3d1dd22 更新科目表 2026-09-22 15:19:19 +08:00
Orangeeee 8f39d5456a 新增databases.py把所有建表基类统一成一个,才可以链接外键。 2026-09-22 15:14:32 +08:00
geyuan a415a7c35e 更新架构功能的文字介绍 2026-09-22 14:39:20 +08:00
geyuan d99eb7367d 增加逻辑删除的查询条件 2026-09-22 14:36:10 +08:00
Orangeeee f6e4cdedfc 新增.env模板。 2026-09-22 14:04:29 +08:00
Orangeeee 1b00ea1fad 新增.env模板。 2026-09-22 14:04:05 +08:00
Orangeeee 99a3f70349 新增.env模板。 2026-09-22 14:00:32 +08:00
geyuan 273bee1ea2 改bug 2026-09-22 13:54:00 +08:00
Orangeeee f736845b6f 分支合并,调整完毕,可以运行,可以拉取该分支进行修改bug。 2026-09-22 13:44:05 +08:00
Anhoa 28e0401383 更改文件导入路径 2026-09-22 13:38:28 +08:00
Coffee fb94d03bb6 添加from文件夹名 2026-09-22 13:34:34 +08:00
Orangeeee 4033dd86c1 Merge remote-tracking branch 'origin/cyf_class_management' into lon_students_merge_code_test
# Conflicts:
#	class_management/database.py
#	class_management/main.py
2026-09-22 13:27:18 +08:00
Orangeeee ee548328d5 Merge remote-tracking branch 'origin/coffee_jiuye' into lon_students_merge_code_test
# Conflicts:
#	.idea/.gitignore
#	stu_jiuye/database.py
#	stu_jiuye/main.py
2026-09-22 13:22:52 +08:00
Orangeeee 4e929295e3 分支合并。 2026-09-22 13:12:15 +08:00
Orangeeee 37a5de11b8 尝试合并,修改主main.py。 2026-09-22 13:06:58 +08:00
Orangeeee fabe78cbcc 尝试合并,所有代码需要调整from后文件路径。 2026-09-22 13:04:53 +08:00
geyuan 81e9a2c534 框架 2026-09-22 13:04:50 +08:00
geeker 48080554a7 测试验证统一环境 2026-09-22 13:04:26 +08:00
Anhoa 23e29d7f1a 更改文件导入路径和部分功能更新 2026-09-22 12:31:52 +08:00
Coffee 2cc3829c88 添加from文件夹名 2026-09-22 12:28:06 +08:00
Orangeeee cae04fd04a 尝试合并,所有代码需要调整from后文件路径。 2026-09-22 12:23:18 +08:00
chen 1fe2b6bc60 增删改查基本完成 2026-09-22 11:45:25 +08:00
Coffee 5ced78a009 更新debug 2026-09-22 11:42:27 +08:00
Orangeeee 73da38f445 students文件夹内新增main,用于整合链接到主main。 2026-09-22 11:16:51 +08:00
Orangeeee 68105a39d5 Merge branch 'lon_students' into lon_students_merge_code_test 2026-09-22 11:12:47 +08:00
Orangeeee 8ea5fcb5b8 students文件夹内新增main,用于整合链接到主main。 2026-09-22 11:12:15 +08:00
Orangeeee ac545d0782 Merge remote-tracking branch 'origin/xiaoyang_score' into lon_students_merge_code_test 2026-09-22 11:03:51 +08:00
Orangeeee 763fd4cb45 Merge remote-tracking branch 'origin/geyuan_hhy_teacher' into lon_students_merge_code_test
# Conflicts:
#	.idea/.gitignore
2026-09-22 11:03:04 +08:00
chen 8a1022390f 增删改查还在调试添加 2026-09-22 11:02:17 +08:00
geyuan 4460a16848 改新buge 2026-09-22 11:00:36 +08:00
Orangeeee be2bbcb5ee Merge remote-tracking branch 'origin/cyf_class_management' into lon_students_merge_code_test 2026-09-22 10:56:50 +08:00
Anhoa fee9e14768 修改字段 和表名 2026-09-22 10:50:34 +08:00
Coffee 29dd7e1e04 更新字段 2026-09-22 10:48:49 +08:00
Orangeeee 67c3cd0452 Merge remote-tracking branch 'origin/coffee_jiuye' into lon_students_merge_code_test 2026-09-22 10:48:43 +08:00
Orangeeee b4be2b083c Merge remote-tracking branch 'origin/xiaoyang_score' into lon_students_merge_code_test 2026-09-22 10:48:11 +08:00
Orangeeee 66ca2cc8b7 Merge remote-tracking branch 'origin/cyf_class_management' into lon_students_merge_code_test
# Conflicts:
#	main.py
#	requirements.txt
2026-09-22 10:46:23 +08:00
chen ed151b55ae 增删改查还在调试添加 2026-09-22 10:26:25 +08:00
Orangeeee c8961950ea 同步业务名称导致需要同步修改业务代码,现全部更新完毕,初步测试增删改查没有问题。 2026-09-22 10:08:56 +08:00
Orangeeee fe7c95fa78 更新同步 所有库名、表名、model字段名 等,导致需要同步修改业务代码,现更新到update的request、api和dao了,暂存。 2026-09-21 23:11:45 +08:00
geyuan 2c969c7b9c 改buge 2026-09-21 22:22:32 +08:00
Anhoa 9a97c7d2a8 重新新增项目架构文件。 2026-09-21 22:06:10 +08:00
chen 7cdad9a9fe 成绩建表 2026-09-21 21:56:16 +08:00
Orangeeee 1f87616004 小组更新同步 所有库名、表名、model字段名 等。 2026-09-21 21:49:16 +08:00
geyuan c996fe00f0 增改表 2026-09-21 21:48:36 +08:00
Coffee 3dcfaa5500 amend! 添加注释
更新字段
2026-09-21 21:33:17 +08:00
geyuan b9134b6ef1 改掉一些报错 2026-09-21 19:36:29 +08:00
chen e88306a555 成绩建表 2026-09-21 19:36:17 +08:00
Orangeeee e71ac77c76 更新 README_students.md 模块解释。 2026-09-21 19:04:09 +08:00
Coffee 77de323412 添加注释 2026-09-21 18:39:33 +08:00
Orangeeee 3edce70bbb 修改delete相关代码逻辑,新增README_students.md用来做模块解释。 2026-09-21 18:07:01 +08:00
Coffee 71c7a3db2f 添加注释 2026-09-21 17:24:14 +08:00
Coffee acf5eb1441 添加注释 2026-09-21 17:05:10 +08:00
geyuan b28430257c 更改逻辑删除 2026-09-21 16:53:08 +08:00
chen 6e526d9f6b 成绩建表 2026-09-21 16:30:07 +08:00
geyuan 12fb6a7e83 更改老师 2026-09-21 16:28:55 +08:00
Coffee 2bc1e58047 修改删除模块debug 2026-09-21 16:18:59 +08:00
Coffee def4b1d2c3 更新debug 2026-09-21 15:35:45 +08:00
Coffee 3976d8c20e 更新debug 2026-09-21 15:07:50 +08:00
chen 1b9e5b8a57 成绩建表 2026-09-21 14:54:01 +08:00
Coffee c45cae80c7 更新外键连接 2026-09-21 12:22:26 +08:00
Orangeeee e9507fef26 基础框架内容已经搭建完毕,剩余外键还未链接。 2026-09-21 12:20:53 +08:00
Coffee 72434a78bc 第三次提交,更新完善表连接,新增公司表和地址表 2026-09-21 11:44:09 +08:00
geyuan 7fc2dedee6 老师 2026-09-21 11:30:04 +08:00
Orangeeee 1f4fa614e5 更新所有模块框架,建表已写好,学生查询模块已经写好,正在写新增模块。 2026-09-21 08:41:49 +08:00
Orangeeee 4627b4a57a 删除原分支,新增配置环境后的新分支,同步构建新框架 2026-09-21 03:32:57 +08:00
Coffee 53a48e0b21 第二次提交 2026-09-20 21:20:12 +08:00
Coffee 55252780e2 Merge branch 'main' into coffee_jiuye 2026-09-19 19:01:23 +08:00
Coffee 868f100b84 第一次提交 2026-09-19 18:56:18 +08:00
Coffee d0dbf696af 第一次提交 2026-09-19 18:24:55 +08:00
geyuan b1081d37a9 框架 2026-09-19 18:14:22 +08:00
71 changed files with 2463 additions and 294 deletions
+7
View File
@@ -0,0 +1,7 @@
# .env(放项目根目录,每个人自己建,不提交 Git)
# 用来修改数据库账户密码
DB_USER=root
DB_PASSWORD=420821
DB_HOST=localhost
DB_PORT=3306
DB_NAME=student_manage_system
+1
View File
@@ -13,3 +13,4 @@ tests/
.env
.env.*
Dockerfile
+12
View File
@@ -0,0 +1,12 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# 已忽略包含查询文件的默认文件夹
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
*.xml
*.iml
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="@localhost" uuid="f010600d-8f20-492b-bab2-be2a925ffc75">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://localhost:3306</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>
+12
View File
@@ -0,0 +1,12 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPep8Inspection" enabled="true" level="INFORMATION" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="E116" />
</list>
</option>
</inspection_tool>
</profile>
</component>
+6
View File
@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="E:\anaconda3\envs\newai0824" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PackageRequirementsSettings" />
<component name="PyDocumentationSettings" />
<component name="ReSTService" />
<component name="TestRunnerService" />
</module>
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/student-manage-system.iml" filepath="$PROJECT_DIR$/.idea/student-manage-system.iml" />
</modules>
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.10" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
+29 -288
View File
@@ -1,291 +1,32 @@
# 教培系统架构设计与开发
# 学生管理系统
## 教师篇
**main:主程序文件、测试接口**\
1 下面所有配置文件的接口全部导入并建表\
2 创建fastapi接口并本地运行\
**api:接口文件**\
*教师表Teacher的功能接口:*\
1.教师信息的查询\
2.教师信息的新增\
3.教师信息的更新\
4.教师信息的逻辑删除\
**dao数据筛选文件**\
1.查询 get_tea_dao:\
2.新增 add_tea_dao:\
3.更新 update_tea_dao:\
4.逻辑删除 delete_tea_dao:\
**tea_model数据库建表文件**\
1.教师表\
字段名:id、name、sex、class_id、sub(学科)\
home_place specialty(专业)、start_time(入职时间)\
work_experience、collage、creat_date、update_date\
is_delete、delete_time\
2.学科表\
字段名:id,name(科目名字)
**schema 请求体、响应体**\
请求体:设置了两个,一个带自增组件用于查询\
一个不带自增组件,新增用;\
响应体:用于返回给前端
本项目旨在开发一个基于FastAPI的学生管理系统,提供**学生基本信息管理、考核成绩管理和就业管理**三大核心功能模块。
系统将采用RESTful API设计,支持前后端分离架构。
## 需求分期
团队对需求管理按照模块化解耦拆分开发,按照三个阶段开发
1. 基础需求:按照开发任务,完成5 + 1 统计模块的开发
> todo: 每个人补充一下自己对本模块的理解
2. 特色需求:
- 数据源配置
- 鉴权管理
- 添加部门管理,顾问管理模块
- 添加用户认证模块
- 开发前端应用
- 开发更多的分析查询接口
- 增加AI智能体和知识库开发
3. 脚手架集成
### 第一阶段 - 基础需求
按照开发任务,完成5 + 1 统计模块的开发
- 学生
- 班级
- 老师
- 成绩
- 就业
- 统计分析
### 第二阶段 - 特色需求
- 数据源配置
- 鉴权管理
- 添加部门管理,顾问管理模块
- 添加用户认证模块
- 开发前端应用
- 开发更多的分析查询接口
- 增加AI智能体和知识库开发
### 第三阶段 - 脚手架集成
核心组件
- 服务状态监控 - utc时区[还有哪些可替代?]
- db ,中间件等组件是否活跃
- 自身服务是否活跃
- 认证模块: oauth2_scheme
-
-
## 业务拆分
> 业务范围划分
>
> - 实体
> - 学生基本信息管理
> - 考核成绩管理
> - 就业管理
> - 班级
> - 老师
> - 统计分析
## 数据架构设计
数据关系
- 学生:班级 = N: 1, 班级:老师 = 1 : 1 / [N:1], 学生: 成绩 = 1:N , 学生:就业 = 1:1,
- 统计分析:
表结构优化
- 索引设置
-
```sql
create database student_management_system;
use student_management_system;
-- students
create table if not exists students(
id int auto_increment primary key,
snum int not null comment '学生编号',
sname varchar(30) not null,
age int not null,
sex int not null,
home_place varchar(50) null comment '家乡',
college varchar(50) null comment '毕业院校',
specialty varchar(50) null comment '专业',
enrollment_time datetime null comment '入学时间',
graduate_time datetime null comment '毕业时间',
education varchar(50) null comment '学历',
class_id int not null,
advisor_id int null comment '顾问id',
advisor_num int null comment '顾问编号',
-- 通用字段
create_time datetime not null,
update_time datetime not null,
is_detleted int not null comment '0 未删除,1'
);
-- teachers
create table if not exists teachers(
id int auto_increment primary key,
tnum int not null comment '老师编号',
tname int not null,
age int not null,
sex int not null,
home_place varchar(50) null comment '家乡',
college varchar(50) null comment '毕业院校',
specialty varchar(50) null comment '专业',
enrollment_time datetime null comment '入学时间',
graduate_time datetime null comment '毕业时间',
education varchar(50) null comment '学历',
-- 额外信息:工作经验/教学评级 ,教学方向:应用,项目,算法;
work_experience text null comment '工作经验',
coach_area text null comment '教学方向:应用,项目,算法',
-- 通用字段
create_time datetime not null,
update_time datetime not null,
is_detleted int not null comment '0 未删除,1'
);
-- classes
create table if not exists classes(
id int auto_increment primary key,
class_num int not null comment '班级编号',
class_start_time datetime null comment '开课时间',
head_teacher_id int null comment '班主任',
head_teacher_num int null comment '班主任编号',
coach_teacher_id int null comment '授课老师',
coach_teacher_num int null comment '授课老师编号',
tutor_teacher_id int null comment '助教老师',
tutor_teacher_num int null comment '助教老师编号',
-- 通用字段
create_time datetime not null,
update_time datetime not null,
is_detleted int not null comment '0 未删除,1'
);
-- scores
create table if not exists scores(
id int auto_increment primary key,
sid int not null,
snum int not null,
score_num int not null comment '考核序次',
score decimal(10,2) not null,
-- 通用字段
create_time datetime not null,
update_time datetime not null,
is_detleted int not null comment '0 未删除,1'
);
-- employment
create table if not exists employment(
id int auto_increment primary key,
sid int not null,
snum int not null,
sname varchar(30) not null,
class_id int not null,
class_num int not null comment '班级编号',
employment_open_time datetime null comment '就业开放时间',
offer_recived_time datetime null comment 'offer下发时间',
employment_company varchar(50) null comment '就业公司',
employment_salary decimal(10,2) null comment '就业薪资',
-- 通用字段
create_time datetime not null,
update_time datetime not null,
is_detleted int not null comment '0 未删除,1'
);
```
## 技术架构设计
### git 协作
- 按照业务模块,每个人新建自己的分支,分支名称:user_module
- main 分支:发版分支,test分支:做集成测试用
### 构建环境适配
```bash
# uv 统一
pip install uv
uv init
uv add fastapi[standard] pymsql sqlalchemy # 添加依赖
# 运行测试
uv run uvicorn main:app --reload # uv run fastapi dev
# 其他平台: sync 同步即可
uv sync
```
### 基础架构适配
- 后端框架:
- web route / api docs / 数据校验 : FastAPI
- ORM 框架: pymysql, sqlarchemy
- 日志系统:loguru,
-
- 数据库:多数据源适配
- dev : sqlite3
- prod: MySQL, PG
- 前端框架:
- vue3
-
### 脚手架设计
View File
+141
View File
@@ -0,0 +1,141 @@
# statcalc_api所有统计分析模块的api路由、接口
from fastapi import APIRouter , Depends , HTTPException
from StatCalc.dao.statcalc_dao import *
from StatCalc.model.statcalc_model import *
from StatCalc.schema.statcalc_request import *
from databases import *
statcalc_api = APIRouter()
@statcalc_api.get("/statcalc"
,summary='人员信息查询'
,description=f'查询所有超过30岁的学员的信息。'
)
def get_students(db=Depends(get_db)):
result = get_age_30( db=db )
if result:
return result
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
@statcalc_api.get("/statcalc"
,summary='人数统计'
,description=f'统计每个班级的人数以及男生女生的人数。'
)
def get_students(g:get_student_dao = Depends()
,db=Depends(get_db)
):
result = get_class_count( g=g , db=db )
if result:
return result
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
@statcalc_api.get("/statcalc"
,summary='80分以上成绩学生信息查询'
,description=f'查询每次考试成绩都在80分以上的学生的编号,姓名和成绩。'
)
def get_students(db=Depends(get_db)):
result = get_score_80( db=db )
if result:
return result
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
@statcalc_api.get("/statcalc"
,summary='不及格成绩查询'
,description=f'查询有两次以上不及格的学生的姓名,班级和不及格成绩。'
)
def get_students( stu:StudentsQuery = Depends()
, db=Depends(get_db)
):
s_dict = stu.model_dump(exclude_unset=True) # 没值的不要
result = get_student_dao( s=s_dict , db=db )
if result:
return result
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
@statcalc_api.get("/statcalc"
,summary='班级平均分查询'
,description=f'统计每次考试每个班级的平均分,按照从高到低排序。'
)
def get_students( stu:StudentsQuery = Depends()
, db=Depends(get_db)
):
s_dict = stu.model_dump(exclude_unset=True) # 没值的不要
result = get_student_dao( s=s_dict , db=db )
if result:
return result
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
@statcalc_api.get("/statcalc"
,summary='排名前五的学生信息'
,description=f'统计就业薪资最高的前五名学生的姓名,班级和就业时间,就业公司。'
)
def get_students( stu:StudentsQuery = Depends()
, db=Depends(get_db)
):
s_dict = stu.model_dump(exclude_unset=True) # 没值的不要
result = get_student_dao( s=s_dict , db=db )
if result:
return result
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
@statcalc_api.get("/statcalc"
,summary='就业时长统计'
,description=f'统计每个学生的就业时长(offer下发时间-就业开放时间)。'
)
def get_students( stu:StudentsQuery = Depends()
, db=Depends(get_db)
):
s_dict = stu.model_dump(exclude_unset=True) # 没值的不要
result = get_student_dao( s=s_dict , db=db )
if result:
return result
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
@statcalc_api.get("/statcalc"
,summary='班级的平均就业时长'
,description=f'统计每个班级的平均就业时长(只统计进入就业阶段的学生,也就是有就业开放时间)'
)
def get_students( stu:StudentsQuery = Depends()
, db=Depends(get_db)
):
s_dict = stu.model_dump(exclude_unset=True) # 没值的不要
result = get_student_dao( s=s_dict , db=db )
if result:
return result
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
@CURD.get('/Top5',summary='就业薪资Top5')
def salarytop5(db=Depends(get_db)):
q = db.query(Employments,Company.employment_company) \
.join(Company, Employments.company_id == Company.id) \
.filter(Employments.is_deleted == 0)\
.order_by(Employments.employment_salary.desc())\
.limit(5) .all()
return [{'学生姓名':i.sname,'学生班级编号':i.class_num,'就业时间':i.offer_recived_time,'公司':employment_company} for i,employment_company in q]
@CURD.get('/worktime',summary='学生就业时常')
def worktime(db=Depends(get_db)):
q = db.query(Employments)\
.filter(Employments.is_deleted==0)\
.all()
return [{'学生姓名':i.sname,'就业时常':f'{i.offer_recived_time-i.employment_open_time}' if i.offer_recived_time and i.employment_open_time else '暂无数据'} for i in q]
@CURD.get('/avgworktime',summary='学生平均就业时常')
def avgworktime(db=Depends(get_db)):
q = db.query(Employments)\
.filter(Employments.employment_open_time.isnot(None)
,Employments.offer_recived_time.isnot(None)
,Employments.is_deleted==0)\
.all()
if not q:
return {'平均就业时常': '暂无数据'}
total = sum((i.offer_recived_time - i.employment_open_time).total_seconds() for i in q)
avg_days=total /len(q)/86400 #一天86400秒
return {
'人数': len(q),
'平均就业时常(天)': round(avg_days, 2)
}
View File
+50
View File
@@ -0,0 +1,50 @@
from pygments.lexers import sql
from sentry_sdk.integrations import sqlalchemy
from StatCalc.schema.statcalc_request import *
from fastapi import HTTPException
from sqlalchemy import *
from students.model.students_model import Students
def get_age_30(db):
q = db.query(Students).filter(Students.age>=30).all()
return q
from class_management.model.class_management_model import ClassInfo
def get_class_count(g , db):
if g == "班级人数":
q = (db.query(ClassInfo.id,ClassInfo.name,func.count(ClassInfo.id) )
.join(Students,Students.class_id==ClassInfo.id)
.group_by(ClassInfo.id,ClassInfo.name,Students.sex)
.all())
elif g == "男女人数":
q = (db.query(Students.sex,func.count(ClassInfo.id) )
.join(Students,Students.class_id==ClassInfo.id)
.group_by(Students.sex)
.all())
elif g == "班级中男女人数":
q = (db.query(ClassInfo.id,ClassInfo.name,Students.sex,func.count(ClassInfo.id) )
.join(Students,Students.class_id==ClassInfo.id)
.group_by(ClassInfo.id,ClassInfo.name,Students.sex)
.all())
else:
return False
return q
from scores.model.score_model import Score
def get_score_80(db):
q = ( db.query(Score)
.group_by(Score.score)
.having( min(Score.score) >= 80)
.all()
)
return q
+7
View File
@@ -0,0 +1,7 @@
from fastapi import FastAPI,APIRouter
from StatCalc.api.statcalc_api import statcalc_api
from databases import *
from contextlib import asynccontextmanager
StatCalc_API = APIRouter()
StatCalc_API.include_router(statcalc_api)
View File
+12
View File
@@ -0,0 +1,12 @@
# students_schema:请求、响应的模型框架
from pydantic import BaseModel , Field , field_validator
from datetime import datetime
import enum
class get_student_dao(enum.Enum):
get_class = "班级人数"
get_sex = "男女人数"
get_class_sex = "班级中男女人数"
+7
View File
@@ -0,0 +1,7 @@
# .env(放项目根目录,每个人自己建,不提交 Git)
# 用来修改数据库账户密码
DB_USER=root
DB_PASSWORD=123456
DB_HOST=localhost
DB_PORT=3306
DB_NAME=student_manage_system
View File
+46
View File
@@ -0,0 +1,46 @@
from fastapi import APIRouter,Depends,HTTPException
from Teachers.schema import *
from Teachers.database import *
from Teachers.model.tea_model import *
from Teachers.dao.tea_dao import *
from Teachers.schema.tea_request import TeaRequest,NewTeaRequest,NewTeaRequest1
from Teachers.schema.tea_response import TeaResponse
tea_api = APIRouter()
@tea_api.post('/tea',response_model=TeaResponse,summary='新增老师',description='输入新增的老师信息,除去姓名外可以为空')
def add_tea(tea: NewTeaRequest=Depends(),db=Depends(get_db)):
r = add_tea_dao(tea.model_dump(),db)
if not r:
raise HTTPException(status_code=500,detail='添加失败!')
return TeaResponse(totals=1,data=tea.model_dump())
@tea_api.get('/tea',response_model=TeaResponse,summary='查询老师',description='任意选择输入的信息数量,不输入则查询全部老师')
def get_tea(t:NewTeaRequest1=Depends(),db=Depends(get_db)): #开启会话窗口
r = get_tea_dao(t.model_dump(),db=db)
if not r:
raise HTTPException(status_code=404,detail='老师不存在')
return TeaResponse(totals=len(r), data=r)
@tea_api.delete('/tea/{id}',summary='删除老师',description='输入删除的老师id')
def delete_tea(id:int|None,db=Depends(get_db)):
r = delete_tea_dao(id,db)
if not r:
raise HTTPException(status_code=500,detail='删除失败!')
return {'detail':'删除成功!'}
@tea_api.put('/tea/{id}',summary='更新老师信息',description='输入更改的信息,可以为空')
def update_tea(id:int,tea:NewTeaRequest,db=Depends(get_db)):
t = tea.model_dump(exclude_unset= True)
r = update_tea_dao(id=id,t=t,db=db)
if not r:
raise HTTPException(status_code=404,detail='老师不存在')
return {'detail':'更新成功'}
#根据任意信息删除
@tea_api.delete('/tea',summary='根据任意信息逻辑删除',description='输入任意信息删除')
def delete_other_tea_dao1(t:TeaRequest=Depends(),db=Depends(get_db)):
row1 = delete_other_tea_dao(t.model_dump(exclude_unset=True),db)
if not row1:
raise HTTPException(status_code=404,detail='没有找到符合条件的老师')
return{'code':200,'data':row1}
View File
+124
View File
@@ -0,0 +1,124 @@
from Teachers.model.tea_model import Tea
from datetime import datetime
#增加
def add_tea_dao(t,db):
try:
not_delete = db.query(Tea).filter(Tea.phone ==t.get('phone'),Tea.is_delete == False).first()
if not_delete:
print('该老师已存在')
raise Exception('老师已存在')
existing = db.query(Tea).filter(Tea.phone == t.get('phone'),Tea.is_delete == True).first()
if existing:
for key,value in t.items():
if hasattr(existing,key) and value is not None and value != '':
setattr(existing,key,value)
existing.is_delete = False
db.commit()
return '恢复修改成功'
t1 = Tea(**t)
db.add(t1)
db.commit()
return True
except Exception as e:
db.rollback()
print('新增失败原因',e)
return '新增失败'
#更新
def update_tea_dao(id,t, db):
try:
row = db.query(Tea).filter(Tea.id == id,Tea.is_delete ==False ).update(t)
except:
db.rollback()
return False
else:
db.commit()
return row
#查询
def get_tea_dao(t,db):
query = db.query(Tea).filter(Tea.is_delete == False)
if t.get('id'):
query = query.filter(Tea.id == t.get('id') )
if t.get('name'):
query = query.filter(Tea.name == t.get('name') )
if t.get("sex"):
query = query.filter(Tea.sex == t.get('sex') )
if t.get('class_id'):
query = query.filter(Tea.class_id == t.get('class_id') )
if t.get('sub'):
query = query.filter(Tea.sub == t.get('sub') )
if t.get('home_place'):
query = query.filter(Tea.home_place == t.get('home_place') )
if t.get('specialty'):
query = query.filter(Tea.specialty == t.get('specialty') )
if t.get('start_time'):
query = query.filter(Tea.start_time == t.get('start_time') )
if t. get('work_experience'):
query = query.filter(Tea.work_experience == t.get('work_experience') )
if t.get('education'):
query = query.filter(Tea.education == t.get('education') )
if t.get('collage'):
query = query.filter(Tea.collage == t.get('collage') )
if t.get('phone'):
query = query.filter(Tea.phone == t.get('phone') )
r = query.all()
return [{'id':i.id,'name':i.name,'class_id':i.class_id,'sub':i.sub,'sex':i.sex,'home_place':i.home_place
,'specialty':i.specialty,'start_time':i.start_time,'work_experience':i.work_experience
,'collage':i.collage,'phone':i.phone} for i in r]
#删除
def delete_tea_dao(id,db):
try:
row = db.query(Tea).filter(Tea.id == id
,Tea.is_delete == False
).update({'is_delete':True,'delete_time':datetime.now()})
except:
db.rollback()
row = 0
return row
else:
db.commit()
return row
def delete_other_tea_dao(t,db):
if not t:
return False
try:
query1 = db.query(Tea).filter(Tea.is_delete == False)
if t.get('id'):
query1 = query1.filter(Tea.id == t.get('id') )
if t.get('name'):
query1 = query1.filter(Tea.name == t.get('name') )
if t.get('sex'):
query1 = query1.filter(Tea.sex == t.get('sex'))
if t.get('class_id'):
query1 = query1.filter(Tea.class_id == t.get('class_id'))
if t.get('sub'):
query1 = query1.filter(Tea.sub == t.get('sub'))
if t.get('home_place'):
query1 = query1.filter(Tea.home_place == t.get('home_place'))
if t.get('collage'):
query1 = query1.filter(Tea.collage == t.get('collage'))
if t.get('education'):
query1 = query1.filter(Tea.education == t.get('education'))
if t.get('work_experience'):
query1 = query1.filter(Tea.work_experience == t.get('work_experience'))
if t.get('start_time'):
query1 = query1.filter(Tea.start_time == t.get('start_time'))
if t.get('specialty'):
query1 = query1.filter(Tea.specialty == t.get('specialty'))
if t.get('phone'):
query1 = query1.filter(Tea.phone == t.get('phone'))
row = query1.update({'is_delete':True,'delete_time':datetime.now()})
db.commit()
return row
except Exception as e:
db.rollback()
print('删除失败',e)
return '删除失败'
+27
View File
@@ -0,0 +1,27 @@
from sqlalchemy import *
from sqlalchemy.orm import sessionmaker,declarative_base
# 导入.env参数
import os
from dotenv import load_dotenv
load_dotenv( )
DB_USER = os.getenv("DB_USER", "root")
DB_PASSWORD = os.getenv("DB_PASSWORD", "")
DB_HOST = os.getenv("DB_HOST", "localhost")
DB_PORT = os.getenv("DB_PORT", "3306")
DB_NAME = os.getenv("DB_NAME", "student_manage_system")
db_url = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4"
engine1 = create_engine(db_url)
Base = declarative_base()
Session = sessionmaker(bind=engine1
,autoflush = False
,autocommit = False)
def get_db():
db = Session()
try:
yield db
finally:
db.close()
Base.metadata.create_all(engine1)
+23
View File
@@ -0,0 +1,23 @@
from fastapi import FastAPI,APIRouter
from Teachers.model.tea_model import *
from Teachers.api.tea_api import tea_api
from Teachers.database import *
# 合并接口
Teachers_API = APIRouter()
Teachers_API.include_router(tea_api)
# 自测接口
app = FastAPI()
app.include_router(tea_api,tags=["老师"])
if __name__ == "__main__":
Base.metadata.create_all(engine1)
import uvicorn
uvicorn.run('main:app', host='0.0.0.0', port=55000)
"""
IPv4 地址 . . . . . . . . . . . . : 192.168.1.51
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . : 192.168.1.1
"""
View File
+61
View File
@@ -0,0 +1,61 @@
from databases import engine_all,Base_all,Session
from datetime import datetime
from sqlalchemy import *
import enum
class Gender(str,enum.Enum):
m = '男'
f = '女'
h = '未知'
class Tea(Base_all):
__tablename__ = 'teachers'
id = Column(Integer
, primary_key=True
,nullable=False
,autoincrement=True
,comment='id')
name = Column(String(50)
,nullable=False
,comment='姓名')
sex = Column(Enum(Gender),default='未知'
,comment='性别')
class_id = Column(Integer
,nullable=False
,comment='班级id')
phone = Column(String(50))
subject_id = Column(Integer
, ForeignKey('subject.id')
,nullable=False
,comment='教学科目')
home_place = Column(String(50)
,comment='家乡')
specialty = Column(String(50)
,comment='专业')
start_time = Column(DateTime,comment='入职时间')
work_experience = Column(String(50)
,comment='工作经验')
education = Column(String(50)
,comment='学历')
collage = Column(String(50)
,comment='毕业院校')
create_date = Column(DateTime
,default=datetime.now
,comment='创建时间')
update_date = Column(DateTime
,onupdate=datetime.now
,comment='更新时间')
is_delete = Column(Boolean,default=False,comment='是否删除')
delete_time = Column(DateTime)
#科目表
class Sub(Base_all):
__tablename__ = 'subject'
id = Column(Integer
, primary_key=True
, nullable=False
, autoincrement=True
, comment='id')
name = Column(String(50)
,nullable=False
,comment='科目名字')
View File
+45
View File
@@ -0,0 +1,45 @@
from pydantic import BaseModel
from Teachers.model.tea_model import *
#查询使用请求体,都可以为空
class TeaRequest(BaseModel):
id: int|None = None
name: str|None = None
sex: Gender|None = None
class_id: int | None = None
sub: str|None = None
phone: str|None = None
home_place: str|None = None
collage: str|None = None
education: str|None = None
work_experience: str|None = None
start_time: datetime|None = None
specialty: str|None = None
#更新、任意逻辑删除、新增使用请求体,没有自增键,电话号码不能为空
class NewTeaRequest(BaseModel):
name: str | None = None
sex: Gender|None = None
class_id: int | None = None
sub: str | None = None
home_place: str | None = None
phone: str
collage: str | None = None
education: str | None = None
work_experience: str | None = None
start_time: datetime | None = None
specialty: str | None = None
class NewTeaRequest1(BaseModel):
name: str | None = None
sex: Gender|None = None
class_id: int | None = None
sub: str | None = None
home_place: str | None = None
phone: str|None = None
collage: str | None = None
education: str | None = None
work_experience: str | None = None
start_time: datetime | None = None
specialty: str | None = None
+8
View File
@@ -0,0 +1,8 @@
from pydantic import BaseModel
class TeaResponse(BaseModel):
code: int = 200
detail: str = 'OK'
totals:int = 0
data: str |dict|tuple|list
+3
View File
@@ -0,0 +1,3 @@
# 教培系统架构设计与开发##
## 2
### 3
View File
@@ -0,0 +1,90 @@
from datetime import date
from fastapi import APIRouter,Depends,HTTPException,Query
from class_management.dao.class_management_dao import add_class_dao, update_class_dao, get_class_dao, delete_class_dao
from class_management.database import get_db
from class_management.schema.class_management_request import ClassCreate, ClassResponse, ClassUpdate
class_api = APIRouter(prefix='/classes')
# ,tags=['班级管理模块']
#新增接口
@class_api.post("",summary='新增班级')
def add_class(cla:ClassCreate,db=Depends(get_db)):
c_dict = cla.model_dump()
res = add_class_dao(c=c_dict,db=db)
if not res:
raise HTTPException(status_code=400,detail='新增失败,班级已存在')
return ClassResponse(detail='新增成功',totals=1,data=c_dict)
#更新接口
@class_api.put("/{class_id}",summary='修改班级信息')
def update_class(class_id:int
,update_info:ClassUpdate
,db=Depends(get_db)
):
update_dict=update_info.model_dump(exclude_unset=True)
if not update_dict:
raise HTTPException(status_code=400, detail="没有传入要修改的字段")
row_count = update_class_dao(cid=class_id, update_data=update_dict, db=db)
if not row_count :
raise HTTPException(status_code=404, detail="班级不存在,修改失败")
return ClassResponse(detail="修改成功", data={"id": class_id})
#删除接口
@class_api.delete('/{class_id}',summary='逻辑删除班级')
def remove_class(class_id:int,db=Depends(get_db)):
del_rows = delete_class_dao(cid = class_id,db=db)
if not del_rows:
raise HTTPException(status_code=404, detail="班级不存在,删除失败")
return ClassResponse(detail='删除成功',data={'id':class_id})
#单条查询接口
@class_api.get('/{class_id}',summary='根据id查询班级')
def get_one_class(class_id:int,db=Depends(get_db)):
res = get_class_dao(c={'id':class_id},db=db)
if not res:
raise HTTPException(status_code=404, detail="该班级不存在或已删除")
return ClassResponse(totals=1,data=res[0])
#分类查询
@class_api.get('',summary='多字段分页查询班级')
def get_some_classes( num:str|None = Query(None,description='班级编号')
,name:str|None = Query(None,description='班级名称')
,head_teacher_id:int|None = Query(None,description='班主任')
,coach_teacher_id:int|None = Query(None,description='授课老师')
,tutor_teacher_id:int|None = Query(None,description='助教老师')
,class_start_time:date|None = Query(None, description="开班日期,格式YYYY‑MM‑DD")
,class_end_time: date|None = Query(None, description="结课日期,格式YYYY‑MM‑DD")
,page:int = Query(1,ge=1,description='页码,从1开始')
,page_size:int = Query(5,ge=1,le=20,description='每页信息条数')
,db=Depends(get_db)
):
c_dict = {
'num': num,
'name': name,
'head_teacher_id': head_teacher_id,
'coach_teacher_id': coach_teacher_id,
'tutor_teacher_id': tutor_teacher_id,
'class_start_time': class_start_time,
'class_end_time': class_end_time,
}
c_dict={k:v for k,v in c_dict.items() if v is not None}#过滤掉值为None的key
res = get_class_dao(c=c_dict,db=db)
total = len(res)
#分页
start_index = (page-1)*page_size
page_data = res[start_index:start_index + page_size]
return ClassResponse(totals=total,data=page_data)
View File
@@ -0,0 +1,79 @@
from class_management.model.class_management_model import ClassInfo
from sqlalchemy.orm import Session
#新增班级
def add_class_dao(c,db):
try:
c1 = ClassInfo(**c)
db.add(c1)
except:
db.rollback()
return False
else:
db.commit()
return True
#更新班级信息
def update_class_dao(cid,update_data,db):
try:
rows = db.query(ClassInfo)\
.filter(ClassInfo.id == cid
,ClassInfo.is_delete==0)\
.update(update_data)
except :
db.rollback()
return False
else:
db.commit()
return rows
#删除(逻辑删除)
def delete_class_dao(cid,db):
try:
rows = db.query(ClassInfo)\
.filter(ClassInfo.id == cid,ClassInfo.is_delete==0)\
.update({'is_delete':1})
db.commit()
return rows
except Exception as e:
db.rollback()
print('删除失败',e)
return 0
#查询
def get_class_dao(c:dict,db:Session) :
q = db.query(ClassInfo).filter(ClassInfo.is_delete == 0)#is_delete=0代表未删除
if c.get('id'):
q = q.filter(ClassInfo.id == c.get('id'))
if c.get('num'):
q = q.filter(ClassInfo.num == c.get('num'))
if c.get('name'):
q = q.filter(ClassInfo.name == c.get('name'))
if c.get('head_teacher_id'):
q = q.filter(ClassInfo.head_teacher_id == c.get('head_teacher_id'))
if c.get('coach_teacher_id'):
q = q.filter(ClassInfo.coach_teacher_id == c.get('coach_teacher_id'))
if c.get('tutor_teacher_id'):
q = q.filter(ClassInfo.tutor_teacher_id == c.get('tutor_teacher_id'))
if c.get('class_start_time'):
q = q.filter(ClassInfo.class_start_time == c.get('class_start_time'))
if c.get('class_end_time'):
q = q.filter(ClassInfo.class_end_time == c.get('class_end_time'))
res = q.all()
if not res:
return []
return[
{'id':i.id
,'num':i.num
,'name':i.name
,'head_teacher_id':i.head_teacher_id
,'coach_teacher_id':i.coach_teacher_id
,'tutor_teacher_id':i.tutor_teacher_id
,'class_start_time':i.class_start_time
,'class_end_time':i.class_end_time
} for i in res
]
+26
View File
@@ -0,0 +1,26 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker,declarative_base
# 导入.env参数
import os
from dotenv import load_dotenv
load_dotenv( )
DB_USER = os.getenv("DB_USER", "root")
DB_PASSWORD = os.getenv("DB_PASSWORD", "")
DB_HOST = os.getenv("DB_HOST", "localhost")
DB_PORT = os.getenv("DB_PORT", "3306")
DB_NAME = os.getenv("DB_NAME", "student_manage_system")
class_management_rul = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4"
engine = create_engine(class_management_rul)
SessionLocal = sessionmaker(bind=engine,autocommit=False, autoflush=False)
Base = declarative_base()
# 获取数据库会话依赖
def get_db():
db = SessionLocal()
try :
yield db
finally:
db.close()
+19
View File
@@ -0,0 +1,19 @@
from fastapi import FastAPI,APIRouter
from class_management.api.class_management_api import class_api
from class_management.model.class_management_model import ClassInfo
from class_management.database import Base,engine
# 合并接口
Classes_API = APIRouter()
Classes_API.include_router(class_api)
if __name__ =='__main__':
# 自测接口
app = FastAPI()
app.include_router(class_api)
Base.metadata.create_all(engine)
import uvicorn
uvicorn.run('main:app',host='127.0.0.1',port=12345)
View File
View File
@@ -0,0 +1,55 @@
from databases import engine_all,Base_all,Session
from sqlalchemy import Column, Integer, String, Date, ForeignKey
from sqlalchemy.dialects.mysql import DATETIME
from datetime import datetime
from class_management.database import Base
class ClassInfo(Base_all):
__tablename__ = 'classes'
id = Column(Integer
,primary_key=True
,autoincrement=True
,comment='班级id主键'
)
num = Column(String(50)
,nullable=False
,unique=True
,comment='班级编号'
)
name = Column(String(50)
,nullable=False
,comment='班级名称'
)
head_teacher_id = Column(Integer
,ForeignKey('teacher.id')
,comment='班主任编号'
)
coach_teacher_id = Column(Integer
,ForeignKey('teacher.id')
,comment='授课老师编号'
)
tutor_teacher_id = Column(Integer
,ForeignKey('teacher.id')
,comment='助教老师编号'
)
class_start_time = Column(Date
,comment='开班日期'
)
class_end_time = Column(Date
,comment='结课日期'
)
is_delete = Column(Integer
,default=0
,comment='逻辑删除 0未删除 1已删除'
)
create_time = Column(DATETIME
,default=datetime.now
,comment='记录创建时间'
)
update_time = Column(DATETIME
,default=datetime.now
,onupdate=datetime.now
,comment='记录更新时间'
)
View File
@@ -0,0 +1,29 @@
from pydantic import BaseModel,Field
from datetime import date,datetime
from typing import List
#班级基础信息请求体
class ClassBase(BaseModel):
num:str|None = Field(None,description='班级编号')
name:str|None = Field(None,description='班级名称')
head_teacher_id:int|None = Field(None,description='班主任编号(teacher表id)')
coach_teacher_id:int|None = Field(None,description='授课老师编号(teacher表id)')
tutor_teacher_id:int|None = Field(None,description='助教老师编号(teacher表id)')
class_start_time:date|None = Field(None,description='开班日期 YYYY‑MM‑DD')
class_end_time:date|None = Field(None,description='结课日期 YYYY‑MM‑DD')
#新增班级请求体:继承父类ClassBase
class ClassCreate(ClassBase):
pass
#修改班级请求体,继承ClassBase,全部字段可选,传哪个就更新哪个
class ClassUpdate(ClassBase):
pass
class ClassResponse(BaseModel):
code:int = 200
detail:str = 'OK'
totals:int = 0
data:str|dict|tuple|list
+35
View File
@@ -0,0 +1,35 @@
# databases链接数据库
from sqlalchemy import *
from sqlalchemy.orm import sessionmaker,declarative_base
# 导入.env参数
import os
from dotenv import load_dotenv
load_dotenv( )
DB_USER = os.getenv("DB_USER", "root")
DB_PASSWORD = os.getenv("DB_PASSWORD", "")
DB_HOST = os.getenv("DB_HOST", "localhost")
DB_PORT = os.getenv("DB_PORT", "3306")
DB_NAME = os.getenv("DB_NAME", "student_manage_system")
db_url = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4"
engine_all = create_engine(db_url)
Base_all = declarative_base()
Session = sessionmaker(bind=engine_all
,autoflush=False
,autocommit=False)
def get_db():
db = Session()
try:
yield db
finally:
db.close()
+28 -6
View File
@@ -1,11 +1,33 @@
from fastapi import FastAPI
from fastapi import FastAPI,APIRouter
from class_management.main import Classes_API
from scores.main import Scores_API
from stu_jiuye.main import Shtudent_Jiuye
from students.main import Students_API
from Teachers.main import Teachers_API
# from StatCalc.main import StatCalc_API
from databases import *
app = FastAPI(title="学生管理系统")
app = FastAPI()
@app.get("/")
def main():
print("Hello from student-manage-system!")
app.include_router(Classes_API,tags=["classes"])
app.include_router(Scores_API,tags=["scores"])
app.include_router(Shtudent_Jiuye,tags=["Employments"])
app.include_router(Students_API,tags=["students"])
app.include_router(Teachers_API,tags=["teachers"])
# app.include_router(StatCalc_API,tags=["StatCalc"])
if __name__ == "__main__":
main()
Base_all.metadata.create_all(bind=engine_all)
def main():
print("Hello from student-manage-system!")
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=59000)
"""
IPv4 地址 . . . . . . . . . . . . : 192.168.6.40
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . : 192.168.6.1
"""
+4
View File
@@ -6,7 +6,11 @@ readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"fastapi[standard]>=0.141.1",
"like>=1.7.1",
"pip>=26.2.1",
"pymysql>=1.2.3",
"python-dotenv>=1.2.3",
"spglib>=2.7.0",
"sqlalchemy>=2.0.54",
"uvicorn>=0.53.0",
]
+46
View File
@@ -0,0 +1,46 @@
from fastapi import APIRouter, Depends,HTTPException
from scores.database import get_db
from scores.dao.score_dao import get_scores_dao,add_scores_dao,update_scores_dao,delete_scores_dao
from scores.model.score_model import Score
from scores.schema.score_request import ScoreRequest, ScoreResponse,ScoreQuery
score_api = APIRouter()
@score_api.get('/Score',summary='成绩查询',description=f'查询条件为空即查询所有成绩')
def get_scores(score:ScoreQuery=Depends(),db=Depends(get_db)):
a = score.model_dump()
aa = get_scores_dao(s=a,db=db)
if aa:
return aa
raise HTTPException(status_code=404,detail='不存在')
@score_api.post('/Score',response_model=ScoreResponse
,summary='成绩添加')
def add_scores(score:ScoreRequest=Depends(),db=Depends(get_db)):
a = score.model_dump()
# print(a,type(a))
aa = add_scores_dao(b=a,db=db)
if aa:
return ScoreResponse(data=a)
raise HTTPException(status_code=500,detail='不可以')
@score_api.put('/Score',summary='成绩更新',description=f'查询条件为学号,班级号,考试序次,考试科目,然后更改这位同学此次科目的成绩')
def update_scores(score:ScoreRequest,sid:int,cid:int,num:int,t_subject:int,db=Depends(get_db)):
a = score.model_dump(exclude_unset=True,exclude={'id','sid','cid','num','t_subject'})
aa = update_scores_dao(sid = sid,cid = cid,num = num,t_subject = t_subject,update_data = a,db=db)
if aa==-1:
raise HTTPException(status_code=500,detail='没有更新')
if aa==0:
raise HTTPException(status_code=404,detail='不存在')
return {'message':'更新成功','data':a}
@score_api.delete('/Score',summary='成绩删除',description=f'查询条件为学号,班级号,考试科目,然后进行删除')
def delete_scores(sid:int,cid:int,t_subject:int,db=Depends(get_db)):
aaa = delete_scores_dao(sid,cid,t_subject,db)
if aaa==1:
raise HTTPException(status_code=500,detail='删除失败')
if aaa==0:
raise HTTPException(status_code=404,detail='记录不存在')
return {'message':'更新成功','detail':aaa}
+78
View File
@@ -0,0 +1,78 @@
from scores.model.score_model import Score
from datetime import datetime
def get_scores_dao(s,db):
ab = db.query(Score).filter(Score.is_deleted ==0)
if s.get('id'):
ab = ab.filter(Score.id==s.get('id'))
if s.get('sid'):
ab = ab.filter(Score.sid==s.get('sid'))
if s.get('cid'):
ab = ab.filter(Score.cid==s.get('cid'))
if s.get('num'):
ab = ab.filter(Score.num==s.get('num'))
if s.get('score'):
ab = ab.filter(Score.score==s.get('score'))
if s.get('t_subject'):
ab = ab.filter(Score.t_subject==s.get('t_subject'))
aa = ab.all()
if aa:
return [{'id':i.id,'sid':i.sid
,'cid':i.cid,'num':i.num
,'score': i.score,'t_subject':i.tsub
,'create_date':i.create_date,'update_date':i.update_date
,'is_deleted': i.is_deleted,'deleted_date': i.deleted_date
}for i in aa]
else:
return []
def add_scores_dao(b,db):
try:
aa = db.query(Score).filter(Score.sid==b['sid']
,Score.t_subject==b['t_subject']).all()
if aa:
raise ValueError
else:
z = Score(**b)
db.add(z)
except Exception as e:
db.rollback()
print(e)
return False
else:
db.commit()
return True
def update_scores_dao(sid:int,cid:int,num:int,t_subject:int,update_data:dict,db):
if not update_data:
return 0
update_data['update_date']=datetime.now()
try:
aa = db.query(Score).filter(Score.sid == sid
,Score.cid == cid
,Score.num == num
,Score.t_subject == t_subject ).update(update_data)
db.commit()
return aa
except Exception :
db.rollback()
return -1
def delete_scores_dao(sid:int,cid:int,t_subject:int,db):
try:
aaa = db.query(Score).filter(Score.sid ==sid
,Score.cid == cid
,Score.t_subject == t_subject
,Score.is_deleted==0
).update({'is_deleted':1
,'deleted_date':datetime.now()})
db.commit()
return aaa
except Exception as e :
db.rollback()
print(e)
return -1
+25
View File
@@ -0,0 +1,25 @@
from sqlalchemy import *
from sqlalchemy.orm import declarative_base,sessionmaker
# 导入.env参数
import os
from dotenv import load_dotenv
load_dotenv( )
DB_USER = os.getenv("DB_USER", "root")
DB_PASSWORD = os.getenv("DB_PASSWORD", "")
DB_HOST = os.getenv("DB_HOST", "localhost")
DB_PORT = os.getenv("DB_PORT", "3306")
DB_NAME = os.getenv("DB_NAME", "student_manage_system")
db_url = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4"
engine = create_engine(db_url)
Base = declarative_base()
Session = sessionmaker(bind=engine, autoflush = False, autocommit = False)
def get_db():
db = Session()
try:
yield db
finally:
db.close()
+19
View File
@@ -0,0 +1,19 @@
from fastapi import FastAPI,APIRouter
from scores.api.score_api import score_api
from scores.database import engine,Base
# 合并接口
Scores_API = APIRouter()
Scores_API.include_router(score_api)
if __name__ == '__main__':
# 自测接口
app = FastAPI(tags=['成绩'], title='成绩管理系统')
app.include_router(score_api)
Base.metadata.create_all(engine)
import uvicorn
uvicorn.run('main:app',host='127.0.0.1',port=12346)
+42
View File
@@ -0,0 +1,42 @@
from databases import engine_all,Base_all,Session
from scores.database import Base
from datetime import datetime
from sqlalchemy import DateTime,Column,Integer,String,Float,ForeignKey
class Score(Base_all):
__tablename__='scores'
id = Column(Integer
,primary_key = True
,autoincrement = True
,comment ='编号'
)
sid = Column(Integer
,ForeignKey('students.id')
,comment ='学生ID'
)
cid = Column(Integer
,ForeignKey('classes.id')
,comment ='班级ID'
)
num = Column(Integer
,comment ='考试序次'
)
score = Column(Integer,nullable = False)
t_subject = Column(Integer
,ForeignKey('subject.id')
,nullable = False
)
create_date = Column(DateTime
,default = datetime.now
)
update_date = Column(DateTime
,default = datetime.now
)
is_deleted = Column(Integer
,default = 0
,comment='是否删除: 0-正常,1-已删除'
)
deleted_date=Column(DateTime
,default=None
,comment='删除时间'
)
+23
View File
@@ -0,0 +1,23 @@
from pydantic import BaseModel
class ScoreRequest(BaseModel):
# id:int
sid:int
cid:int
num:int
score:int
t_subject:int
class ScoreQuery(BaseModel):
id:int|None = None
sid:int|None = None
cid:int|None = None
num:int|None = None
score:int|None = None
t_subject:int|None = None
class ScoreResponse(BaseModel):
code:int = 200
detail:str = 'OK'
totals:int = 0
data:str|dict|tuple|list
+138
View File
@@ -0,0 +1,138 @@
from datetime import date
from fastapi import APIRouter,Depends,HTTPException,Query
from stu_jiuye.dao import *
from stu_jiuye.request import PostRequest, EmpResponse, AddRequest,AddResponse,CompRequest,CompResponse
from stu_jiuye.database import get_db
CURD=APIRouter()
@CURD.get('/emp',summary='查询就业信息')
def get_stuinfo( #查询学生就业信息
sname:str| None = Query(None,description='学生姓名')
,class_id:int| None = Query(None,description='班级编号')
,employment_company:str|None=Query(None,description='公司名称')
,min_salary:int|None=Query(None,description='最小薪资范围')
,max_salary:int|None=Query(None,description='最大薪资范围')
,somewhere:str|None=Query(None,description='公司地区')
,employment_open_start: date | None = Query(None,description='就业开放起始时间')
,employment_open_end: date | None = Query(None,description='就业开放结束时间')
,offer_recived_start: date | None = Query(None,description='offer下发起始时间')
,offer_recived_end: date | None = Query(None,description='offer下发结束时间')
,db=Depends(get_db)
): #查询条件
r=get_emp_dao( #调用函数
sname,
class_id,
employment_company,
min_salary,
max_salary,
somewhere,
employment_open_start,
employment_open_end,
offer_recived_start,
offer_recived_end,
db
)
if r:
return r #成功返回
raise HTTPException(status_code=404, detail='名单不存在!') #失败返回错误信息
@CURD.post('/emp',response_model=EmpResponse,summary='新增就业信息')
def add_stuinfo( #新增学生就业信息
p:PostRequest,db=Depends(get_db)
): #调用请求体
d=p.model_dump() #解析成字典
r=add_employment_dao(o=d,db=db) #调用函数
if not r:
raise HTTPException(status_code=500, detail='服务器繁忙,请稍后添加!') #失败返回错误
return EmpResponse(totals=1, data=d) #成功返回响应体
@CURD.post('/emp/company',response_model=CompResponse,summary='新增公司信息')
def add_company( #新增公司表信息
c:CompRequest,db=Depends(get_db)
):
d = c.model_dump() #解析
r = add_company_dao(o=d, db=db) #调用添加公司函数
if not r:
raise HTTPException(status_code=500, detail='服务器繁忙,请稍后添加!') #失败返回错误信息
return CompResponse(totals=1, data=d) #成功返回响应体
@CURD.post('/emp/company/address',response_model=AddResponse,summary='新增公司所在区域')
def add_address( #新增公司地址区域信息
add:AddRequest,db=Depends(get_db)
):
d = add.model_dump() #解析
r = add_address_dao(o=d, db=db) #调用添加地址函数
if not r:
raise HTTPException(status_code=500, detail='服务器繁忙,请稍后添加!') #失败返回错误信息
return AddResponse(totals=1, data=d) #成功返回响应体
@CURD.put('/emp',summary='更改就业信息')
def updata_stuinfo( #更新数据模块
up:PostRequest
,id:int
,db=Depends(get_db)
): #根据id选择对象
d = up.model_dump(exclude_unset=True) #解析
d.pop('create_time', None)
d.pop('is_deleted', None)
if not d:
raise HTTPException(status_code=400, detail='没有需要更新的字段')
r = update_emp_dao(id=id, update_data=d, db=db) #调用更新函数
if not r:
raise HTTPException(status_code=404, detail='未找到对应记录') #失败返回错误信息
return {'code': 200, 'totals': r, 'detail': '更新成功'} #成功返回信息
@CURD.delete('/emp/{id}',summary='删除就业信息')
def delete_stuinfo( #删除模块
id:int
,db=Depends(get_db)
): #根据id选择对象
rows = delete_emp_dao(id, db) #调用删除函数
if not rows:
raise HTTPException(status_code=500, detail='没有删除!') #失败返回错误信息
return {'code': 200, 'totals': rows, 'detail': '删除成功'} #成功返回信息
'''
@CURD.get('/Top5',summary='就业薪资Top5')
def salarytop5(db=Depends(get_db)):
q = db.query(Employments,Company.employment_company) \
.join(Company, Employments.company_id == Company.id) \
.filter(Employments.is_deleted == 0)\
.order_by(Employments.employment_salary.desc())\
.limit(5) .all()
return [{'学生姓名':i.sname,'学生班级编号':i.class_num,'就业时间':i.offer_recived_time,'公司':employment_company} for i,employment_company in q]
@CURD.get('/worktime',summary='学生就业时常')
def worktime(db=Depends(get_db)):
q = db.query(Employments)\
.filter(Employments.is_deleted==0)\
.all()
return [
{'学生姓名':i.sname
,'就业时常':f'{i.offer_recived_time-i.employment_open_time}'
if i.offer_recived_time and i.employment_open_time else '暂无数据'} for i in q
]
@CURD.get('/avgworktime',summary='学生平均就业时常')
def avgworktime(db=Depends(get_db)):
q = db.query(Employments)\
.filter(Employments.employment_open_time.isnot(None)
,Employments.offer_recived_time.isnot(None)
,Employments.is_deleted==0)\
.all()
if not q:
return {'平均就业时常': '暂无数据'}
total = sum((i.offer_recived_time - i.employment_open_time).total_seconds() for i in q)
avg_days=total /len(q)/86400 #一天86400秒
return {
'人数': len(q),
'平均就业时常(天)': round(avg_days, 2)
}
'''
+146
View File
@@ -0,0 +1,146 @@
from stu_jiuye.model import Employments,Address,Company
from students.model.students_model import *
from class_management.model.class_management_model import *
def add_employment_dao(o,db): #新增就业信息函数
try:
# r = db.query(Employments).filter(Employments.id == o['id'] ).all() #
o.pop('id', None) #摘除id字段,自增字段无需手动添加
o.pop('create_time', None)
o.pop('update_time', None)
o.pop('is_deleted', None)
o1 = Employments( **o) #实例化参数
db.add(o1) #添加数据进入表
except Exception as e: #失败返回
print(f'添加失败,{e}')
db.rollback() #回滚数据
return False
else:
db.commit() #提交事务,永久保存
return True
def add_company_dao(o,db): #添加公司信息函数
try:
o.pop('id', None) #摘除id字段,自增字段无需手动添加
o.pop('create_time', None)
o.pop('update_time', None)
o.pop('is_deleted', None)
o1 = Company( **o) #实例化参数
db.add(o1) #添加数据进入表
except Exception as e: #失败返回
print(f'添加失败,{e}')
db.rollback() #回滚数据
return False
else:
db.commit() #提交事务,永久保存
return True
def add_address_dao(o,db): #添加地址区域信息函数
try:
o.pop('id', None) #摘除id字段,自增字段无需手动添加
o.pop('create_time', None)
o.pop('update_time', None)
o.pop('is_deleted', None)
o1 = Address( **o) #实例化参数
db.add(o1) #添加数据进入表
except Exception as e: #失败返回
print(f'添加失败,{e}')
db.rollback() #回滚数据
return False
else:
db.commit() #提交事务,永久保存
return True
def update_emp_dao(id,update_data,db): #更新数据函数
try:
rows = db.query( Employments )\
.filter( Employments.id == id )\
.update( update_data ) #更新条件匹配id
except Exception as e:
print(f'更新失败,{e}') #返回失败信息
db.rollback() #数据回滚
return 0
else:
db.commit() #提交事务,永久保存
return rows
def get_emp_dao( #获取学生就业信息函数
# stuid,
sname
,class_id
,employment_company
,min_salary
,max_salary
,somewhere
,employment_open_start
,employment_open_end
,offer_recived_start
,offer_recived_end
,db
# ,page
# ,page_size
): #根据选择条件获取信息
q = db.query(Employments,Company.employment_company,Address.somewhere) \
.outerjoin(Company, Students.id == Employments.stuid) \
.outerjoin(Company, ClassInfo.id == Employments.class_id)\
.outerjoin(Company,Employments.company_id==Company.id)\
.outerjoin(Address,Company.address_id==Address.id)\
.filter(Employments.is_deleted == 0) #连接公司表和地址表,筛选未被删除信息
# if stuid:
# q = q.filter( Employments.id == stuid )
if sname: #根据学生姓名筛选
q = q.filter( Students.name == sname )
if class_id:
q = q.filter(ClassInfo.id == class_id)
if somewhere:
q = q.filter(Address.somewhere.like(f'%{somewhere}%'))
if employment_company: #根据公司姓名筛选
q=q.filter(Company.employment_company.like(f'%{employment_company}%'))
# r = q.offset( (page-1)*page_size ).limit( page_size ).all()
if min_salary is not None: #根据最小薪资筛选
q = q.filter(Employments.employment_salary >= min_salary)
if max_salary is not None: ##根据最大薪资筛选
q = q.filter(Employments.employment_salary <= max_salary)
if employment_open_start:
q = q.filter(Employments.employment_open_time >= employment_open_start)
if employment_open_end:
q = q.filter(Employments.employment_open_time <= employment_open_end)
if offer_recived_start:
q = q.filter(Employments.offer_recived_time >= offer_recived_start)
if offer_recived_end:
q = q.filter(Employments.offer_recived_time <= offer_recived_end)
r= q.all()
if r:
return [ {"id": i.id,
'学生姓名':i.sname,
'学生班级编号':i.class_id,
"公司": employment_company or '未就业',
"公司区域":somewhere or '-',
# "company_id": i.company_id,
"薪资":i.employment_salary or '-',
# "address_id": i.address_id,
"就业开放时间":i.employment_open_time.strftime('%Y-%m-%d') ,
"offer下发时间":i.offer_recived_time.strftime('%Y-%m-%d')
# , "创建时间": i.create_time,
# "更新时间": i.update_time,
# "是否删除":i.is_deleted
} for i,sname,class_id,employment_company,somewhere in r ] #返回信息列表
def delete_emp_dao(id,db): #删除函数
try:
rows = db.query(Employments)\
.filter(Employments.id == id,Employments.is_deleted == 0)\
.update( {'is_deleted':1} ) #软删除,is_deleted返回1
except Exception as e: #失败返回信息
print(f'删除失败,{e}')
db.rollback() #数据回滚
rows = 0 #返回0
else:
db.commit() #提交事务,永久保存
return rows
+41
View File
@@ -0,0 +1,41 @@
# from sqlalchemy import *
# from sqlalchemy.orm import declarative_base,sessionmaker
#
# db_url = "mysql+pymysql://root:123456@127.0.0.1:3306/ai0824_001?charset=utf8mb4"
# # db_url = "mysql+pymysql://root:420821@localhost:3306/student_manage_system?charset=utf8mb4"
from sqlalchemy import *
from sqlalchemy.orm import declarative_base,sessionmaker
# 导入.env参数
import os
from dotenv import load_dotenv
load_dotenv( )
DB_USER = os.getenv("DB_USER", "root")
DB_PASSWORD = os.getenv("DB_PASSWORD", "")
DB_HOST = os.getenv("DB_HOST", "localhost")
DB_PORT = os.getenv("DB_PORT", "3306")
DB_NAME = os.getenv("DB_NAME", "student_manage_system")
db_url = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4"
engine = create_engine(db_url
, pool_size=50
, echo=True
)
Base = declarative_base()
Session = sessionmaker(bind=engine
,autoflush=False
,autocommit = False
)
def get_db():
db = Session()
try:
yield db
finally:
db.close()
+24
View File
@@ -0,0 +1,24 @@
from fastapi import FastAPI,APIRouter
from stu_jiuye.CURD_api import CURD
from stu_jiuye.database import Base,engine
import stu_jiuye.model
# 合并接口
Shtudent_Jiuye = APIRouter()
Shtudent_Jiuye.include_router(CURD)
# 自测接口
app=FastAPI()
app.include_router(CURD,tags=['就业管理系统'])
if __name__=='__main__':
Base.metadata.create_all(engine)
import uvicorn
uvicorn.run('main:app', host='127.0.0.1', port=12395)
+82
View File
@@ -0,0 +1,82 @@
from databases import engine_all,Base_all,Session
from sqlalchemy import ForeignKey
from stu_jiuye.database import DATETIME,Base,Column,Integer,String,DATE
from datetime import datetime
class Employments(Base_all): #学生就业信息基类
__tablename__ = 'Employments' #表名
id = Column(Integer #主键,自增主键
, primary_key=True
, autoincrement=True
# , ForeignKey('student.id')
, comment='自增主键'
)
stuid = Column(Integer
, ForeignKey('students.id')
) #学生姓名,冗余字段
class_id = Column(Integer
, ForeignKey('classes.id')
) #学生班级,冗余字段
company_id = Column(Integer #公司id,外键匹配公司表
, ForeignKey('Company.id')
, nullable=True
)
employment_salary = Column(Integer, nullable=True) #薪资
employment_open_time = Column(DATE, nullable=True) #就业开放时间
offer_recived_time = Column(DATE, nullable=True) #offer下发时间
create_time = Column(DATETIME #创建数据时间
, default=datetime.now
)
update_time = Column(DATETIME #更新数据时间
, default=datetime.now
, onupdate=datetime.now
)
is_deleted = Column(Integer, default=0) #是否为删除信息,默认为0
class Company(Base_all): #公司信息基类
__tablename__ = 'Company' #表名
id = Column(Integer
, primary_key=True
,autoincrement=True
) #主键id,自增主键
employment_company = Column(String(50) #公司姓名
, nullable=False #非空约束
)
address_id = Column(Integer #地址id,匹配地址表
, ForeignKey('Address.id') #外键地址表id
, nullable=False #非空约束
)
create_time = Column(DATETIME
, default=datetime.now
)
update_time = Column(DATETIME
, default=datetime.now
, onupdate=datetime.now
)
is_deleted = Column(Integer, default=0)
class Address(Base_all):
__tablename__ = 'Address'
id = Column(Integer #地址id,自增主键
, primary_key=True
,autoincrement=True
)
somewhere=Column(String(50) #地址区域
, nullable=False
)
create_time = Column(DATETIME
, default=datetime.now
)
update_time = Column(DATETIME
, default=datetime.now
, onupdate=datetime.now
)
is_deleted = Column(Integer, default=0)
+43
View File
@@ -0,0 +1,43 @@
from datetime import *
from pydantic import BaseModel
class PostRequest(BaseModel): #学生就业信息请求体模型
# compname: str|None=None
sname: str|None=None
class_id: int|None=None
company_id:int|None=None
# address_id: int|None=None
employment_salary: int|None=None
employment_open_time:date|None=None
offer_recived_time:date|None=None
class EmpResponse(BaseModel): #学生就业信息响应体
code: int = 200
detail: str = 'OK'
totals: int = 0
data: str | dict | tuple | list
class CompRequest(BaseModel): #公司信息请求体模型
employment_company:str
address_id: int
class CompResponse(BaseModel): #公司信息响应体
code: int = 200
detail: str = 'OK'
totals: int = 0
data: str | dict | tuple | list
class AddRequest(BaseModel): #地址请求体
somewhere:str
class AddResponse(BaseModel): #地址响应体
code: int = 200
detail: str = 'OK'
totals: int = 0
data: str | dict | tuple | list
+63
View File
@@ -0,0 +1,63 @@
# students模块解释
## 项目层:
web框架:FastAPI(自动生成接口)
ORM:sqlalchemy
校验:pydantic
数据库:MYSQL(通过 PyMysql 驱动)
运行:uvicorn,端口暂定52000
总结:用 FastAPI 暴露一组 HTTP 接口
, 对 MySQL 里的一张 `students` 表做**增、删、改、查**
, 其中删除是**逻辑删除(软删除)**
, 数据不会被真正抹掉。
项目根目录是`main.py`是程序入口,而`students/`是一个
## main:学生管理系统的主程序文件、测试接口
1. 下面所有配置文件的接口全部导入并建表
2. 创建fastapi接口并本地运行
## databases:链接数据库配置文件
1. 链接本地mysql,实例化mysql,创建引擎,创建基类,实例化数据库会话
2. 创建函数,调用数据库会话
## api.students_api:接口文件
### 学生表students的功能接口:
1. 学生信息的查询
2. 学生信息的新增
3. 学生信息的更新
4. 学生信息的逻辑删除
## dao.students_dao:数据筛选文件
### 数据库的逻辑筛选文件:
1. 查询:get_student_dao
2. 新增:add_student_dao
3. 更新:update_student_dao
4. 逻辑删除:delete_student_dao
## model.students_model:数据库建表文件
1. 数据表建表代码
2. 字段信息: sid **PK**
, student_name
, student_phone
, class_name
, job_open_time
, offer_issue_time
, company_name
, salary
, create_date
, update_date
, is_deleted
, deleted_date
, **待建FK:** class_id/teacher_id/score_id/company_id/
## schema.students_request:请求体、数据过滤
请求体根据业务要求有不同调整,方便后续维护和更改需求
1. 查询:StudentsQuery
2. 新增:StudentsResponse
3. 更新:StudentsUpdate
4. 逻辑删除:StudentsDelete
+25
View File
@@ -0,0 +1,25 @@
show databases;
create database student_manage_system;
use student_manage_system;
show tables;
drop database student_manage_system;
drop table students;
select * from students limit 50;
desc students;
select *
from cast(student_manage_system as BINARY)
where age >30
join on
group by
having
order by
limit
View File
+65
View File
@@ -0,0 +1,65 @@
# students_api所有students的api路由、接口
from fastapi import APIRouter , Depends , HTTPException
from students.dao.students_dao import *
from students.model.students_model import *
from students.schema.students_request import *
from students.databases import *
s_api = APIRouter()
@s_api.get("/students"
,summary='学生资料查询'
,description=f'筛选条件全为空即查询所有学生资料,输入薪资会查询大于输入薪资以上的学生。\n '
f'时间格式:YYYY-MM-DD'
,response_model=list[StudentsQuery]
)
def get_students( stu:StudentsQuery = Depends()
, db=Depends(get_db)
):
s_dict = stu.model_dump(exclude_unset=True) # 没值的不要
result = get_student_dao( s=s_dict , db=db )
if result:
return result
raise HTTPException(status_code=404, detail="没有找到符合条件的学生")
@s_api.post("/students"
,summary='新增学生资料'
,description=f'学生ID自动分配,学生姓名、学生手机号和班级是必填项,不能为空。\n '
f'时间格式:YYYY-MM-DD'
,response_model=StudentsResponse )
def add_student( stu:StudentsResponse = Depends()
, db=Depends(get_db)
):
s_dict = stu.model_dump(exclude_unset=True)
result = add_student_dao( s=s_dict , db=db )
if not result:
raise HTTPException(status_code=500, detail='手机号已有学生使用或必填项为空。')
return result
@s_api.put("/students"
,summary='学生资料更新'
,description=f'请输入需要更新信息的学生ID,学生手机号不可重复,填写即更新,为空即不更新。\n '
f'时间格式:YYYY-MM-DD'
,response_model=StudentsUpdate )
def get_students( stu:StudentsUpdate = Depends()
, db=Depends(get_db)
):
s_dict = stu.model_dump(exclude_unset=True)
result = update_student_dao( s=s_dict , db=db )
if not result:
raise HTTPException(status_code=500, detail='手机号已有学生使用。')
return result
@s_api.delete("/students"
,summary='删除学生资料'
,description=f'输入需要删除的学生信息,删除条件任选,但不可全为空,工资筛选能删除大于等于输入工资的所有人。\n '
f'时间格式:YYYY-MM-DD'
,response_model=list[StudentsDelete] ) # 逻辑删除/软删除
def delete_students( stu:StudentsDelete = Depends()
, db=Depends(get_db)
):
s_dict = stu.model_dump(exclude_unset=True)
result = delete_student_dao(s=s_dict, db=db)
return result
View File
+218
View File
@@ -0,0 +1,218 @@
# student_dao:所有数据库的逻辑
from students.model.students_model import Students
from students.schema.students_request import *
from fastapi import HTTPException
# 查询
def get_student_dao( s , db):
q = db.query(Students).filter(Students.is_deleted == False)
if s.get('id'):
q = q.filter( Students.id == s.get("id") )
if s.get('num'):
q = q.filter( Students.num == s.get("num") )
if s.get('name'):
q = q.filter( Students.name == s.get("name") )
if s.get('age'):
q = q.filter( Students.age == s.get("age") )
if s.get('sex'):
q = q.filter( Students.sex == s.get("sex") )
if s.get('home_Place'):
q = q.filter( Students.home_Place == s.get("home_Place") )
if s.get('college'):
q = q.filter( Students.college == s.get("college") )
if s.get('specialty'):
q = q.filter( Students.specialty == s.get("specialty") )
if s.get('enrollment_time'):
q = q.filter( Students.enrollment_time == s.get("enrollment_time") )
if s.get('graduate_time'):
q = q.filter( Students.graduate_time == s.get("graduate_time") )
if s.get('education'):
q = q.filter( Students.education == s.get("education") )
if s.get('class_id'):
q = q.filter(Students.class_id == s.get("class_id") )
if s.get('advisor_id'):
q = q.filter(Students.advisor_id == s.get("advisor_id") )
if s.get('phone'):
q = q.filter(Students.phone == s.get("phone"))
s1 = q.all()
if s1:
return [{ 'id':i.id
, 'num':i.num
, 'name':i.name
, 'age':i.age
, 'sex':i.sex
, 'home_Place':i.home_Place
, 'college':i.college
, 'specialty':i.specialty
, 'enrollment_time':i.enrollment_time
, 'graduate_time':i.graduate_time
, 'education':i.education
# 外键字段
, 'class_id':i.class_id
, 'advisor_id':i.advisor_id
# 额外字段
, 'phone':i.phone
} for i in s1
]
# 新增
def add_student_dao( s , db):
try:
phone = s.get("phone")
s1_deleted = db.query( Students ).filter( Students.phone == phone ).first()
if s1_deleted and s1_deleted.is_deleted == False:
return False
if not phone:
return False
if s1_deleted and s1_deleted.is_deleted == True:
s1_deleted.name = s.get("name")
s1_deleted.age = s.get("age")
s1_deleted.sex = s.get("sex")
s1_deleted.home_Place = s.get("home_Place")
s1_deleted.college = s.get("college")
s1_deleted.specialty = s.get("specialty")
s1_deleted.enrollment_time = s.get("enrollment_time")
s1_deleted.graduate_time = s.get("graduate_time")
s1_deleted.education = s.get("education")
# 外键字段
s1_deleted.class_id = s.get("class_id")
s1_deleted.advisor_id = s.get("advisor_id")
# 额外字段
s1_deleted.phone = s.get("phone")
s1_deleted.create_date = datetime.now()
s1_deleted.update_date = datetime.now()
s1_deleted.is_deleted = False
s1_deleted.deleted_date = None
db.commit()
db.refresh(s1_deleted) # 从数据库再拉一次数据
return s1_deleted
s1 = Students( **s )
db.add(s1)
except Exception:
# 这里可以加日志
db.rollback()
return False
else:
db.commit()
db.refresh(s1)
return s1
# 更新
def update_student_dao( s , db):
try:
phone = s.get("phone")
if phone is not None and phone != '':
s1_con = db.query( Students ).filter( Students.phone == phone
,Students.id != s.get("id")
,Students.is_deleted == False ).first()
if s1_con:
raise HTTPException(status_code=409, detail=f'手机号已被其他学生使用:{phone}')
s1_id = db.query( Students ).filter( Students.id == s.get("id")
,Students.is_deleted == False ).first()
if not s1_id:
raise HTTPException(status_code=404, detail=f'学生不存在:id={s.get("id")}')
if s.get("num") is not None:
s1_id.name = s.get("num")
if s.get("name") is not None:
s1_id.name = s.get("name")
if s.get("age") is not None:
s1_id.age = s.get("age")
if s.get("sex") is not None:
s1_id.sex = s.get("sex")
if s.get("home_Place") is not None:
s1_id.home_Place = s.get("home_Place")
if s.get("college") is not None:
s1_id.college = s.get("college")
if s.get("specialty") is not None:
s1_id.specialty = s.get("specialty")
if s.get("enrollment_time") is not None:
s1_id.enrollment_time = s.get("enrollment_time")
if s.get("graduate_time") is not None:
s1_id.graduate_time = s.get("graduate_time")
if s.get("education") is not None:
s1_id.education = s.get("education")
# 外键字段
if s.get("class_id") is not None:
s1_id.class_id = s.get("class_id")
if s.get("advisor_id") is not None:
s1_id.advisor_id = s.get("advisor_id")
# 额外字段
if s.get("phone") is not None:
s1_id.student_phone = s.get("phone")
except HTTPException:
raise
except Exception:
db.rollback()
raise
else:
s1_id.update_date = datetime.now()
db.commit()
db.refresh(s1_id) # 从数据库再拉一次数据
return s1_id
# 逻辑删除/软删除
def delete_student_dao( s , db):
try:
if not any([ s.get("id")
,s.get("num")
,s.get("name")
,s.get("age")
,s.get("sex")
,s.get("home_Place")
,s.get("college")
,s.get("specialty")
,s.get("enrollment_time")
,s.get("graduate_time")
,s.get("education")
,s.get("class_id")
,s.get("advisor_id")
,s.get("phone")
]):
raise HTTPException(status_code=400, detail="输入需要删除的学生信息,删除条件任选,但不可全为空。")
q = db.query(Students).filter(Students.is_deleted == False)
if s.get('id'):
q = q.filter(Students.id == s.get("id") )
if s.get('num'):
q = q.filter(Students.num == s.get("num"))
if s.get('name'):
q = q.filter(Students.name == s.get("name") )
if s.get('age'):
q = q.filter(Students.age == s.get("age") )
if s.get('sex'):
q = q.filter(Students.sex == s.get("sex"))
if s.get('home_Place'):
q = q.filter(Students.home_Place == s.get("home_Place") )
if s.get('college'):
q = q.filter(Students.college == s.get("college") )
if s.get('specialty'):
q = q.filter(Students.specialty == s.get("specialty"))
if s.get('enrollment_time'):
q = q.filter(Students.enrollment_time == s.get("enrollment_time") )
if s.get('graduate_time'):
q = q.filter(Students.graduate_time == s.get("graduate_time"))
if s.get('education'):
q = q.filter(Students.education == s.get("education") )
if s.get('class_id'):
q = q.filter(Students.class_id == s.get("class_id") )
if s.get('advisor_id'):
q = q.filter(Students.advisor_id == s.get("v") )
if s.get("phone"):
q = q.filter(Students.phone == s.get("phone") )
s1_delete = q.all()
if not s1_delete:
raise HTTPException(status_code=404, detail="没有找到该学生。")
for i in s1_delete:
i.is_deleted = True
i.deleted_date = datetime.now()
# s2_deleted =[{"id":i.id,"name":i.name,"phone":i.phone}for i in s1_delete]
except HTTPException:
raise
except Exception:
db.rollback()
raise HTTPException(status_code=500, detail="Internal server error")
else:
db.commit()
return s1_delete
+35
View File
@@ -0,0 +1,35 @@
# databases链接数据库
from sqlalchemy import *
from sqlalchemy.orm import sessionmaker,declarative_base
# 导入.env参数
import os
from dotenv import load_dotenv
load_dotenv( )
DB_USER = os.getenv("DB_USER", "root")
DB_PASSWORD = os.getenv("DB_PASSWORD", "")
DB_HOST = os.getenv("DB_HOST", "localhost")
DB_PORT = os.getenv("DB_PORT", "3306")
DB_NAME = os.getenv("DB_NAME", "student_manage_system")
db_url = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}?charset=utf8mb4"
engine1 = create_engine(db_url)
Base = declarative_base()
Session = sessionmaker(bind=engine1
,autoflush=False
,autocommit=False)
def get_db():
db = Session()
try:
yield db
finally:
db.close()
+35
View File
@@ -0,0 +1,35 @@
from fastapi import FastAPI,APIRouter
from students.api.students_api import s_api
from students.databases import *
from students.model import students_model
from contextlib import asynccontextmanager
# 合并接口
Students_API = APIRouter()
Students_API.include_router(s_api)
if __name__ == "__main__":
# 自测接口
@asynccontextmanager
async def S_Fastapi(app: FastAPI):
Base.metadata.create_all(engine1)
yield
engine1.dispose()
app = FastAPI(title="学生管理系统", lifespan=S_Fastapi) # 这里必须把上面写的加上
app.include_router(s_api, tags=["students"])
def main():
print("Hello from student-manage-system!")
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=51000)
"""students api
IPv4 地址 . . . . . . . . . . . . : 192.168.6.40
默认网关. . . . . . . . . . . . . : 192.168.6.1
"""
View File
+88
View File
@@ -0,0 +1,88 @@
# students_model处理数据库模型
import datetime
from databases import engine_all,Base_all,Session
from sqlalchemy import *
# from students.databases import *
from datetime import datetime
from sqlalchemy.dialects.mysql import DATETIME
from students.schema.students_request import SexEnum , EducationEnum
from sqlalchemy import Enum,ForeignKey,Integer,String
class Students(Base_all):
__tablename__ = 'students'
id = Column(Integer
, primary_key=True
, autoincrement=True
, comment='学生ID,自增主键'
)
num = Column(String(50)
, nullable=False
, unique=True
, comment='学生编号'
)
name = Column(String(50)
, comment='学生姓名'
, nullable=False )
age = Column(Integer
, comment='年龄'
, nullable=False)
sex = Column(Enum(SexEnum, values_callable=lambda e: [i.value for i in e])
, comment='性别'
, nullable=False)
home_Place = Column(String(50)
, comment='籍贯')
college = Column(String(50)
, comment='毕业院校')
specialty = Column(String(50)
, comment='专业')
enrollment_time = Column(DATETIME
, comment='入学时间')
graduate_time = Column(DATETIME
, comment='毕业时间')
education = Column(Enum(EducationEnum, values_callable=lambda e: [i.value for i in e])
, comment='学历'
, nullable=True)
# 外键字段
class_id = Column(Integer
, ForeignKey("classes.id")
, comment='学生班级'
, nullable=False )
advisor_id = Column(Integer
, ForeignKey("teachers.id")
, comment='顾问ID')
# 额外字段
phone = Column(String(11)
, comment='学生手机号'
, nullable=False
, unique=True )
# 通用字段
create_date = Column(DATETIME(fsp=6)
, default=datetime.now
, comment='创建时间' )
update_date = Column(DATETIME(fsp=6)
, default=datetime.now
, comment='更新时间' )
is_deleted = Column(INTEGER
, default=False
, comment='是否删除:0-正常,1-已删除' )
deleted_date = Column(DATETIME(fsp=6)
, default=None
, comment='删除时间' )
View File
+90
View File
@@ -0,0 +1,90 @@
# students_schema:请求、响应的模型框架
from pydantic import BaseModel , Field , field_validator
from datetime import datetime
import enum
class SexEnum(enum.Enum):
m = '男'
f = '女'
# 查询
class StudentsQuery(BaseModel):
id:int | None = None
num:str | None = None
name:str | None = None
age:int | None = None
sex:SexEnum | None = None
home_Place:str | None = None
college:str | None = None
specialty:str | None = None
enrollment_time:datetime | None = None
graduate_time:datetime | None = None
education:str | None = None
# 外键字段
class_id:int | None = None
advisor_id:int | None = None
# 额外字段
phone:str | None = None
class EducationEnum(enum.Enum):
z = '专科'
ben = '本科'
s = '硕士'
bo = '博士'
# 新建
class StudentsResponse(BaseModel):
num:str = Field(..., min_length=1, max_length=50, description='学生编号' )
name:str = Field(..., min_length=1, max_length=50, description='学生姓名' )
age:int = Field(..., gt=0, lt=200, description='学生年龄')
sex:SexEnum
home_Place:str | None = None
college:str | None = None
specialty:str | None = None
enrollment_time:datetime | None = None
graduate_time:datetime | None = None
education:EducationEnum
# 外键字段
class_id:int = Field(..., gt=0, description='班级ID')
advisor_id:int | None = None
# 额外字段
phone:str = Field(...,min_length=11,max_length=11,description='学生手机号')
# 更新
class StudentsUpdate(BaseModel):
id:int = Field(..., gt=0 , description='学生ID' )
num:str | None = None
name:str | None = None
age:int | None = None
sex:SexEnum | None = None
home_Place:str | None = None
college:str | None = None
specialty:str | None = None
enrollment_time:datetime | None = None
graduate_time:datetime | None = None
education:str | None = None
# 外键字段
class_id:int | None = None
advisor_id:int | None = None
# 额外字段
phone:str | None = None
# 删除
class StudentsDelete(BaseModel):
id:int | None = None
num:str | None = None
name:str | None = None
age:int | None = None
sex:SexEnum | None = None
home_Place:str | None = None
college:str | None = None
specialty:str | None = None
enrollment_time:datetime | None = None
graduate_time:datetime | None = None
education:str | None = None
# 外键字段
class_id:int | None = None
advisor_id:int | None = None
# 额外字段
phone:str | None = None
Generated
+101
View File
@@ -396,6 +396,12 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
]
[[package]]
name = "like"
version = "1.7.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0d/d1/5284ae5ae97876808622090d858098e11787469c601b679274e17bcb7b34/like-1.7.1.zip", hash = "sha256:2614e24923fb934955b6abeeda6d13db567d7901da44bc01480e14838618ded2", size = 925, upload-time = "2016-12-19T04:01:13.729Z" }
[[package]]
name = "markdown-it-py"
version = "4.2.0"
@@ -469,6 +475,68 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
]
[[package]]
name = "numpy"
version = "2.5.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/13/01/11703282db468b85f6f7b8c7f22d058de5970d5c7e60a3a8aaa313c3de36/numpy-2.5.3.tar.gz", hash = "sha256:df2d5874ff183595a4ba404edd04f6bd9b5505c1d7708573f6a6c17489a67563", size = 20791231, upload-time = "2026-09-06T16:27:47.073Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/79/e5/8fb89cd46d14e35699d13bf943a5f5f441ecee8667120a1f6105ab89e349/numpy-2.5.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:66a78fe4556c60aceda5916f9eacd638b18e9e681016ec302dcb4682d6d4d034", size = 16991061, upload-time = "2026-09-06T16:25:00.411Z" },
{ url = "https://files.pythonhosted.org/packages/2f/06/9dc9e48b5e5e941c8b10350c5ff2d721da42a20517d911d15544246775ff/numpy-2.5.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92f30e89b8ee0ecf363033576c422b2f58fed6a80bed0aa48dff6d14c654663e", size = 12003676, upload-time = "2026-09-06T16:25:03.475Z" },
{ url = "https://files.pythonhosted.org/packages/ab/2a/98282aa5b8f58b1157d440bb6282eed47e3632a5de53a714fbab17e659fe/numpy-2.5.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f9a2353b37a1a9e78fd82b27ad7e2a32a2d036604d18f02b05e3136c62ca3b09", size = 5439695, upload-time = "2026-09-06T16:25:05.978Z" },
{ url = "https://files.pythonhosted.org/packages/a1/f9/b6533d777be9d6ffd29dc1be0867e563e6e8cc9a220ff1b716adc317f060/numpy-2.5.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:ccbc4665079665c3cf3bab4db9f6b095370cd6437d66be549b6c2a1fd19e1958", size = 6779395, upload-time = "2026-09-06T16:25:08.599Z" },
{ url = "https://files.pythonhosted.org/packages/73/85/735720d04ec197c5dcfacdfc9922667c7f1f5f496a279b7ba4d7c74c4cc7/numpy-2.5.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c76d5dde9f445058f83d0c02af00557a4db91de9a9a57c0df87d1535001d654b", size = 15681750, upload-time = "2026-09-06T16:25:11.173Z" },
{ url = "https://files.pythonhosted.org/packages/3a/1b/3b16a9bc514a440a7a0883684111dcb1ef1aee960af2ca95da8fc775f124/numpy-2.5.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5fa86b80fd24bcd1aff83ad23be44ea323de3f787be8f8b15d4a65621e25321", size = 16708577, upload-time = "2026-09-06T16:25:14.171Z" },
{ url = "https://files.pythonhosted.org/packages/69/c4/386f397831b07328b639c96c5b62719346cf4baf07c68d927239752b1534/numpy-2.5.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd4cb9ad3c7889b9b3fe0a9a9fb5d2ed26f9879bff2608d9f01aed147a20d231", size = 17042047, upload-time = "2026-09-06T16:25:17.582Z" },
{ url = "https://files.pythonhosted.org/packages/5f/3e/a700ecbf36e85ae8328fd3b0e12eeddc22ed6358a64cb2bd913e0d195d65/numpy-2.5.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1302b90c0e52281681b2975adfe8a860cb7b12216a27b4b0b4207c44bf7bccf0", size = 18465724, upload-time = "2026-09-06T16:25:20.949Z" },
{ url = "https://files.pythonhosted.org/packages/41/ee/38e785e88a4045f6ad1d1f2808dcdfafdca48c760260c0587bf171e29fc9/numpy-2.5.3-cp313-cp313-win32.whl", hash = "sha256:1c80eabb4035ecf4ca9cd49cde8a9fdd69a729e63e6474887d1523ade7aa277f", size = 6129003, upload-time = "2026-09-06T16:25:23.664Z" },
{ url = "https://files.pythonhosted.org/packages/f3/ec/100f2b1794ede74a9b3d7ec6b9736927f56713414c1dfe19ab6c383494bf/numpy-2.5.3-cp313-cp313-win_amd64.whl", hash = "sha256:71cad2b2a7451ab79d8f5e71b453485b6775963d5cf794179144a7463fe6e8ec", size = 12560965, upload-time = "2026-09-06T16:25:26.602Z" },
{ url = "https://files.pythonhosted.org/packages/80/b1/7dc825ca94c12acebbce4c37caa5e198695eb31424bc579679f32b1bb49d/numpy-2.5.3-cp313-cp313-win_arm64.whl", hash = "sha256:8e4dd766076855b5ff7ea52fa5f07ce26286726e0f8bff446b7739d02e6ea204", size = 10482343, upload-time = "2026-09-06T16:25:29.772Z" },
{ url = "https://files.pythonhosted.org/packages/70/78/cf416f15dc29375a229d9dfebf8db6e313f291580b39fa1a568b6052bb07/numpy-2.5.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:350ba9783ce969cf9f7ce6e6a9a58e1a6e2a19ca025b7ee448c4db727706212a", size = 16998686, upload-time = "2026-09-06T16:25:33.171Z" },
{ url = "https://files.pythonhosted.org/packages/9e/59/abcc2d8def4fd60eec7d87f92d27c13448ffd9ab14339bcc63a0d7a2fdea/numpy-2.5.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:012e66aca395d795496446e52aeeb5866312a5d4d3f27da270e5a0b43f70dc5c", size = 12013862, upload-time = "2026-09-06T16:25:36.748Z" },
{ url = "https://files.pythonhosted.org/packages/94/75/4640d2d6e4b64a049e48425a82728a41ef4adb61332d2cba68055774878b/numpy-2.5.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:adc1ada2662f8a5f960b8a10d9986897e7499ef07e06d4cfe7197f8cce923c07", size = 5449793, upload-time = "2026-09-06T16:25:39.476Z" },
{ url = "https://files.pythonhosted.org/packages/96/cd/625b57ae33d4ca560f32cc0b47b4a5922146d9beb998ddf773900d440a73/numpy-2.5.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:54a115e5a73b8fc44f0cebef486365a1894b5c9760685d4558b72b7c3eb846e0", size = 6785176, upload-time = "2026-09-06T16:25:42.069Z" },
{ url = "https://files.pythonhosted.org/packages/9c/72/12918652e7912ef9751e8694c88820fcd1908e0618cb23f5f3caa6004b7b/numpy-2.5.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be5a8381859b6da607c84f4f7d6847725f1cf1853ef8a2c9e115b7d58bef47dc", size = 15703377, upload-time = "2026-09-06T16:25:45.135Z" },
{ url = "https://files.pythonhosted.org/packages/45/8f/9beacf79ca7c650688ad0baa80931adb988fe6e6e5d5903c23cc3dbd70eb/numpy-2.5.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b0521d0f4aebb6e06189451025fa17a913287b13c03d5fe05c017333b654ea5b", size = 16711928, upload-time = "2026-09-06T16:25:48.461Z" },
{ url = "https://files.pythonhosted.org/packages/09/8d/41d0a56e1ac4c87495c897a211b1368691b7237aadabec8b3b8f3a74d48f/numpy-2.5.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9deb49575e5b0b94ed72c8a64ec4d033381adc27e9060ae842971f697ba96104", size = 17059507, upload-time = "2026-09-06T16:25:51.873Z" },
{ url = "https://files.pythonhosted.org/packages/08/1e/0dfbc5cc251d54e2af790f254d24ec38637fa97ec7d5d11de7ffed787098/numpy-2.5.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b00eefbcf0f292945c4b4dec2ae845389ef5bcdcd596e6e4328051db5b5ba694", size = 18471002, upload-time = "2026-09-06T16:25:55.233Z" },
{ url = "https://files.pythonhosted.org/packages/b5/2c/dfa40f6991f8185c8c30ffd023dfcbb11888e823cfab9557b920f3bb7bed/numpy-2.5.3-cp314-cp314-win32.whl", hash = "sha256:c2381f82999704f818e2c987a865050e285ec3621262c66d40f5a96c8f899f8e", size = 6180485, upload-time = "2026-09-06T16:25:58.157Z" },
{ url = "https://files.pythonhosted.org/packages/a4/73/d2c08231e4fde7e415501fd02c715d96e98599b2d8384445933944152984/numpy-2.5.3-cp314-cp314-win_amd64.whl", hash = "sha256:2c25dfa72943e4336ddb6b0ee4277b47a0c85bede0807530ec68103bf58e2c10", size = 12698179, upload-time = "2026-09-06T16:26:00.789Z" },
{ url = "https://files.pythonhosted.org/packages/5c/e9/dcdcc9b95cf5f49815055573aee1b11cfbf5299f38a180e437ded050810f/numpy-2.5.3-cp314-cp314-win_arm64.whl", hash = "sha256:15aa985ac73a8db02db7663381aa109510449d3819d37206caed27b33a65a8a6", size = 10769383, upload-time = "2026-09-06T16:26:04.011Z" },
{ url = "https://files.pythonhosted.org/packages/49/c4/af8bc08a7ef4e1529a7c0cf24969accce316b783999802089a581ec99272/numpy-2.5.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ac7bb1c52d445bd4f8f7f97fefe6abc3a084dc4d63df50d79b17fa2b78e89297", size = 12132668, upload-time = "2026-09-06T16:26:07.138Z" },
{ url = "https://files.pythonhosted.org/packages/c5/ae/0f15eb56d4ec5e13c1f7ff04ff407f997d1acbadb45d3e1f2e2645a8f43c/numpy-2.5.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e6ab667ba76450084eb64013762c438ea76d9d29cc676dcd6c2e9892ba37f841", size = 5568580, upload-time = "2026-09-06T16:26:09.828Z" },
{ url = "https://files.pythonhosted.org/packages/23/fb/c72a8f25d4b6e96c354e7ab45ace3b27dc11e5d6a13b6c7d0cd6b08bf112/numpy-2.5.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:f7fabeb6cea87d65f3b926de33d03fb016cfdc29314c90974383b5582ae72891", size = 6882634, upload-time = "2026-09-06T16:26:12.524Z" },
{ url = "https://files.pythonhosted.org/packages/07/a9/968c90ed2ab15060c338e8137f1215b5a60756ae07328e0a60d1c6734df4/numpy-2.5.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fb6f8fb9ff0b3a69f52c66ce397b0246583e9f28616231b0e32ca49259a5fa6", size = 15748923, upload-time = "2026-09-06T16:26:15.092Z" },
{ url = "https://files.pythonhosted.org/packages/59/08/9df04103947b95e3b6b1f2ed1a70521f325647a31b82da6a2aae3a485508/numpy-2.5.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93e1f5447e2b1e479d7bd74701e84746b86450cff1fc368b132d195e2b8f8211", size = 16746748, upload-time = "2026-09-06T16:26:18.43Z" },
{ url = "https://files.pythonhosted.org/packages/41/a0/14c8d5fe5b53a334aabb653deb391c0fef49558f491880ea300ed6785224/numpy-2.5.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c00abe94c1a69d75d827dcf1c025b25c8a45d230b3bcd77a9020883a1b047653", size = 17111561, upload-time = "2026-09-06T16:26:22.113Z" },
{ url = "https://files.pythonhosted.org/packages/c4/a6/d7e96e42f01522e154c32489640f16dfc4f6181d165d05fc3bec8c2c4999/numpy-2.5.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:536f963710a4e63934d80ac0dc4f478804a83e9a84b6828018f25d09953ada33", size = 18513945, upload-time = "2026-09-06T16:26:25.401Z" },
{ url = "https://files.pythonhosted.org/packages/25/39/3453afb7119d0449ef11c886874120ff180e2c337760e0e2d88f70f1a945/numpy-2.5.3-cp314-cp314t-win32.whl", hash = "sha256:4c8a6d2ebce6305fd82fbefca827775437147052a976ee7c94b36a0c1b52ac6c", size = 6335421, upload-time = "2026-09-06T16:26:28.175Z" },
{ url = "https://files.pythonhosted.org/packages/99/01/22815d2b19a1a746b1d45205cffebb3fe511a18acb75fba6c88491fc9894/numpy-2.5.3-cp314-cp314t-win_amd64.whl", hash = "sha256:9a37475425b431b4d060f23b4f52cd2f3aef6bc7c654bd760adf0040eec9d435", size = 12896420, upload-time = "2026-09-06T16:26:31.265Z" },
{ url = "https://files.pythonhosted.org/packages/fa/ee/a7cbba67eeaff038dc29ca8b98a88396c8b0cc9c89d4924f4a27a5c9150b/numpy-2.5.3-cp314-cp314t-win_arm64.whl", hash = "sha256:2d8240cb4c16fd831074aa2b2cf9fc54664d826341d61c372245b96a74a49a9a", size = 10857177, upload-time = "2026-09-06T16:26:34.167Z" },
{ url = "https://files.pythonhosted.org/packages/45/56/78194492883ff5eec90423fe56a3a44b154da047d88a6307f629713c584f/numpy-2.5.3-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:a6391fafaba97500887132cd582abc6e19452b1ac775a47caa7b24490e152058", size = 16996531, upload-time = "2026-09-06T16:26:37.287Z" },
{ url = "https://files.pythonhosted.org/packages/11/39/dd55c0af90bbab564b09ae3b0aa60ec5c02b900fa4f1ba23440525c8b32d/numpy-2.5.3-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:09d5a423c71ad5feb5625844ad58050e35df43871004b52ac9c0ad44a56775be", size = 12012569, upload-time = "2026-09-06T16:26:40.707Z" },
{ url = "https://files.pythonhosted.org/packages/b6/51/04f67d32e4862b281b1cb84ceeaed3421189a84fb6fb51a391cd6d5009f7/numpy-2.5.3-cp315-cp315-macosx_14_0_arm64.whl", hash = "sha256:f9579f383d1bf9df80081e72760e84960a7fd4f88cf0c9e535a8597c9bb646f5", size = 5448498, upload-time = "2026-09-06T16:26:43.435Z" },
{ url = "https://files.pythonhosted.org/packages/a3/c9/25b4dc0dd1344ec26c7319e84fd4e9809d2b5628f4e12decd618036e5178/numpy-2.5.3-cp315-cp315-macosx_14_0_x86_64.whl", hash = "sha256:86bff898a431c0fb71f7610b75726e75a54d47b37edc9d537f48de63bb3c0b90", size = 6783026, upload-time = "2026-09-06T16:26:46.374Z" },
{ url = "https://files.pythonhosted.org/packages/fc/c7/29285be1e5232a6e7ee3268a33c85843f5a8ee93350c6465cddd66ebbf76/numpy-2.5.3-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f3ed25271581281f2fccb1adcedfcde4c07362eec69189b50baf6f90e3ae159", size = 15697322, upload-time = "2026-09-06T16:26:49.415Z" },
{ url = "https://files.pythonhosted.org/packages/55/49/bbad5335fb4996a16881f853ff3e0ba582f01720e55c89b1c06b8fc42a90/numpy-2.5.3-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ffdc76bfcae6b255dff75202c5e7feaf95b40246bc0a17944facc1fecf9f79ab", size = 16708995, upload-time = "2026-09-06T16:26:53.127Z" },
{ url = "https://files.pythonhosted.org/packages/ef/e9/1df35483760b04a65ea44669f89dc64f30e5aca098b48ceb8b1310b0e0fe/numpy-2.5.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:116f96cadd935c6122e9228d676fe7ede19e741f5c8bb1c3cddbe0c51ccebea2", size = 17052508, upload-time = "2026-09-06T16:26:56.464Z" },
{ url = "https://files.pythonhosted.org/packages/b8/99/66e54da8265cc8be8a7382bf96edce17aaa2837d6f484432025932a3caa5/numpy-2.5.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:09ffa5d903faeaa5c4dd05009cf81c8bab9f2cb37c548b8d39b65b4cfa7c97f7", size = 18468224, upload-time = "2026-09-06T16:26:59.966Z" },
{ url = "https://files.pythonhosted.org/packages/01/bc/b5e90a91c115168d793dfd2ad9c69c438c2fe7a13a437e770bc5b078e732/numpy-2.5.3-cp315-cp315-win32.whl", hash = "sha256:e01c918ac3d48e18a927cf7b14a26a3e29ff2bdf2eacb976da0aecd6a43ed034", size = 6179919, upload-time = "2026-09-06T16:27:03.166Z" },
{ url = "https://files.pythonhosted.org/packages/37/ea/780748fd3985109075514ef8fc64cd25f943e40dde13a6d59141eb268fc8/numpy-2.5.3-cp315-cp315-win_amd64.whl", hash = "sha256:e931e4f499e0dc7ef29d269a8e5b35dd722e5d14be07df6240166ea7c6532fae", size = 12697656, upload-time = "2026-09-06T16:27:06.153Z" },
{ url = "https://files.pythonhosted.org/packages/b3/16/407be69a2a87c8cab64d95975a8977a426a29e138f07e276ec258f0fe4e5/numpy-2.5.3-cp315-cp315-win_arm64.whl", hash = "sha256:26e15e4aecd8617dfbaecb37d223e365d7b39411fba20454be2670a96aa74cb5", size = 10767601, upload-time = "2026-09-06T16:27:09.297Z" },
{ url = "https://files.pythonhosted.org/packages/44/bf/a97ffb01e41d50a32a9177aef942a4d0e389a3daf451d04e5f38ef6afb87/numpy-2.5.3-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:6cef4bb1706dfec49243c05d921eefb4e190d41e2528b30d8035ea1f36b4c24a", size = 17090092, upload-time = "2026-09-06T16:27:12.907Z" },
{ url = "https://files.pythonhosted.org/packages/d1/24/136c02f2c2af9a067a84d0c3aa10c99012c0476fa5066732fa4a4202557d/numpy-2.5.3-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:d1c89973648c85069c5046ad460f7b8a00218b29a2e42359ac8cc63e9ab94832", size = 12129429, upload-time = "2026-09-06T16:27:16.089Z" },
{ url = "https://files.pythonhosted.org/packages/fe/6c/b47582d6597789bf946d5efbeb6b9e56fd8bcbd5efc6fbf51dbe1ea31eb3/numpy-2.5.3-cp315-cp315t-macosx_14_0_arm64.whl", hash = "sha256:214045a5bf00113a146ab9ee9730c44501af6723cdf1f6830932f7b5ef2e7af0", size = 5565452, upload-time = "2026-09-06T16:27:19.868Z" },
{ url = "https://files.pythonhosted.org/packages/be/b4/ef3cc6da73774202d4deae16bb321fd8298a4e0561e3539f8c4be237d916/numpy-2.5.3-cp315-cp315t-macosx_14_0_x86_64.whl", hash = "sha256:8617bbfae4486cf99c9f899966699428d19da931d06ca94ad3da986c76e15997", size = 6876736, upload-time = "2026-09-06T16:27:22.232Z" },
{ url = "https://files.pythonhosted.org/packages/9e/24/e3813329498596cb842703dcacac1741612ed9fb9c4e6a3e0c7e2ebbc597/numpy-2.5.3-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:595d020938c84e320bcf40ad71089e108eac0d377cd018e14a8c094f39e98d85", size = 15745777, upload-time = "2026-09-06T16:27:25.181Z" },
{ url = "https://files.pythonhosted.org/packages/4a/9e/4e7a07fd0776dc2210cdacf2010be8665194d094defc10c419d7dea794cc/numpy-2.5.3-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f24021b9f22bc6301c37b196974a92c1c18dccedb6fef3dd252e95f2d6adbe4", size = 16746949, upload-time = "2026-09-06T16:27:28.576Z" },
{ url = "https://files.pythonhosted.org/packages/91/db/01674c0e20335057813a00c2ebd546ed25bff9ed7914f9bced00f8c55d94/numpy-2.5.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:71b39d9f935b6ec0f8753e3e2afb51e3efba6f2e05b68b32a40754d24bcd4a3c", size = 17108994, upload-time = "2026-09-06T16:27:31.946Z" },
{ url = "https://files.pythonhosted.org/packages/45/7a/584c5e71f8d378e57cac0b033891ed65c683ef90573ba4854e8c28203db0/numpy-2.5.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:6b05c171afb3aa07adbd20abc00aea86fe375beb0fdb9ef780ec5b7f63bab1c0", size = 18512266, upload-time = "2026-09-06T16:27:35.196Z" },
{ url = "https://files.pythonhosted.org/packages/a1/d2/4e1014173aa3c55e6a756e0e567290743a6ab33a288460374d7ef6bcd239/numpy-2.5.3-cp315-cp315t-win32.whl", hash = "sha256:f54660b0eb6b0b9f36e7fe1cdfdff472028dd0d14acd9b9b65098efbad059469", size = 6330292, upload-time = "2026-09-06T16:27:38.149Z" },
{ url = "https://files.pythonhosted.org/packages/6c/b0/ff5658a58199b7bcaad87bf260eef6713d9d42cca4e028f935b4fc5fbac6/numpy-2.5.3-cp315-cp315t-win_amd64.whl", hash = "sha256:1aad64d99730d013cfc6debafed22783b4fc5a7f4b8bc744d2d8cf7dcc880551", size = 12884918, upload-time = "2026-09-06T16:27:40.965Z" },
{ url = "https://files.pythonhosted.org/packages/fb/0b/b12a2df5d1b774bd9007a6fdff9381145b6223d37f11afc9c37ab0efd9a1/numpy-2.5.3-cp315-cp315t-win_arm64.whl", hash = "sha256:befa1ae5bd6030b3f512b43ff3fa5290bbed6b84411a44244b14adf835f5b89d", size = 10850807, upload-time = "2026-09-06T16:27:43.868Z" },
]
[[package]]
name = "pip"
version = "26.2.1"
@@ -790,6 +858,31 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" },
]
[[package]]
name = "spglib"
version = "2.7.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "numpy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a9/06/7964acb4c444191376bd87f91579475fbe7623ca943cce40cee8fb7f2c36/spglib-2.7.0.tar.gz", hash = "sha256:c40907a42c9dc45572f46740bf95412f84fb0eda30267e31665d104a4bde6627", size = 2366134, upload-time = "2025-12-29T09:48:26.42Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/1c/a0fe8c0523a0e7d608f49f09895e5c599329265c9bfacd269a21458b7564/spglib-2.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ab061ea6a3c3c25a1d0018b09c333c0458792036d3f45d892bd52793ed1f1bda", size = 911085, upload-time = "2025-12-29T09:47:51.606Z" },
{ url = "https://files.pythonhosted.org/packages/2a/34/cb3c522c4aaf6ce319b37bbec71d373b9e2cf0bcfe7d42c365cd6c113b4b/spglib-2.7.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be28673e90f7a6c7770f73c57e529d2bdbb373d06d26ee5e90991b548e9238aa", size = 946857, upload-time = "2025-12-29T09:47:53.059Z" },
{ url = "https://files.pythonhosted.org/packages/9e/64/3b1213f2f655ff143ed142292b47ec3f1f9bda8641e659a7e33c4cf0e8a9/spglib-2.7.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f627a4ed6f2396ed6e3e8eaf33a53ad143c8ffb8756a84a640f4569ac5ffa2a7", size = 962470, upload-time = "2025-12-29T09:47:54.878Z" },
{ url = "https://files.pythonhosted.org/packages/5c/3a/c51883ce739a00f9f60196f3dcb4ed91b690299a4ec64defd8ec5b2c5899/spglib-2.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:c76411bc1b96cd87c8733994747c7692512b583bb4ef89a65463ff4255221c11", size = 671073, upload-time = "2025-12-29T09:47:56.887Z" },
{ url = "https://files.pythonhosted.org/packages/35/78/3f9ec6ae93a48527dce0eceb6eeab74e6ad1fb2977adb5cbdfc03d43193c/spglib-2.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:0d8ecf030d13d67c4cc272423e5652b74eda57f86a0b118e007f6d12974cc256", size = 646711, upload-time = "2025-12-29T09:47:58.697Z" },
{ url = "https://files.pythonhosted.org/packages/1b/47/86e3c15c3e1c252bde40a794eea4742c142f23fc5f9c3d7551f083c1fa20/spglib-2.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:95e3dd7ef992ff8a88f6ef2e5909aaa60ecb479004cc1f73c1e6285d54227960", size = 911712, upload-time = "2025-12-29T09:48:01.14Z" },
{ url = "https://files.pythonhosted.org/packages/05/61/ab2447bb47fa69934adc2fc2d13f771dedd3b2fd3171c95307446c948f01/spglib-2.7.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97e0fcea2db3915bd973fdd2cc0a757b1f99bda71ce815da333d75ad1ffc3eb1", size = 947528, upload-time = "2025-12-29T09:48:03.258Z" },
{ url = "https://files.pythonhosted.org/packages/9d/69/898d9e005131b0b1c7e5dce2b79f36aeb20ec4d3a88cca596b522a0fa4df/spglib-2.7.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:39b978c08ef2ebc0eaba833c488fc4c0f9b1fc0f50d4a8584f176741eea69376", size = 962474, upload-time = "2025-12-29T09:48:05.617Z" },
{ url = "https://files.pythonhosted.org/packages/c8/56/7b25ee5348722dc93ca245ed950f1a89f8a944906140629055f394c072a4/spglib-2.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:5f334b4b66c8aafd583fafab5b15a56e27efdd2dc6cb1064dfcd0fe59ae130f4", size = 679679, upload-time = "2025-12-29T09:48:15.393Z" },
{ url = "https://files.pythonhosted.org/packages/20/37/eda9a34f25b13e47298fa1b94cc4dfd8b0fcfc46c7d63ea046aa1bf91fe7/spglib-2.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:b032842fc223de46d2ef7d220459e1a61ed90329ac2e72818c605f1fc87451b8", size = 656403, upload-time = "2025-12-29T09:48:17.027Z" },
{ url = "https://files.pythonhosted.org/packages/39/af/1c8d0f98d07969b7fa7323d522732124d88caf4ee3b680ef59120bd7b229/spglib-2.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b7e29c796cfdadcc3857aef330acc19b9bc50c83e9911fb23b28390e7c80bae5", size = 920791, upload-time = "2025-12-29T09:48:07.085Z" },
{ url = "https://files.pythonhosted.org/packages/b5/c6/89a3f31f831efc4108a19f110873559990b72186745cd3e151de28b256cc/spglib-2.7.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b6ca88bb6e604bc8f63efe87b3b2470c2e25f56988b775bd332cefa8866f5c5", size = 946881, upload-time = "2025-12-29T09:48:09.154Z" },
{ url = "https://files.pythonhosted.org/packages/7e/e9/1ca63db2cebd381bd6b27ae309f25d270e70928359a6f0360db09b77894e/spglib-2.7.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50629939a9cd6fa3df5a12f6f025ceb3c78534284f875371574c360e4ccaf5e1", size = 963803, upload-time = "2025-12-29T09:48:12.478Z" },
{ url = "https://files.pythonhosted.org/packages/28/97/459b37c3802633f77c883883c75f5d4429b601ae8d930410b999c4e1dafb/spglib-2.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:cb77daaf9dd5d48d523a888f37cebd47fa63ff28dfcf1aac2b031b914f9ed55a", size = 696536, upload-time = "2025-12-29T09:48:13.885Z" },
]
[[package]]
name = "sqlalchemy"
version = "2.0.54"
@@ -842,17 +935,25 @@ version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "fastapi", extra = ["standard"] },
{ name = "like" },
{ name = "pip" },
{ name = "pymysql" },
{ name = "python-dotenv" },
{ name = "spglib" },
{ name = "sqlalchemy" },
{ name = "uvicorn" },
]
[package.metadata]
requires-dist = [
{ name = "fastapi", extras = ["standard"], specifier = ">=0.141.1" },
{ name = "like", specifier = ">=1.7.1" },
{ name = "pip", specifier = ">=26.2.1" },
{ name = "pymysql", specifier = ">=1.2.3" },
{ name = "spglib", specifier = ">=2.7.0" },
{ name = "python-dotenv", specifier = ">=1.2.3" },
{ name = "sqlalchemy", specifier = ">=2.0.54" },
{ name = "uvicorn", specifier = ">=0.53.0" },
]
[[package]]