From f0831731789c936ad3c210b42f699a50ac0ebdbc Mon Sep 17 00:00:00 2001 From: longsihua2003 <1161443436@qq.com> Date: Mon, 6 May 2024 01:36:44 +0800 Subject: [PATCH 1/6] fastapi demo --- ecjtu/ecjtu_api/api.py | 42 ++++++++++++++++++++ ecjtu/ecjtu_api/auth.py | 12 ++++++ ecjtu/ecjtu_api/ecjtu_schema.py | 5 +++ ecjtu/ecjtu_api/middle.py | 32 +++++++++++++++ ecjtu/ecjtu_api/respose_result.py | 66 +++++++++++++++++++++++++++++++ ecjtu/ecjtu_api/server.py | 18 +++++++++ ecjtu/server.py | 0 7 files changed, 175 insertions(+) create mode 100644 ecjtu/ecjtu_api/api.py create mode 100644 ecjtu/ecjtu_api/auth.py create mode 100644 ecjtu/ecjtu_api/ecjtu_schema.py create mode 100644 ecjtu/ecjtu_api/middle.py create mode 100644 ecjtu/ecjtu_api/respose_result.py create mode 100644 ecjtu/ecjtu_api/server.py delete mode 100644 ecjtu/server.py diff --git a/ecjtu/ecjtu_api/api.py b/ecjtu/ecjtu_api/api.py new file mode 100644 index 0000000..30b7113 --- /dev/null +++ b/ecjtu/ecjtu_api/api.py @@ -0,0 +1,42 @@ +from fastapi import FastAPI, Depends,Header +from fastapi.security import OAuth2PasswordRequestForm + +import middle +from ecjtu_schema import UserSchema, UserLoginSchema +from respose_result import ResponseResult +from auth import encode,decode + +from ecjtu.client import ECJTU + +app = FastAPI(title="ECJTU API", description="API for ECJTU") + +app.add_middleware(middle.MyMiddleware) + +@app.post("/login") +def login(user: UserLoginSchema): + client = ECJTU(user.stud_id, user.password) + try: + client.login() + except Exception as e: + return ResponseResult.error(str(e)) + token = encode(user.stud_id, user.password) + return ResponseResult.success({"token": token}) + +# gpa接口 +@app.get("/gpa") +def gpa(token: str = Header(None)): + stud_id, password = decode(token) + client = ECJTU(stud_id, password) + try: + gpa = client.gpa.today() + except Exception as e: + return ResponseResult.error(str(e)) + return ResponseResult.success(dict(gpa)) + +# 启动api服务 +def start_api_server(): + import uvicorn + uvicorn.run(app, host="127.0.0.1", port=8080) + +if __name__ == "__main__": + start_api_server() \ No newline at end of file diff --git a/ecjtu/ecjtu_api/auth.py b/ecjtu/ecjtu_api/auth.py new file mode 100644 index 0000000..2c065df --- /dev/null +++ b/ecjtu/ecjtu_api/auth.py @@ -0,0 +1,12 @@ +import base64 +# from ecjtu.client import _get_enc_password +KEY = "zxcvbnmasdgfhjklpoiuytrewq" + +def encode(stud_id, pwd): + # enc_pwd = _get_enc_password(pwd); + token = base64.b64encode(f"{stud_id}:{pwd}".encode()).decode() + return token + +def decode(token): + stud_id, enc_pwd = base64.b64decode(token.encode()).decode().split(":") + return stud_id, enc_pwd \ No newline at end of file diff --git a/ecjtu/ecjtu_api/ecjtu_schema.py b/ecjtu/ecjtu_api/ecjtu_schema.py new file mode 100644 index 0000000..1fb7bd9 --- /dev/null +++ b/ecjtu/ecjtu_api/ecjtu_schema.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel, Field + +class UserLoginSchema(BaseModel): + stud_id: str = Field(..., min_length=1) + password: str = Field(..., min_length=1) \ No newline at end of file diff --git a/ecjtu/ecjtu_api/middle.py b/ecjtu/ecjtu_api/middle.py new file mode 100644 index 0000000..a178ce8 --- /dev/null +++ b/ecjtu/ecjtu_api/middle.py @@ -0,0 +1,32 @@ +from starlette.middleware.base import BaseHTTPMiddleware +from respose_result import ResponseResult +from auth import decode + +from fastapi import HTTPException +from fastapi.security import OAuth2PasswordBearer +from starlette.status import HTTP_401_UNAUTHORIZED + +class MyMiddleware(BaseHTTPMiddleware): + async def dispatch(self, request, call_next): + # 不被拦截的路由 + secure_routes = ["/token","/login","/","/docs","/openapi.json","/favicon.ico"] + + response = ResponseResult.auth_error() + path = request.url.path + + if path not in secure_routes: + header = request.headers.get("token") + # 是否存在token + if not header: + return response + token = header + stud_id, enc_pwd = decode(token) + if not stud_id or not enc_pwd: + return response + response = await call_next(request) + + return response + + + + \ No newline at end of file diff --git a/ecjtu/ecjtu_api/respose_result.py b/ecjtu/ecjtu_api/respose_result.py new file mode 100644 index 0000000..43cbe47 --- /dev/null +++ b/ecjtu/ecjtu_api/respose_result.py @@ -0,0 +1,66 @@ +""" +构建response返回结果 +{ + "code": 200, + "msg": "success", + "data": { + "name": "张三", + "age": 18 + } +} +""" +from typing import Any, Dict, Union +from fastapi.responses import JSONResponse + +class ResponseResult: + @staticmethod + def success(data: Union[Dict[str, Any], Any] = None, msg: str = "success") -> JSONResponse: + return JSONResponse( + content={ + "code": 200, + "msg": msg, + "data": data + } + ) + @staticmethod + def success_no_data(msg: str = "success") -> JSONResponse: + return JSONResponse( + content={ + "code": 200, + "msg": msg, + "data": None + } + ) + + @staticmethod + def not_found(data: Union[Dict[str, Any], Any] = None, msg: str = "not found") -> JSONResponse: + return JSONResponse( + status_code=404, + content={ + "code": 404, + "msg": msg, + "data": data + } + ) + + @staticmethod + def auth_error(data: Union[Dict[str, Any], Any] = None, msg: str = "auth error") -> JSONResponse: + return JSONResponse( + status_code=401, + content={ + "code": 401, + "msg": msg, + "data": data + } + ) + + @staticmethod + def error(data: Union[Dict[str, Any], Any] = None, msg: str = "error") -> JSONResponse: + return JSONResponse( + status_code=500, + content={ + "code": 500, + "msg": msg, + "data": data + } + ) \ No newline at end of file diff --git a/ecjtu/ecjtu_api/server.py b/ecjtu/ecjtu_api/server.py new file mode 100644 index 0000000..8cf4b0b --- /dev/null +++ b/ecjtu/ecjtu_api/server.py @@ -0,0 +1,18 @@ +#TODO + +import argparse +from ecjtu.client import ECJTU + +def main(): + parser = argparse.ArgumentParser(description="ECJTU Command Line Interface") + parser.add_argument('--stud_id', required=True, help='Student ID') + parser.add_argument('--pwd', required=True, help='Password') + parser.add_argument('--port', type=int, default=8000, help='Port to run the server on') + + args = parser.parse_args() + + client = ECJTU(stud_id=args.stud_id, password=args.pwd) + client.start_api_server(port=args.port) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/ecjtu/server.py b/ecjtu/server.py deleted file mode 100644 index e69de29..0000000 From 8a2ccca136c7573670bf72ca85563fd9ad69152a Mon Sep 17 00:00:00 2001 From: longsihua2003 <1161443436@qq.com> Date: Mon, 6 May 2024 22:34:22 +0800 Subject: [PATCH 2/6] add server end --- ecjtu/ecjtu_api/api.py | 139 ++++++++++++-- ecjtu/ecjtu_api/auth.py | 1 - ecjtu/ecjtu_api/middle.py | 12 +- ecjtu/ecjtu_api/respose_result.py | 11 -- ecjtu/ecjtu_api/server.py | 18 -- ecjtu/server.py | 13 ++ examples/ecjtu-api.md | 309 ++++++++++++++++++++++++++++++ examples/ecjtu.api.ipynb | 114 +++++++++++ 8 files changed, 559 insertions(+), 58 deletions(-) delete mode 100644 ecjtu/ecjtu_api/server.py create mode 100644 ecjtu/server.py create mode 100644 examples/ecjtu-api.md create mode 100644 examples/ecjtu.api.ipynb diff --git a/ecjtu/ecjtu_api/api.py b/ecjtu/ecjtu_api/api.py index 30b7113..399c908 100644 --- a/ecjtu/ecjtu_api/api.py +++ b/ecjtu/ecjtu_api/api.py @@ -1,10 +1,7 @@ -from fastapi import FastAPI, Depends,Header -from fastapi.security import OAuth2PasswordRequestForm +from fastapi import FastAPI,Header +from fastapi.responses import RedirectResponse -import middle -from ecjtu_schema import UserSchema, UserLoginSchema -from respose_result import ResponseResult -from auth import encode,decode +from . import middle,ecjtu_schema,respose_result,auth from ecjtu.client import ECJTU @@ -12,31 +9,133 @@ app.add_middleware(middle.MyMiddleware) -@app.post("/login") -def login(user: UserLoginSchema): +@app.get("/",include_in_schema=False) +def push_docs(): + respose = RedirectResponse(url="/docs") + return respose + +@app.post("/login",tags=["登录"],summary="登录",description="登录并获取token,以下所有接口都需要token才可以使用") +def login(user: ecjtu_schema.UserLoginSchema): client = ECJTU(user.stud_id, user.password) try: client.login() except Exception as e: - return ResponseResult.error(str(e)) - token = encode(user.stud_id, user.password) - return ResponseResult.success({"token": token}) + return respose_result.ResponseResult.error(str(e)) + token = auth.encode(user.stud_id, user.password) + return respose_result.ResponseResult.success({"token": token}) # gpa接口 -@app.get("/gpa") +@app.get("/gpa",tags=["GPA"],summary="获取GPA",description="获取当学期GPA") def gpa(token: str = Header(None)): - stud_id, password = decode(token) + stud_id, password = auth.decode(token) client = ECJTU(stud_id, password) try: gpa = client.gpa.today() except Exception as e: - return ResponseResult.error(str(e)) - return ResponseResult.success(dict(gpa)) + return respose_result.ResponseResult.error(str(e)) + return respose_result.ResponseResult.success(dict(gpa)) + +# 课表接口 +@app.get("/schedule",tags=["课表"],summary="获取当天课表",description="获取当天课表") +def schedule(token: str = Header(None)): + stud_id, password = auth.decode(token) + client = ECJTU(stud_id, password) + try: + schedule = client.scheduled_courses.today() + except Exception as e: + return respose_result.ResponseResult.error(str(e)) + schedule_list = [] + for sublist in schedule: + schedule_list.append(dict(sublist)) + return respose_result.ResponseResult.success(schedule_list) + +@app.get("/schedule/{date}",tags=["课表"],summary="获取指定日期课表",description="获取指定日期课表,指定日期格式为yyyy-mm-dd") +def schedule_date(token: str = Header(None),date:str = None): + # date(str): The date to filter, eg: 2023-01-01 + stud_id, password = auth.decode(token) + client = ECJTU(stud_id, password) + try: + scheduled_courses = client.scheduled_courses.filter(date=date) + except Exception as e: + return respose_result.ResponseResult.error(str(e)) + schedule_list = [] + for sublist in scheduled_courses: + schedule_list.append(dict(sublist)) + return respose_result.ResponseResult.success(schedule_list) + +@app.get("/schedule/week",tags=["课表"],summary="获取本周课表",description="获取本周课表") +def schedule_week(token: str = Header(None)): + stud_id, password = auth.decode(token) + client = ECJTU(stud_id, password) + try: + schedule = client.scheduled_courses.this_week() + except Exception as e: + return respose_result.ResponseResult.error(str(e)) + dict_list = [] + for sublist in schedule: + dict_sublist = [] + for item in sublist: + dict_sublist.append(dict(item)) + dict_list.append(dict_sublist) + return respose_result.ResponseResult.success(dict_list) + +# 成绩接口 +@app.get("/score",tags=["成绩"],summary="获取当前成绩",description="获取当学期成绩") +def score(token: str = Header(None)): + stud_id, password = auth.decode(token) + client = ECJTU(stud_id, password) + try: + score = client.scores.today() + except Exception as e: + return respose_result.ResponseResult.error(str(e)) + score_list = [] + for sublist in score: + score_list.append(dict(sublist)) + return respose_result.ResponseResult.success(score_list) + +@app.get("/score/{semester}",tags=["成绩"],summary="获取指定学期成绩",description="获取指定学期成绩,semester格式为yyyy.1或yyyy.2") +def score_semester(token: str = Header(None),semester:str = None): + # semester(Optional[str]): The semester to filter, eg: 2023.1, 2023.2 + stud_id, password = auth.decode(token) + client = ECJTU(stud_id, password) + try: + scores = client.scores.filter(semester=semester) + except Exception as e: + return respose_result.ResponseResult.error(str(e)) + score_list = [] + for sublist in scores: + score_list.append(dict(sublist)) + return respose_result.ResponseResult.success(score_list) + +# 选课情况接口 +@app.get("/elective_courses",tags=["选课情况"],summary="获取当前选课情况",description="获取当前学期选课情况") +def elective_courses(token: str = Header(None)): + stud_id, password = auth.decode(token) + client = ECJTU(stud_id, password) + try: + elective_courses = client.elective_courses.today() + except Exception as e: + return respose_result.ResponseResult.error(str(e)) + elective_courses_list = [] + for sublist in elective_courses: + elective_courses_list.append(dict(sublist)) + return respose_result.ResponseResult.success(elective_courses_list) + +@app.get("/elective_courses/{semester}",tags=["选课情况"],summary="获取指定学期选课情况",description="获取指定学期选课情况,semester格式为yyyy.1或yyyy.2") +def elective_courses_semester(token: str = Header(None),semester:str = None): + # semester(Optional[str]): The semester to filter, eg: 2023.1, 2023.2 + stud_id, password = auth.decode(token) + client = ECJTU(stud_id, password) + try: + elective_courses = client.elective_courses.filter(semester=semester) + except Exception as e: + return respose_result.ResponseResult.error(str(e)) + elective_courses_list = [] + for sublist in elective_courses: + elective_courses_list.append(dict(sublist)) + return respose_result.ResponseResult.success(elective_courses_list) # 启动api服务 -def start_api_server(): +def start_api_server(port=8080): import uvicorn - uvicorn.run(app, host="127.0.0.1", port=8080) - -if __name__ == "__main__": - start_api_server() \ No newline at end of file + uvicorn.run(app, host="127.0.0.1", port=port) \ No newline at end of file diff --git a/ecjtu/ecjtu_api/auth.py b/ecjtu/ecjtu_api/auth.py index 2c065df..cf86bbf 100644 --- a/ecjtu/ecjtu_api/auth.py +++ b/ecjtu/ecjtu_api/auth.py @@ -1,5 +1,4 @@ import base64 -# from ecjtu.client import _get_enc_password KEY = "zxcvbnmasdgfhjklpoiuytrewq" def encode(stud_id, pwd): diff --git a/ecjtu/ecjtu_api/middle.py b/ecjtu/ecjtu_api/middle.py index a178ce8..e56a722 100644 --- a/ecjtu/ecjtu_api/middle.py +++ b/ecjtu/ecjtu_api/middle.py @@ -1,17 +1,13 @@ from starlette.middleware.base import BaseHTTPMiddleware -from respose_result import ResponseResult -from auth import decode +from . import respose_result,auth -from fastapi import HTTPException -from fastapi.security import OAuth2PasswordBearer -from starlette.status import HTTP_401_UNAUTHORIZED class MyMiddleware(BaseHTTPMiddleware): async def dispatch(self, request, call_next): # 不被拦截的路由 - secure_routes = ["/token","/login","/","/docs","/openapi.json","/favicon.ico"] + secure_routes = ["/token","/login","/","/docs","/docs/","/openapi.json","/favicon.ico"] - response = ResponseResult.auth_error() + response =respose_result.ResponseResult.auth_error() path = request.url.path if path not in secure_routes: @@ -20,7 +16,7 @@ async def dispatch(self, request, call_next): if not header: return response token = header - stud_id, enc_pwd = decode(token) + stud_id, enc_pwd = auth.decode(token) if not stud_id or not enc_pwd: return response response = await call_next(request) diff --git a/ecjtu/ecjtu_api/respose_result.py b/ecjtu/ecjtu_api/respose_result.py index 43cbe47..3f9ccc7 100644 --- a/ecjtu/ecjtu_api/respose_result.py +++ b/ecjtu/ecjtu_api/respose_result.py @@ -1,14 +1,3 @@ -""" -构建response返回结果 -{ - "code": 200, - "msg": "success", - "data": { - "name": "张三", - "age": 18 - } -} -""" from typing import Any, Dict, Union from fastapi.responses import JSONResponse diff --git a/ecjtu/ecjtu_api/server.py b/ecjtu/ecjtu_api/server.py deleted file mode 100644 index 8cf4b0b..0000000 --- a/ecjtu/ecjtu_api/server.py +++ /dev/null @@ -1,18 +0,0 @@ -#TODO - -import argparse -from ecjtu.client import ECJTU - -def main(): - parser = argparse.ArgumentParser(description="ECJTU Command Line Interface") - parser.add_argument('--stud_id', required=True, help='Student ID') - parser.add_argument('--pwd', required=True, help='Password') - parser.add_argument('--port', type=int, default=8000, help='Port to run the server on') - - args = parser.parse_args() - - client = ECJTU(stud_id=args.stud_id, password=args.pwd) - client.start_api_server(port=args.port) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/ecjtu/server.py b/ecjtu/server.py new file mode 100644 index 0000000..3f4c2a9 --- /dev/null +++ b/ecjtu/server.py @@ -0,0 +1,13 @@ +import argparse +from ecjtu_api.api import start_api_server + +def main(): + parser = argparse.ArgumentParser(description="ECJTU Command Line Interface") + parser.add_argument('--port', type=int, default=8000, help='Port to run the server on') + + args = parser.parse_args() + + start_api_server(args.port) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/examples/ecjtu-api.md b/examples/ecjtu-api.md new file mode 100644 index 0000000..046703c --- /dev/null +++ b/examples/ecjtu-api.md @@ -0,0 +1,309 @@ +--- +title: ecjtu-api +language_tabs: + - shell: Shell + - http: HTTP + - javascript: JavaScript + - ruby: Ruby + - python: Python + - php: PHP + - java: Java + - go: Go +toc_footers: [] +includes: [] +search: true +code_clipboard: true +highlight_theme: darkula +headingLevel: 2 +generator: "@tarslib/widdershins v4.0.23" + +--- + +# ecjtu-api + +Base URLs: + +# Authentication + +# 登录 + + + +## POST 登录 + +POST /login + +登录并获取token,以下所有接口都需要token才可以使用 + +> Body 请求参数 + +```json +{ + "stud_id": "string", + "password": "string" +} +``` + +### 请求参数 + +|名称|位置|类型|必选|中文名|说明| +|---|---|---|---|---|---| +|body|body|[UserLoginSchema](#schemauserloginschema)| 否 | UserLoginSchema|none| + +> 返回示例 + +> 200 Response + +```json +"string" +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| + +# GPA + + + +## GET 获取GPA + +GET /gpa + +获取当学期GPA + +### 请求参数 + +|名称|位置|类型|必选|中文名|说明| +|---|---|---|---|---|---| +|token|header|string| 否 | Token|none| + +> 返回示例 + +> 200 Response + +```json +"string" +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| + +# 课表 + + + +## GET 获取当天课表 + +GET /schedule + +获取当天课表 + +### 请求参数 + +|名称|位置|类型|必选|中文名|说明| +|---|---|---|---|---|---| +|token|header|string| 否 | Token|none| + +> 返回示例 + +> 200 Response + +```json +"string" +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| + + + +## GET 获取指定日期课表 + +GET /schedule/{date} + +获取指定日期课表,指定日期格式为yyyy-mm-dd + +### 请求参数 + +|名称|位置|类型|必选|中文名|说明| +|---|---|---|---|---|---| +|date|path|string| 是 | Date|none| +|token|header|string| 否 | Token|none| + +> 返回示例 + +> 200 Response + +```json +"string" +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| + + + +## GET 获取本周课表 + +GET /schedule/week + +获取本周课表 + +### 请求参数 + +|名称|位置|类型|必选|中文名|说明| +|---|---|---|---|---|---| +|token|header|string| 否 | Token|none| + +> 返回示例 + +> 200 Response + +```json +"string" +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| + +# 成绩 + + + +## GET 获取当前成绩 + +GET /score + +获取当学期成绩 + +### 请求参数 + +|名称|位置|类型|必选|中文名|说明| +|---|---|---|---|---|---| +|token|header|string| 否 | Token|none| + +> 返回示例 + +> 200 Response + +```json +"string" +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| + + + +## GET 获取指定学期成绩 + +GET /score/{semester} + +获取指定学期成绩,semester格式为yyyy.1或yyyy.2 + +### 请求参数 + +|名称|位置|类型|必选|中文名|说明| +|---|---|---|---|---|---| +|semester|path|string| 是 | Semester|none| +|token|header|string| 否 | Token|none| + +> 返回示例 + +> 200 Response + +```json +"string" +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| + +# 选课情况 + + + +## GET 获取当前选课情况 + +GET /elective_courses + +获取当前学期选课情况 + +### 请求参数 + +|名称|位置|类型|必选|中文名|说明| +|---|---|---|---|---|---| +|token|header|string| 否 | Token|none| + +> 返回示例 + +> 200 Response + +```json +"string" +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| + + + +## GET 获取指定学期选课情况 + +GET /elective_courses/{semester} + +获取指定学期选课情况,semester格式为yyyy.1或yyyy.2 + +### 请求参数 + +|名称|位置|类型|必选|中文名|说明| +|---|---|---|---|---|---| +|semester|path|string| 是 | Semester|none| +|token|header|string| 否 | Token|none| + +> 返回示例 + +> 200 Response + +```json +"string" +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| \ No newline at end of file diff --git a/examples/ecjtu.api.ipynb b/examples/ecjtu.api.ipynb new file mode 100644 index 0000000..ffd520c --- /dev/null +++ b/examples/ecjtu.api.ipynb @@ -0,0 +1,114 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# ecjtu-api\n", + "我们提供了ecjtu的一套服务。\n", + "通过fastapi的形式实现" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 相关接口描述\n", + "\n", + "详细信息可以参考ecjtu-api.md。\n", + "\n", + "1. 登录\n", + " \n", + " post /login \n", + " 通过学号和密码进行登录,获取登录token,其他接口的请求均要token认证\n", + "2. gpa\n", + " \n", + " get /gpa\n", + " 获取当前gpa情况\n", + "3. 课表\n", + " * get /schedule\n", + " 获取今日课表\n", + " * get /schedule/{date}\n", + " 获取指定日期课表 date格式为2024-05-01\n", + " * get /schedule/week\n", + " 获取本周课表\n", + "4. 成绩\n", + " * get /score\n", + " 获取目前成绩\n", + " * /score/{semester}\n", + " 获取指定学期成绩 semester格式为2023.1\n", + "5. 选课情况\n", + " * get /elective_courses\n", + " 获取当前选课信息\n", + " * get /elective_courses/{semester}\n", + " 获取指定学期选课信息 semester格式为2023.1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 服务的启动\n", + "我们提供了两种服务的启动方式\n", + "\n", + "### 通过python文件启动" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from ecjtu.ecjtu_api.api import start_api_server\n", + "\n", + "def main():\n", + " start_api_server(port=8080)\n", + "\n", + "if __name__ == '__main__':\n", + " main()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 通过命令行启动" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "shellscript" + } + }, + "outputs": [], + "source": [ + "python ecjtu/server.py --port 8080 " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "ecjtut", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 4016736b34604c57f12e645ff35f4050a9d4dea0 Mon Sep 17 00:00:00 2001 From: longsihua2003 <1161443436@qq.com> Date: Mon, 6 May 2024 22:39:00 +0800 Subject: [PATCH 3/6] lint and formatting --- ecjtu/ecjtu_api/api.py | 75 ++- ecjtu/ecjtu_api/auth.py | 5 +- ecjtu/ecjtu_api/ecjtu_schema.py | 3 +- ecjtu/ecjtu_api/middle.py | 21 +- ecjtu/ecjtu_api/respose_result.py | 60 +-- ecjtu/server.py | 9 +- poetry.lock | 851 +++++++++++++++++++++++++++++- pyproject.toml | 2 + 8 files changed, 956 insertions(+), 70 deletions(-) diff --git a/ecjtu/ecjtu_api/api.py b/ecjtu/ecjtu_api/api.py index 399c908..2db1afb 100644 --- a/ecjtu/ecjtu_api/api.py +++ b/ecjtu/ecjtu_api/api.py @@ -1,20 +1,27 @@ -from fastapi import FastAPI,Header +from fastapi import FastAPI, Header from fastapi.responses import RedirectResponse -from . import middle,ecjtu_schema,respose_result,auth - from ecjtu.client import ECJTU +from . import auth, ecjtu_schema, middle, respose_result + app = FastAPI(title="ECJTU API", description="API for ECJTU") app.add_middleware(middle.MyMiddleware) -@app.get("/",include_in_schema=False) + +@app.get("/", include_in_schema=False) def push_docs(): respose = RedirectResponse(url="/docs") return respose -@app.post("/login",tags=["登录"],summary="登录",description="登录并获取token,以下所有接口都需要token才可以使用") + +@app.post( + "/login", + tags=["登录"], + summary="登录", + description="登录并获取token,以下所有接口都需要token才可以使用", +) def login(user: ecjtu_schema.UserLoginSchema): client = ECJTU(user.stud_id, user.password) try: @@ -24,8 +31,9 @@ def login(user: ecjtu_schema.UserLoginSchema): token = auth.encode(user.stud_id, user.password) return respose_result.ResponseResult.success({"token": token}) + # gpa接口 -@app.get("/gpa",tags=["GPA"],summary="获取GPA",description="获取当学期GPA") +@app.get("/gpa", tags=["GPA"], summary="获取GPA", description="获取当学期GPA") def gpa(token: str = Header(None)): stud_id, password = auth.decode(token) client = ECJTU(stud_id, password) @@ -35,8 +43,9 @@ def gpa(token: str = Header(None)): return respose_result.ResponseResult.error(str(e)) return respose_result.ResponseResult.success(dict(gpa)) + # 课表接口 -@app.get("/schedule",tags=["课表"],summary="获取当天课表",description="获取当天课表") +@app.get("/schedule", tags=["课表"], summary="获取当天课表", description="获取当天课表") def schedule(token: str = Header(None)): stud_id, password = auth.decode(token) client = ECJTU(stud_id, password) @@ -49,8 +58,14 @@ def schedule(token: str = Header(None)): schedule_list.append(dict(sublist)) return respose_result.ResponseResult.success(schedule_list) -@app.get("/schedule/{date}",tags=["课表"],summary="获取指定日期课表",description="获取指定日期课表,指定日期格式为yyyy-mm-dd") -def schedule_date(token: str = Header(None),date:str = None): + +@app.get( + "/schedule/{date}", + tags=["课表"], + summary="获取指定日期课表", + description="获取指定日期课表,指定日期格式为yyyy-mm-dd", +) +def schedule_date(token: str = Header(None), date: str = None): # date(str): The date to filter, eg: 2023-01-01 stud_id, password = auth.decode(token) client = ECJTU(stud_id, password) @@ -63,14 +78,17 @@ def schedule_date(token: str = Header(None),date:str = None): schedule_list.append(dict(sublist)) return respose_result.ResponseResult.success(schedule_list) -@app.get("/schedule/week",tags=["课表"],summary="获取本周课表",description="获取本周课表") + +@app.get( + "/schedule/week", tags=["课表"], summary="获取本周课表", description="获取本周课表" +) def schedule_week(token: str = Header(None)): stud_id, password = auth.decode(token) client = ECJTU(stud_id, password) try: schedule = client.scheduled_courses.this_week() except Exception as e: - return respose_result.ResponseResult.error(str(e)) + return respose_result.ResponseResult.error(str(e)) dict_list = [] for sublist in schedule: dict_sublist = [] @@ -79,8 +97,9 @@ def schedule_week(token: str = Header(None)): dict_list.append(dict_sublist) return respose_result.ResponseResult.success(dict_list) + # 成绩接口 -@app.get("/score",tags=["成绩"],summary="获取当前成绩",description="获取当学期成绩") +@app.get("/score", tags=["成绩"], summary="获取当前成绩", description="获取当学期成绩") def score(token: str = Header(None)): stud_id, password = auth.decode(token) client = ECJTU(stud_id, password) @@ -93,8 +112,14 @@ def score(token: str = Header(None)): score_list.append(dict(sublist)) return respose_result.ResponseResult.success(score_list) -@app.get("/score/{semester}",tags=["成绩"],summary="获取指定学期成绩",description="获取指定学期成绩,semester格式为yyyy.1或yyyy.2") -def score_semester(token: str = Header(None),semester:str = None): + +@app.get( + "/score/{semester}", + tags=["成绩"], + summary="获取指定学期成绩", + description="获取指定学期成绩,semester格式为yyyy.1或yyyy.2", +) +def score_semester(token: str = Header(None), semester: str = None): # semester(Optional[str]): The semester to filter, eg: 2023.1, 2023.2 stud_id, password = auth.decode(token) client = ECJTU(stud_id, password) @@ -107,8 +132,14 @@ def score_semester(token: str = Header(None),semester:str = None): score_list.append(dict(sublist)) return respose_result.ResponseResult.success(score_list) + # 选课情况接口 -@app.get("/elective_courses",tags=["选课情况"],summary="获取当前选课情况",description="获取当前学期选课情况") +@app.get( + "/elective_courses", + tags=["选课情况"], + summary="获取当前选课情况", + description="获取当前学期选课情况", +) def elective_courses(token: str = Header(None)): stud_id, password = auth.decode(token) client = ECJTU(stud_id, password) @@ -121,8 +152,14 @@ def elective_courses(token: str = Header(None)): elective_courses_list.append(dict(sublist)) return respose_result.ResponseResult.success(elective_courses_list) -@app.get("/elective_courses/{semester}",tags=["选课情况"],summary="获取指定学期选课情况",description="获取指定学期选课情况,semester格式为yyyy.1或yyyy.2") -def elective_courses_semester(token: str = Header(None),semester:str = None): + +@app.get( + "/elective_courses/{semester}", + tags=["选课情况"], + summary="获取指定学期选课情况", + description="获取指定学期选课情况,semester格式为yyyy.1或yyyy.2", +) +def elective_courses_semester(token: str = Header(None), semester: str = None): # semester(Optional[str]): The semester to filter, eg: 2023.1, 2023.2 stud_id, password = auth.decode(token) client = ECJTU(stud_id, password) @@ -135,7 +172,9 @@ def elective_courses_semester(token: str = Header(None),semester:str = None): elective_courses_list.append(dict(sublist)) return respose_result.ResponseResult.success(elective_courses_list) + # 启动api服务 def start_api_server(port=8080): import uvicorn - uvicorn.run(app, host="127.0.0.1", port=port) \ No newline at end of file + + uvicorn.run(app, host="127.0.0.1", port=port) diff --git a/ecjtu/ecjtu_api/auth.py b/ecjtu/ecjtu_api/auth.py index cf86bbf..8ba0050 100644 --- a/ecjtu/ecjtu_api/auth.py +++ b/ecjtu/ecjtu_api/auth.py @@ -1,11 +1,14 @@ import base64 + KEY = "zxcvbnmasdgfhjklpoiuytrewq" + def encode(stud_id, pwd): # enc_pwd = _get_enc_password(pwd); token = base64.b64encode(f"{stud_id}:{pwd}".encode()).decode() return token + def decode(token): stud_id, enc_pwd = base64.b64decode(token.encode()).decode().split(":") - return stud_id, enc_pwd \ No newline at end of file + return stud_id, enc_pwd diff --git a/ecjtu/ecjtu_api/ecjtu_schema.py b/ecjtu/ecjtu_api/ecjtu_schema.py index 1fb7bd9..8a4406d 100644 --- a/ecjtu/ecjtu_api/ecjtu_schema.py +++ b/ecjtu/ecjtu_api/ecjtu_schema.py @@ -1,5 +1,6 @@ from pydantic import BaseModel, Field + class UserLoginSchema(BaseModel): stud_id: str = Field(..., min_length=1) - password: str = Field(..., min_length=1) \ No newline at end of file + password: str = Field(..., min_length=1) diff --git a/ecjtu/ecjtu_api/middle.py b/ecjtu/ecjtu_api/middle.py index e56a722..e69d75a 100644 --- a/ecjtu/ecjtu_api/middle.py +++ b/ecjtu/ecjtu_api/middle.py @@ -1,13 +1,22 @@ from starlette.middleware.base import BaseHTTPMiddleware -from . import respose_result,auth + +from . import auth, respose_result class MyMiddleware(BaseHTTPMiddleware): async def dispatch(self, request, call_next): # 不被拦截的路由 - secure_routes = ["/token","/login","/","/docs","/docs/","/openapi.json","/favicon.ico"] - - response =respose_result.ResponseResult.auth_error() + secure_routes = [ + "/token", + "/login", + "/", + "/docs", + "/docs/", + "/openapi.json", + "/favicon.ico", + ] + + response = respose_result.ResponseResult.auth_error() path = request.url.path if path not in secure_routes: @@ -22,7 +31,3 @@ async def dispatch(self, request, call_next): response = await call_next(request) return response - - - - \ No newline at end of file diff --git a/ecjtu/ecjtu_api/respose_result.py b/ecjtu/ecjtu_api/respose_result.py index 3f9ccc7..a3a05c4 100644 --- a/ecjtu/ecjtu_api/respose_result.py +++ b/ecjtu/ecjtu_api/respose_result.py @@ -1,55 +1,39 @@ from typing import Any, Dict, Union + from fastapi.responses import JSONResponse + class ResponseResult: @staticmethod - def success(data: Union[Dict[str, Any], Any] = None, msg: str = "success") -> JSONResponse: - return JSONResponse( - content={ - "code": 200, - "msg": msg, - "data": data - } - ) + def success( + data: Union[Dict[str, Any], Any] = None, msg: str = "success" + ) -> JSONResponse: + return JSONResponse(content={"code": 200, "msg": msg, "data": data}) + @staticmethod def success_no_data(msg: str = "success") -> JSONResponse: - return JSONResponse( - content={ - "code": 200, - "msg": msg, - "data": None - } - ) + return JSONResponse(content={"code": 200, "msg": msg, "data": None}) @staticmethod - def not_found(data: Union[Dict[str, Any], Any] = None, msg: str = "not found") -> JSONResponse: + def not_found( + data: Union[Dict[str, Any], Any] = None, msg: str = "not found" + ) -> JSONResponse: return JSONResponse( - status_code=404, - content={ - "code": 404, - "msg": msg, - "data": data - } + status_code=404, content={"code": 404, "msg": msg, "data": data} ) - + @staticmethod - def auth_error(data: Union[Dict[str, Any], Any] = None, msg: str = "auth error") -> JSONResponse: + def auth_error( + data: Union[Dict[str, Any], Any] = None, msg: str = "auth error" + ) -> JSONResponse: return JSONResponse( - status_code=401, - content={ - "code": 401, - "msg": msg, - "data": data - } + status_code=401, content={"code": 401, "msg": msg, "data": data} ) @staticmethod - def error(data: Union[Dict[str, Any], Any] = None, msg: str = "error") -> JSONResponse: + def error( + data: Union[Dict[str, Any], Any] = None, msg: str = "error" + ) -> JSONResponse: return JSONResponse( - status_code=500, - content={ - "code": 500, - "msg": msg, - "data": data - } - ) \ No newline at end of file + status_code=500, content={"code": 500, "msg": msg, "data": data} + ) diff --git a/ecjtu/server.py b/ecjtu/server.py index 3f4c2a9..8317631 100644 --- a/ecjtu/server.py +++ b/ecjtu/server.py @@ -1,13 +1,18 @@ import argparse + from ecjtu_api.api import start_api_server + def main(): parser = argparse.ArgumentParser(description="ECJTU Command Line Interface") - parser.add_argument('--port', type=int, default=8000, help='Port to run the server on') + parser.add_argument( + "--port", type=int, default=8000, help="Port to run the server on" + ) args = parser.parse_args() start_api_server(args.port) + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/poetry.lock b/poetry.lock index 1be763a..4528467 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "annotated-types" @@ -14,6 +14,28 @@ files = [ [package.dependencies] typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} +[[package]] +name = "anyio" +version = "4.3.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, + {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} + +[package.extras] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] + [[package]] name = "beautifulsoup4" version = "4.12.3" @@ -35,6 +57,17 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + [[package]] name = "cffi" version = "1.16.0" @@ -110,6 +143,20 @@ files = [ {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, ] +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -265,6 +312,41 @@ files = [ {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] +[[package]] +name = "dnspython" +version = "2.6.1" +description = "DNS toolkit" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, + {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, +] + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=0.9.25)"] +idna = ["idna (>=3.6)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "email-validator" +version = "2.1.1" +description = "A robust email address syntax and deliverability validation library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "email_validator-2.1.1-py3-none-any.whl", hash = "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05"}, + {file = "email_validator-2.1.1.tar.gz", hash = "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84"}, +] + +[package.dependencies] +dnspython = ">=2.0.0" +idna = ">=2.0.0" + [[package]] name = "exceptiongroup" version = "1.2.1" @@ -279,6 +361,49 @@ files = [ [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "fastapi" +version = "0.111.0" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.111.0-py3-none-any.whl", hash = "sha256:97ecbf994be0bcbdadedf88c3150252bed7b2087075ac99735403b1b76cc8fc0"}, + {file = "fastapi-0.111.0.tar.gz", hash = "sha256:b9db9dd147c91cb8b769f7183535773d8741dd46f9dc6676cd82eab510228cd7"}, +] + +[package.dependencies] +email_validator = ">=2.0.0" +fastapi-cli = ">=0.0.2" +httpx = ">=0.23.0" +jinja2 = ">=2.11.2" +orjson = ">=3.2.1" +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +python-multipart = ">=0.0.7" +starlette = ">=0.37.2,<0.38.0" +typing-extensions = ">=4.8.0" +ujson = ">=4.0.1,<4.0.2 || >4.0.2,<4.1.0 || >4.1.0,<4.2.0 || >4.2.0,<4.3.0 || >4.3.0,<5.0.0 || >5.0.0,<5.1.0 || >5.1.0" +uvicorn = {version = ">=0.12.0", extras = ["standard"]} + +[package.extras] +all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "fastapi-cli" +version = "0.0.2" +description = "Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi_cli-0.0.2-py3-none-any.whl", hash = "sha256:d7a8ec89fd52ad16c52de9fe7299e4b22c7a32e1bf28aa886e5517e4927423dd"}, + {file = "fastapi_cli-0.0.2.tar.gz", hash = "sha256:589565ba758432632eadcf7b950e0ec76bb283b549784d9df17f261a8a9de476"}, +] + +[package.dependencies] +fastapi = "*" +typer = ">=0.12.3" +uvicorn = {version = ">=0.29.0", extras = ["standard"]} + [[package]] name = "filelock" version = "3.13.4" @@ -295,6 +420,110 @@ docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1 testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] typing = ["typing-extensions (>=4.8)"] +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "1.0.5" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, + {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<0.26.0)"] + +[[package]] +name = "httptools" +version = "0.6.1" +description = "A collection of framework independent HTTP protocol utils." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, + {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, + {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, + {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, + {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, + {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, + {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, +] + +[package.extras] +test = ["Cython (>=0.29.24,<0.30.0)"] + +[[package]] +name = "httpx" +version = "0.27.0" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + [[package]] name = "identify" version = "2.5.35" @@ -309,6 +538,17 @@ files = [ [package.extras] license = ["ukkonen"] +[[package]] +name = "idna" +version = "3.7" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, +] + [[package]] name = "iniconfig" version = "2.0.0" @@ -320,6 +560,127 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + [[package]] name = "nodeenv" version = "1.8.0" @@ -334,6 +695,61 @@ files = [ [package.dependencies] setuptools = "*" +[[package]] +name = "orjson" +version = "3.10.3" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = false +python-versions = ">=3.8" +files = [ + {file = "orjson-3.10.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9fb6c3f9f5490a3eb4ddd46fc1b6eadb0d6fc16fb3f07320149c3286a1409dd8"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:252124b198662eee80428f1af8c63f7ff077c88723fe206a25df8dc57a57b1fa"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f3e87733823089a338ef9bbf363ef4de45e5c599a9bf50a7a9b82e86d0228da"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8334c0d87103bb9fbbe59b78129f1f40d1d1e8355bbed2ca71853af15fa4ed3"}, + {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1952c03439e4dce23482ac846e7961f9d4ec62086eb98ae76d97bd41d72644d7"}, + {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c0403ed9c706dcd2809f1600ed18f4aae50be263bd7112e54b50e2c2bc3ebd6d"}, + {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:382e52aa4270a037d41f325e7d1dfa395b7de0c367800b6f337d8157367bf3a7"}, + {file = "orjson-3.10.3-cp310-none-win32.whl", hash = "sha256:be2aab54313752c04f2cbaab4515291ef5af8c2256ce22abc007f89f42f49109"}, + {file = "orjson-3.10.3-cp310-none-win_amd64.whl", hash = "sha256:416b195f78ae461601893f482287cee1e3059ec49b4f99479aedf22a20b1098b"}, + {file = "orjson-3.10.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:73100d9abbbe730331f2242c1fc0bcb46a3ea3b4ae3348847e5a141265479700"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a12eee96e3ab828dbfcb4d5a0023aa971b27143a1d35dc214c176fdfb29b3"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520de5e2ef0b4ae546bea25129d6c7c74edb43fc6cf5213f511a927f2b28148b"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccaa0a401fc02e8828a5bedfd80f8cd389d24f65e5ca3954d72c6582495b4bcf"}, + {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7bc9e8bc11bac40f905640acd41cbeaa87209e7e1f57ade386da658092dc16"}, + {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3582b34b70543a1ed6944aca75e219e1192661a63da4d039d088a09c67543b08"}, + {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c23dfa91481de880890d17aa7b91d586a4746a4c2aa9a145bebdbaf233768d5"}, + {file = "orjson-3.10.3-cp311-none-win32.whl", hash = "sha256:1770e2a0eae728b050705206d84eda8b074b65ee835e7f85c919f5705b006c9b"}, + {file = "orjson-3.10.3-cp311-none-win_amd64.whl", hash = "sha256:93433b3c1f852660eb5abdc1f4dd0ced2be031ba30900433223b28ee0140cde5"}, + {file = "orjson-3.10.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a39aa73e53bec8d410875683bfa3a8edf61e5a1c7bb4014f65f81d36467ea098"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0943a96b3fa09bee1afdfccc2cb236c9c64715afa375b2af296c73d91c23eab2"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e852baafceff8da3c9defae29414cc8513a1586ad93e45f27b89a639c68e8176"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18566beb5acd76f3769c1d1a7ec06cdb81edc4d55d2765fb677e3eaa10fa99e0"}, + {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bd2218d5a3aa43060efe649ec564ebedec8ce6ae0a43654b81376216d5ebd42"}, + {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf20465e74c6e17a104ecf01bf8cd3b7b252565b4ccee4548f18b012ff2f8069"}, + {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba7f67aa7f983c4345eeda16054a4677289011a478ca947cd69c0a86ea45e534"}, + {file = "orjson-3.10.3-cp312-none-win32.whl", hash = "sha256:17e0713fc159abc261eea0f4feda611d32eabc35708b74bef6ad44f6c78d5ea0"}, + {file = "orjson-3.10.3-cp312-none-win_amd64.whl", hash = "sha256:4c895383b1ec42b017dd2c75ae8a5b862fc489006afde06f14afbdd0309b2af0"}, + {file = "orjson-3.10.3-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:be2719e5041e9fb76c8c2c06b9600fe8e8584e6980061ff88dcbc2691a16d20d"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0175a5798bdc878956099f5c54b9837cb62cfbf5d0b86ba6d77e43861bcec2"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:978be58a68ade24f1af7758626806e13cff7748a677faf95fbb298359aa1e20d"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16bda83b5c61586f6f788333d3cf3ed19015e3b9019188c56983b5a299210eb5"}, + {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ad1f26bea425041e0a1adad34630c4825a9e3adec49079b1fb6ac8d36f8b754"}, + {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9e253498bee561fe85d6325ba55ff2ff08fb5e7184cd6a4d7754133bd19c9195"}, + {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a62f9968bab8a676a164263e485f30a0b748255ee2f4ae49a0224be95f4532b"}, + {file = "orjson-3.10.3-cp38-none-win32.whl", hash = "sha256:8d0b84403d287d4bfa9bf7d1dc298d5c1c5d9f444f3737929a66f2fe4fb8f134"}, + {file = "orjson-3.10.3-cp38-none-win_amd64.whl", hash = "sha256:8bc7a4df90da5d535e18157220d7915780d07198b54f4de0110eca6b6c11e290"}, + {file = "orjson-3.10.3-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9059d15c30e675a58fdcd6f95465c1522b8426e092de9fff20edebfdc15e1cb0"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d40c7f7938c9c2b934b297412c067936d0b54e4b8ab916fd1a9eb8f54c02294"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a654ec1de8fdaae1d80d55cee65893cb06494e124681ab335218be6a0691e7"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:831c6ef73f9aa53c5f40ae8f949ff7681b38eaddb6904aab89dca4d85099cb78"}, + {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99b880d7e34542db89f48d14ddecbd26f06838b12427d5a25d71baceb5ba119d"}, + {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2e5e176c994ce4bd434d7aafb9ecc893c15f347d3d2bbd8e7ce0b63071c52e25"}, + {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b69a58a37dab856491bf2d3bbf259775fdce262b727f96aafbda359cb1d114d8"}, + {file = "orjson-3.10.3-cp39-none-win32.whl", hash = "sha256:b8d4d1a6868cde356f1402c8faeb50d62cee765a1f7ffcfd6de732ab0581e063"}, + {file = "orjson-3.10.3-cp39-none-win_amd64.whl", hash = "sha256:5102f50c5fc46d94f2033fe00d392588564378260d64377aec702f21a7a22912"}, + {file = "orjson-3.10.3.tar.gz", hash = "sha256:2b166507acae7ba2f7c315dcf185a9111ad5e992ac81f2d507aac39193c2c818"}, +] + [[package]] name = "packaging" version = "24.0" @@ -525,6 +941,20 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + [[package]] name = "pyopenssl" version = "24.1.0" @@ -616,6 +1046,34 @@ pytest = ">=7.0.0" [package.extras] test = ["black (>=22.1.0)", "flake8 (>=4.0.1)", "pre-commit (>=2.17.0)", "tox (>=3.24.5)"] +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-multipart" +version = "0.0.9" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"}, + {file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"}, +] + +[package.extras] +dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"] + [[package]] name = "pyyaml" version = "6.0.1" @@ -641,6 +1099,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -675,6 +1134,25 @@ files = [ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] +[[package]] +name = "rich" +version = "13.7.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + [[package]] name = "ruff" version = "0.1.15" @@ -717,6 +1195,28 @@ docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + [[package]] name = "soupsieve" version = "2.5" @@ -728,6 +1228,24 @@ files = [ {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, ] +[[package]] +name = "starlette" +version = "0.37.2" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.8" +files = [ + {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, + {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] + [[package]] name = "tomli" version = "2.0.1" @@ -739,6 +1257,23 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] +[[package]] +name = "typer" +version = "0.12.3" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.7" +files = [ + {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, + {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, +] + +[package.dependencies] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" + [[package]] name = "typing-extensions" version = "4.11.0" @@ -750,6 +1285,80 @@ files = [ {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, ] +[[package]] +name = "ujson" +version = "5.9.0" +description = "Ultra fast JSON encoder and decoder for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ujson-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab71bf27b002eaf7d047c54a68e60230fbd5cd9da60de7ca0aa87d0bccead8fa"}, + {file = "ujson-5.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a365eac66f5aa7a7fdf57e5066ada6226700884fc7dce2ba5483538bc16c8c5"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e015122b337858dba5a3dc3533af2a8fc0410ee9e2374092f6a5b88b182e9fcc"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:779a2a88c53039bebfbccca934430dabb5c62cc179e09a9c27a322023f363e0d"}, + {file = "ujson-5.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10ca3c41e80509fd9805f7c149068fa8dbee18872bbdc03d7cca928926a358d5"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a566e465cb2fcfdf040c2447b7dd9718799d0d90134b37a20dff1e27c0e9096"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f833c529e922577226a05bc25b6a8b3eb6c4fb155b72dd88d33de99d53113124"}, + {file = "ujson-5.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b68a0caab33f359b4cbbc10065c88e3758c9f73a11a65a91f024b2e7a1257106"}, + {file = "ujson-5.9.0-cp310-cp310-win32.whl", hash = "sha256:7cc7e605d2aa6ae6b7321c3ae250d2e050f06082e71ab1a4200b4ae64d25863c"}, + {file = "ujson-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:a6d3f10eb8ccba4316a6b5465b705ed70a06011c6f82418b59278fbc919bef6f"}, + {file = "ujson-5.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b23bbb46334ce51ddb5dded60c662fbf7bb74a37b8f87221c5b0fec1ec6454b"}, + {file = "ujson-5.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6974b3a7c17bbf829e6c3bfdc5823c67922e44ff169851a755eab79a3dd31ec0"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5964ea916edfe24af1f4cc68488448fbb1ec27a3ddcddc2b236da575c12c8ae"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ba7cac47dd65ff88571eceeff48bf30ed5eb9c67b34b88cb22869b7aa19600d"}, + {file = "ujson-5.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bbd91a151a8f3358c29355a491e915eb203f607267a25e6ab10531b3b157c5e"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:829a69d451a49c0de14a9fecb2a2d544a9b2c884c2b542adb243b683a6f15908"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a807ae73c46ad5db161a7e883eec0fbe1bebc6a54890152ccc63072c4884823b"}, + {file = "ujson-5.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8fc2aa18b13d97b3c8ccecdf1a3c405f411a6e96adeee94233058c44ff92617d"}, + {file = "ujson-5.9.0-cp311-cp311-win32.whl", hash = "sha256:70e06849dfeb2548be48fdd3ceb53300640bc8100c379d6e19d78045e9c26120"}, + {file = "ujson-5.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:7309d063cd392811acc49b5016728a5e1b46ab9907d321ebbe1c2156bc3c0b99"}, + {file = "ujson-5.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:20509a8c9f775b3a511e308bbe0b72897ba6b800767a7c90c5cca59d20d7c42c"}, + {file = "ujson-5.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b28407cfe315bd1b34f1ebe65d3bd735d6b36d409b334100be8cdffae2177b2f"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d302bd17989b6bd90d49bade66943c78f9e3670407dbc53ebcf61271cadc399"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f21315f51e0db8ee245e33a649dd2d9dce0594522de6f278d62f15f998e050e"}, + {file = "ujson-5.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5635b78b636a54a86fdbf6f027e461aa6c6b948363bdf8d4fbb56a42b7388320"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82b5a56609f1235d72835ee109163c7041b30920d70fe7dac9176c64df87c164"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5ca35f484622fd208f55041b042d9d94f3b2c9c5add4e9af5ee9946d2d30db01"}, + {file = "ujson-5.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:829b824953ebad76d46e4ae709e940bb229e8999e40881338b3cc94c771b876c"}, + {file = "ujson-5.9.0-cp312-cp312-win32.whl", hash = "sha256:25fa46e4ff0a2deecbcf7100af3a5d70090b461906f2299506485ff31d9ec437"}, + {file = "ujson-5.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:60718f1720a61560618eff3b56fd517d107518d3c0160ca7a5a66ac949c6cf1c"}, + {file = "ujson-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d581db9db9e41d8ea0b2705c90518ba623cbdc74f8d644d7eb0d107be0d85d9c"}, + {file = "ujson-5.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ff741a5b4be2d08fceaab681c9d4bc89abf3c9db600ab435e20b9b6d4dfef12e"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdcb02cabcb1e44381221840a7af04433c1dc3297af76fde924a50c3054c708c"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e208d3bf02c6963e6ef7324dadf1d73239fb7008491fdf523208f60be6437402"}, + {file = "ujson-5.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4b3917296630a075e04d3d07601ce2a176479c23af838b6cf90a2d6b39b0d95"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0c4d6adb2c7bb9eb7c71ad6f6f612e13b264942e841f8cc3314a21a289a76c4e"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0b159efece9ab5c01f70b9d10bbb77241ce111a45bc8d21a44c219a2aec8ddfd"}, + {file = "ujson-5.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0cb4a7814940ddd6619bdce6be637a4b37a8c4760de9373bac54bb7b229698b"}, + {file = "ujson-5.9.0-cp38-cp38-win32.whl", hash = "sha256:dc80f0f5abf33bd7099f7ac94ab1206730a3c0a2d17549911ed2cb6b7aa36d2d"}, + {file = "ujson-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:506a45e5fcbb2d46f1a51fead991c39529fc3737c0f5d47c9b4a1d762578fc30"}, + {file = "ujson-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0fd2eba664a22447102062814bd13e63c6130540222c0aa620701dd01f4be81"}, + {file = "ujson-5.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bdf7fc21a03bafe4ba208dafa84ae38e04e5d36c0e1c746726edf5392e9f9f36"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2f909bc08ce01f122fd9c24bc6f9876aa087188dfaf3c4116fe6e4daf7e194f"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd4ea86c2afd41429751d22a3ccd03311c067bd6aeee2d054f83f97e41e11d8f"}, + {file = "ujson-5.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:63fb2e6599d96fdffdb553af0ed3f76b85fda63281063f1cb5b1141a6fcd0617"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:32bba5870c8fa2a97f4a68f6401038d3f1922e66c34280d710af00b14a3ca562"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:37ef92e42535a81bf72179d0e252c9af42a4ed966dc6be6967ebfb929a87bc60"}, + {file = "ujson-5.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f69f16b8f1c69da00e38dc5f2d08a86b0e781d0ad3e4cc6a13ea033a439c4844"}, + {file = "ujson-5.9.0-cp39-cp39-win32.whl", hash = "sha256:3382a3ce0ccc0558b1c1668950008cece9bf463ebb17463ebf6a8bfc060dae34"}, + {file = "ujson-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:6adef377ed583477cf005b58c3025051b5faa6b8cc25876e594afbb772578f21"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ffdfebd819f492e48e4f31c97cb593b9c1a8251933d8f8972e81697f00326ff1"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4eec2ddc046360d087cf35659c7ba0cbd101f32035e19047013162274e71fcf"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbb90aa5c23cb3d4b803c12aa220d26778c31b6e4b7a13a1f49971f6c7d088e"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba0823cb70866f0d6a4ad48d998dd338dce7314598721bc1b7986d054d782dfd"}, + {file = "ujson-5.9.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4e35d7885ed612feb6b3dd1b7de28e89baaba4011ecdf995e88be9ac614765e9"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b048aa93eace8571eedbd67b3766623e7f0acbf08ee291bef7d8106210432427"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:323279e68c195110ef85cbe5edce885219e3d4a48705448720ad925d88c9f851"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ac92d86ff34296f881e12aa955f7014d276895e0e4e868ba7fddebbde38e378"}, + {file = "ujson-5.9.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6eecbd09b316cea1fd929b1e25f70382917542ab11b692cb46ec9b0a26c7427f"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:473fb8dff1d58f49912323d7cb0859df5585cfc932e4b9c053bf8cf7f2d7c5c4"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f91719c6abafe429c1a144cfe27883eace9fb1c09a9c5ef1bcb3ae80a3076a4e"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b1c0991c4fe256f5fdb19758f7eac7f47caac29a6c57d0de16a19048eb86bad"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a8ea0f55a1396708e564595aaa6696c0d8af532340f477162ff6927ecc46e21"}, + {file = "ujson-5.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:07e0cfdde5fd91f54cd2d7ffb3482c8ff1bf558abf32a8b953a5d169575ae1cd"}, + {file = "ujson-5.9.0.tar.gz", hash = "sha256:89cc92e73d5501b8a7f48575eeb14ad27156ad092c2e9fc7e3cf949f07e75532"}, +] + [[package]] name = "urllib3" version = "1.26.18" @@ -766,6 +1375,76 @@ brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotl secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +[[package]] +name = "uvicorn" +version = "0.29.0" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"}, + {file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "uvloop" +version = "0.19.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"}, + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5"}, + {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, +] + +[package.extras] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + [[package]] name = "virtualenv" version = "20.25.3" @@ -786,7 +1465,175 @@ platformdirs = ">=3.9.1,<5" docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +[[package]] +name = "watchfiles" +version = "0.21.0" +description = "Simple, modern and high performance file watching and code reload in python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa"}, + {file = "watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d"}, + {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c"}, + {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9"}, + {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9"}, + {file = "watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293"}, + {file = "watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235"}, + {file = "watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7"}, + {file = "watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d"}, + {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7"}, + {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0"}, + {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365"}, + {file = "watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400"}, + {file = "watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe"}, + {file = "watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078"}, + {file = "watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a"}, + {file = "watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7"}, + {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c"}, + {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235"}, + {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7"}, + {file = "watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3"}, + {file = "watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094"}, + {file = "watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6"}, + {file = "watchfiles-0.21.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:4ea10a29aa5de67de02256a28d1bf53d21322295cb00bd2d57fcd19b850ebd99"}, + {file = "watchfiles-0.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:40bca549fdc929b470dd1dbfcb47b3295cb46a6d2c90e50588b0a1b3bd98f429"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9b37a7ba223b2f26122c148bb8d09a9ff312afca998c48c725ff5a0a632145f7"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec8c8900dc5c83650a63dd48c4d1d245343f904c4b64b48798c67a3767d7e165"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ad3fe0a3567c2f0f629d800409cd528cb6251da12e81a1f765e5c5345fd0137"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d353c4cfda586db2a176ce42c88f2fc31ec25e50212650c89fdd0f560ee507b"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83a696da8922314ff2aec02987eefb03784f473281d740bf9170181829133765"}, + {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a03651352fc20975ee2a707cd2d74a386cd303cc688f407296064ad1e6d1562"}, + {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ad692bc7792be8c32918c699638b660c0de078a6cbe464c46e1340dadb94c19"}, + {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06247538e8253975bdb328e7683f8515ff5ff041f43be6c40bff62d989b7d0b0"}, + {file = "watchfiles-0.21.0-cp38-none-win32.whl", hash = "sha256:9a0aa47f94ea9a0b39dd30850b0adf2e1cd32a8b4f9c7aa443d852aacf9ca214"}, + {file = "watchfiles-0.21.0-cp38-none-win_amd64.whl", hash = "sha256:8d5f400326840934e3507701f9f7269247f7c026d1b6cfd49477d2be0933cfca"}, + {file = "watchfiles-0.21.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7f762a1a85a12cc3484f77eee7be87b10f8c50b0b787bb02f4e357403cad0c0e"}, + {file = "watchfiles-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6e9be3ef84e2bb9710f3f777accce25556f4a71e15d2b73223788d528fcc2052"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4c48a10d17571d1275701e14a601e36959ffada3add8cdbc9e5061a6e3579a5d"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c889025f59884423428c261f212e04d438de865beda0b1e1babab85ef4c0f01"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66fac0c238ab9a2e72d026b5fb91cb902c146202bbd29a9a1a44e8db7b710b6f"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4a21f71885aa2744719459951819e7bf5a906a6448a6b2bbce8e9cc9f2c8128"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c9198c989f47898b2c22201756f73249de3748e0fc9de44adaf54a8b259cc0c"}, + {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f57c4461cd24fda22493109c45b3980863c58a25b8bec885ca8bea6b8d4b28"}, + {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:853853cbf7bf9408b404754b92512ebe3e3a83587503d766d23e6bf83d092ee6"}, + {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d5b1dc0e708fad9f92c296ab2f948af403bf201db8fb2eb4c8179db143732e49"}, + {file = "watchfiles-0.21.0-cp39-none-win32.whl", hash = "sha256:59137c0c6826bd56c710d1d2bda81553b5e6b7c84d5a676747d80caf0409ad94"}, + {file = "watchfiles-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:6cb8fdc044909e2078c248986f2fc76f911f72b51ea4a4fbbf472e01d14faa58"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c"}, + {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:08dca260e85ffae975448e344834d765983237ad6dc308231aa16e7933db763e"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ccceb50c611c433145502735e0370877cced72a6c70fd2410238bcbc7fe51d8"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57d430f5fb63fea141ab71ca9c064e80de3a20b427ca2febcbfcef70ff0ce895"}, + {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd5fad9b9c0dd89904bbdea978ce89a2b692a7ee8a0ce19b940e538c88a809c"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:be6dd5d52b73018b21adc1c5d28ac0c68184a64769052dfeb0c5d9998e7f56a2"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b3cab0e06143768499384a8a5efb9c4dc53e19382952859e4802f294214f36ec"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6ed10c2497e5fedadf61e465b3ca12a19f96004c15dcffe4bd442ebadc2d85"}, + {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43babacef21c519bc6631c5fce2a61eccdfc011b4bcb9047255e9620732c8097"}, + {file = "watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + +[[package]] +name = "websockets" +version = "12.0" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, + {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, + {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, + {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, + {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, + {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, + {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, + {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, + {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, + {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, + {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, + {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, + {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, + {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, + {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, + {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, + {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, + {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, + {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, + {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, + {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, + {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, + {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, + {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, + {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, +] + [metadata] lock-version = "2.0" python-versions = ">=3.8" -content-hash = "72b7362f5f84bff8c84aefc41e8ea1747692ae0c66d4e0c0169ceda9bea67196" +content-hash = "e17c06bc0dfb64dc90ed83f7fd1bee2f74204b8bcc5539a6e3a0cac6b8f90b3c" diff --git a/pyproject.toml b/pyproject.toml index aabe4ae..024b4b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,8 @@ urllib3 = "1.26.18" pyopenssl = "24.1.0" beautifulsoup4 = "4.12.3" pydantic = ">=2.0.0" +fastapi = "^0.111.0" +uvicorn = "^0.29.0" [tool.poetry.dev-dependencies] pytest = "^7.4.3" From cf14f2bb24eca7472014e470ddc1a1164be259ef Mon Sep 17 00:00:00 2001 From: longsihua2003 <1161443436@qq.com> Date: Tue, 7 May 2024 23:30:00 +0800 Subject: [PATCH 4/6] ecjtu-api end --- ecjtu/client.py | 15 +- ecjtu/ecjtu_api/api.py | 83 ++++++--- ecjtu/ecjtu_api/auth.py | 111 +++++++++++- ecjtu/ecjtu_api/ecjtu_schema.py | 6 - ecjtu/ecjtu_api/middle.py | 14 +- ecjtu/ecjtu_api/respose_result.py | 8 + ecjtu/ecjtu_api/schema.py | 22 +++ ecjtu/utils/cookie.py | 23 +++ ecjtu/utils/logger.py | 5 + poetry.lock | 286 ++++++++++++++++-------------- pyproject.toml | 3 + 11 files changed, 393 insertions(+), 183 deletions(-) delete mode 100644 ecjtu/ecjtu_api/ecjtu_schema.py create mode 100644 ecjtu/ecjtu_api/schema.py create mode 100644 ecjtu/utils/cookie.py diff --git a/ecjtu/client.py b/ecjtu/client.py index 97452a2..71fff40 100644 --- a/ecjtu/client.py +++ b/ecjtu/client.py @@ -63,7 +63,11 @@ def has_login(self) -> bool: class ECJTU(BaseClient[httpx.Client], httpx.Client): def __init__( - self, stud_id: Optional[str] = None, password: Optional[str] = None, **kwargs + self, + stud_id: Optional[str] = None, + password: Optional[str] = None, + cookie: Optional[CookieTypes] = None, + **kwargs, ) -> None: """Initialize ECJTU client. @@ -73,9 +77,12 @@ def __init__( """ super().__init__(verify=False, **kwargs) - self.stud_id: str = stud_id or os.environ.get("ECJTU_STUDENT_ID") - self.password: str = password or os.environ.get("ECJTU_PASSWORD") - self.enc_password: str = _get_enc_password(self.password) + if cookie: + self.cookies = cookie + else: + self.stud_id: str = stud_id or os.environ.get("ECJTU_STUDENT_ID") + self.password: str = password or os.environ.get("ECJTU_PASSWORD") + self.enc_password: str = _get_enc_password(self.password) self.scheduled_courses = crud.ScheduledCourseCRUD(self) self.scores = crud.ScoreCRUD(self) diff --git a/ecjtu/ecjtu_api/api.py b/ecjtu/ecjtu_api/api.py index 2db1afb..6e9b5cf 100644 --- a/ecjtu/ecjtu_api/api.py +++ b/ecjtu/ecjtu_api/api.py @@ -1,9 +1,12 @@ +import datetime +import re + from fastapi import FastAPI, Header from fastapi.responses import RedirectResponse from ecjtu.client import ECJTU -from . import auth, ecjtu_schema, middle, respose_result +from . import auth, middle, respose_result, schema app = FastAPI(title="ECJTU API", description="API for ECJTU") @@ -22,21 +25,37 @@ def push_docs(): summary="登录", description="登录并获取token,以下所有接口都需要token才可以使用", ) -def login(user: ecjtu_schema.UserLoginSchema): - client = ECJTU(user.stud_id, user.password) +def login(user: schema.UserLoginSchema): + try: + access_token, refresh_token = auth.create_tokens(user.stud_id, user.password) + except Exception as e: + return respose_result.ResponseResult.error(str(e)) + + return respose_result.ResponseResult.success( + {"access_token": access_token, "refresh_token": refresh_token} + ) + + +@app.post( + "/refresh_token", + tags=["登录"], + summary="刷新access_token", + description="刷新access_token", +) +def refresh_token(data: str = None): try: - client.login() + access_token = auth.refresh_access_token(data) except Exception as e: return respose_result.ResponseResult.error(str(e)) - token = auth.encode(user.stud_id, user.password) - return respose_result.ResponseResult.success({"token": token}) + return respose_result.ResponseResult.success({"access_token": access_token}) # gpa接口 @app.get("/gpa", tags=["GPA"], summary="获取GPA", description="获取当学期GPA") def gpa(token: str = Header(None)): - stud_id, password = auth.decode(token) - client = ECJTU(stud_id, password) + stud_id = auth.get_stud_id(token) + cookie = auth.get_cookie(stud_id) + client = ECJTU(cookie=cookie) try: gpa = client.gpa.today() except Exception as e: @@ -47,8 +66,9 @@ def gpa(token: str = Header(None)): # 课表接口 @app.get("/schedule", tags=["课表"], summary="获取当天课表", description="获取当天课表") def schedule(token: str = Header(None)): - stud_id, password = auth.decode(token) - client = ECJTU(stud_id, password) + stud_id = auth.get_stud_id(token) + cookie = auth.get_cookie(stud_id) + client = ECJTU(cookie=cookie) try: schedule = client.scheduled_courses.today() except Exception as e: @@ -67,10 +87,16 @@ def schedule(token: str = Header(None)): ) def schedule_date(token: str = Header(None), date: str = None): # date(str): The date to filter, eg: 2023-01-01 - stud_id, password = auth.decode(token) - client = ECJTU(stud_id, password) try: - scheduled_courses = client.scheduled_courses.filter(date=date) + valid_date = datetime.datetime.strptime(date, "%Y-%m-%d").date() + except ValueError: + return respose_result.ResponseResult.param_error("日期格式错误") + + stud_id = auth.get_stud_id(token) + cookie = auth.get_cookie(stud_id) + client = ECJTU(cookie=cookie) + try: + scheduled_courses = client.scheduled_courses.filter(date=valid_date) except Exception as e: return respose_result.ResponseResult.error(str(e)) schedule_list = [] @@ -80,11 +106,12 @@ def schedule_date(token: str = Header(None), date: str = None): @app.get( - "/schedule/week", tags=["课表"], summary="获取本周课表", description="获取本周课表" + "/schedule_week", tags=["课表"], summary="获取本周课表", description="获取本周课表" ) def schedule_week(token: str = Header(None)): - stud_id, password = auth.decode(token) - client = ECJTU(stud_id, password) + stud_id = auth.get_stud_id(token) + cookie = auth.get_cookie(stud_id) + client = ECJTU(cookie=cookie) try: schedule = client.scheduled_courses.this_week() except Exception as e: @@ -101,8 +128,9 @@ def schedule_week(token: str = Header(None)): # 成绩接口 @app.get("/score", tags=["成绩"], summary="获取当前成绩", description="获取当学期成绩") def score(token: str = Header(None)): - stud_id, password = auth.decode(token) - client = ECJTU(stud_id, password) + stud_id = auth.get_stud_id(token) + cookie = auth.get_cookie(stud_id) + client = ECJTU(cookie=cookie) try: score = client.scores.today() except Exception as e: @@ -121,8 +149,11 @@ def score(token: str = Header(None)): ) def score_semester(token: str = Header(None), semester: str = None): # semester(Optional[str]): The semester to filter, eg: 2023.1, 2023.2 - stud_id, password = auth.decode(token) - client = ECJTU(stud_id, password) + if not re.match(r"\d{4}\.[12]", semester): + return respose_result.ResponseResult.param_error("学期格式错误") + stud_id = auth.get_stud_id(token) + cookie = auth.get_cookie(stud_id) + client = ECJTU(cookie=cookie) try: scores = client.scores.filter(semester=semester) except Exception as e: @@ -141,8 +172,9 @@ def score_semester(token: str = Header(None), semester: str = None): description="获取当前学期选课情况", ) def elective_courses(token: str = Header(None)): - stud_id, password = auth.decode(token) - client = ECJTU(stud_id, password) + stud_id = auth.get_stud_id(token) + cookie = auth.get_cookie(stud_id) + client = ECJTU(cookie=cookie) try: elective_courses = client.elective_courses.today() except Exception as e: @@ -161,8 +193,11 @@ def elective_courses(token: str = Header(None)): ) def elective_courses_semester(token: str = Header(None), semester: str = None): # semester(Optional[str]): The semester to filter, eg: 2023.1, 2023.2 - stud_id, password = auth.decode(token) - client = ECJTU(stud_id, password) + if not re.match(r"\d{4}\.[12]", semester): + return respose_result.ResponseResult.param_error("学期格式错误") + stud_id = auth.get_stud_id(token) + cookie = auth.get_cookie(stud_id) + client = ECJTU(cookie=cookie) try: elective_courses = client.elective_courses.filter(semester=semester) except Exception as e: diff --git a/ecjtu/ecjtu_api/auth.py b/ecjtu/ecjtu_api/auth.py index 8ba0050..193e5d3 100644 --- a/ecjtu/ecjtu_api/auth.py +++ b/ecjtu/ecjtu_api/auth.py @@ -1,14 +1,109 @@ import base64 +import datetime -KEY = "zxcvbnmasdgfhjklpoiuytrewq" +from cushy_storage import CushyOrmCache +from ecjtu.client import ECJTU +from ecjtu.utils.cookie import cookies_tolist, list_tocookie +from ecjtu.utils.logger import get_path -def encode(stud_id, pwd): - # enc_pwd = _get_enc_password(pwd); - token = base64.b64encode(f"{stud_id}:{pwd}".encode()).decode() - return token +from . import schema -def decode(token): - stud_id, enc_pwd = base64.b64decode(token.encode()).decode().split(":") - return stud_id, enc_pwd +def encode_data(data): + # 将数据编码为base64字符串 + return base64.b64encode(data.encode()).decode() + + +def decode_data(encoded_data): + # 解码base64字符串 + return base64.b64decode(encoded_data.encode()).decode() + + +# 双token加密 +def create_tokens(stud_id, pwd): + access_time = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta( + minutes=60 + ) + access_data = f"{stud_id}:access_token:{access_time}" + refresh_time = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta( + days=7 + ) + refresh_data = f"{stud_id}:{pwd}:{refresh_time}" + + access_token = encode_data(access_data) + refresh_token = encode_data(refresh_data) + client = ECJTU(stud_id, pwd) + try: + client.login() + except Exception as e: + raise e + + cookie_list = cookies_tolist(client.cookies) + stud_file = CushyOrmCache(get_path()) + stud = stud_file.query("FileAuth").filter(stud_id=stud_id).first() + + # 创建用户储存信息 + if not stud: + stud = schema.FileAuth(stud_id, access_token, cookie_list) + stud_file.add(stud) + return access_token, refresh_token + # 更新用户信息 + stud.cookie = cookie_list + stud.token = access_token + stud_file.update_obj(stud) + return access_token, refresh_token + + +# 刷新access_token +def refresh_access_token(refresh_token): + data = decode_data(refresh_token) + print(data) + stud_id = data.split(":")[0] + pwd = data.split(":")[1] + token_time = data.split(":")[2] + + expire_time = datetime.datetime.fromisoformat(token_time) + expire_time = expire_time.replace(tzinfo=datetime.timezone.utc) + current_time = datetime.datetime.now(datetime.timezone.utc) + + if current_time > expire_time: + raise Exception("令牌已过期,请重新登录") + else: + try: + create_tokens(stud_id, pwd) + except Exception: + raise Exception("令牌有误,请重新登录") + return create_tokens(stud_id, pwd)[0] + + +# 验证和读取access_token的stud_id +def get_stud_id(access_token): + try: + data = decode_data(access_token) + stud_file = CushyOrmCache(get_path()) + stud = stud_file.query("FileAuth").filter(token=access_token).first() + if not stud: + raise Exception("无效令牌") + except Exception as e: + # raise Exception("无效令牌") + raise e + stud_id = data.split(":")[0] + token_time = data.split(":")[2] + + expire_time = datetime.datetime.fromisoformat(token_time) + expire_time = expire_time.replace(tzinfo=datetime.timezone.utc) + current_time = datetime.datetime.now(datetime.timezone.utc) + + if current_time > expire_time: + raise Exception("令牌已过期,请刷新") + return stud_id + # return data.split(':')[0] + + +# 读取cookie +def get_cookie(stud_id): + stud_file = CushyOrmCache(get_path()) + stud = stud_file.query("FileAuth").filter(stud_id=stud_id).first() + cookies = list_tocookie(stud.cookie) + return cookies diff --git a/ecjtu/ecjtu_api/ecjtu_schema.py b/ecjtu/ecjtu_api/ecjtu_schema.py deleted file mode 100644 index 8a4406d..0000000 --- a/ecjtu/ecjtu_api/ecjtu_schema.py +++ /dev/null @@ -1,6 +0,0 @@ -from pydantic import BaseModel, Field - - -class UserLoginSchema(BaseModel): - stud_id: str = Field(..., min_length=1) - password: str = Field(..., min_length=1) diff --git a/ecjtu/ecjtu_api/middle.py b/ecjtu/ecjtu_api/middle.py index e69d75a..c97e98a 100644 --- a/ecjtu/ecjtu_api/middle.py +++ b/ecjtu/ecjtu_api/middle.py @@ -7,7 +7,7 @@ class MyMiddleware(BaseHTTPMiddleware): async def dispatch(self, request, call_next): # 不被拦截的路由 secure_routes = [ - "/token", + "/refresh_token", "/login", "/", "/docs", @@ -24,9 +24,15 @@ async def dispatch(self, request, call_next): # 是否存在token if not header: return response - token = header - stud_id, enc_pwd = auth.decode(token) - if not stud_id or not enc_pwd: + # 是否过期 + try: + stud_id = auth.get_stud_id(header) + except Exception as e: + return respose_result.ResponseResult.auth_error(str(e)) + if not stud_id: + response = respose_result.ResponseResult.auth_error( + "access_token令牌已过期,请刷新" + ) return response response = await call_next(request) diff --git a/ecjtu/ecjtu_api/respose_result.py b/ecjtu/ecjtu_api/respose_result.py index a3a05c4..0bb3722 100644 --- a/ecjtu/ecjtu_api/respose_result.py +++ b/ecjtu/ecjtu_api/respose_result.py @@ -30,6 +30,14 @@ def auth_error( status_code=401, content={"code": 401, "msg": msg, "data": data} ) + @staticmethod + def param_error( + data: Union[Dict[str, Any], Any] = None, msg: str = "param error" + ) -> JSONResponse: + return JSONResponse( + status_code=400, content={"code": 400, "msg": msg, "data": data} + ) + @staticmethod def error( data: Union[Dict[str, Any], Any] = None, msg: str = "error" diff --git a/ecjtu/ecjtu_api/schema.py b/ecjtu/ecjtu_api/schema.py new file mode 100644 index 0000000..4063b05 --- /dev/null +++ b/ecjtu/ecjtu_api/schema.py @@ -0,0 +1,22 @@ +from typing import Dict, List, Optional + +from cushy_storage import BaseORMModel, CushyOrmCache +from pydantic import BaseModel, Field + + +class UserLoginSchema(BaseModel): + stud_id: str = Field(..., min_length=1) + password: str = Field(..., min_length=1) + + +class FileAuth(BaseORMModel): + def __init__( + self, + stud_id: Optional[str] = None, + token: str = None, + cookie: Optional[List[Dict]] = None, + ): + super().__init__() + self.stud_id = stud_id + self.token = token + self.cookie = cookie diff --git a/ecjtu/utils/cookie.py b/ecjtu/utils/cookie.py new file mode 100644 index 0000000..813d095 --- /dev/null +++ b/ecjtu/utils/cookie.py @@ -0,0 +1,23 @@ +from typing import Optional + +import httpx +from httpx._types import CookieTypes + + +def cookies_tolist(cookies: Optional[CookieTypes]): + cookies_list = [] + for cookie in cookies.jar: + dict = { + "name": cookie.name, + "value": cookie.value, + "domain": cookie.domain, + } + cookies_list.append(dict) + return cookies_list + + +def list_tocookie(cookies_list: list): + cookies = httpx.Cookies() + for cookie in cookies_list: + cookies.set(**cookie) + return cookies diff --git a/ecjtu/utils/logger.py b/ecjtu/utils/logger.py index e9e00f5..0eb5e68 100644 --- a/ecjtu/utils/logger.py +++ b/ecjtu/utils/logger.py @@ -14,6 +14,11 @@ def get_log_path() -> str: return f"{log_directory}/{current_time}.log" +def get_path() -> str: + log_directory = get_default_storage_path("logs") + return f"{log_directory}" + + class LogManager(metaclass=Singleton): def __init__(self) -> None: self.logger = logging.getLogger("ecjtu") diff --git a/poetry.lock b/poetry.lock index 4528467..9c81838 100644 --- a/poetry.lock +++ b/poetry.lock @@ -235,13 +235,13 @@ toml = ["tomli"] [[package]] name = "coverage-badge" -version = "1.1.0" +version = "1.1.1" description = "Generate coverage badges for Coverage.py." optional = false python-versions = "*" files = [ - {file = "coverage-badge-1.1.0.tar.gz", hash = "sha256:c824a106503e981c02821e7d32f008fb3984b2338aa8c3800ec9357e33345b78"}, - {file = "coverage_badge-1.1.0-py2.py3-none-any.whl", hash = "sha256:e365d56e5202e923d1b237f82defd628a02d1d645a147f867ac85c58c81d7997"}, + {file = "coverage-badge-1.1.1.tar.gz", hash = "sha256:42252df917404af6147380861228a4ace3d9a29804df8fc2d34a22b2bc4f45b6"}, + {file = "coverage_badge-1.1.1-py2.py3-none-any.whl", hash = "sha256:1d8e566ad47c37910fa2bbc74ea19972b171b5b4e40624b31b3e2f2d93680266"}, ] [package.dependencies] @@ -249,43 +249,43 @@ coverage = "*" [[package]] name = "cryptography" -version = "42.0.5" +version = "42.0.7" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"}, - {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"}, - {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"}, - {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"}, - {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"}, - {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"}, - {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"}, - {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"}, - {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"}, - {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"}, - {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"}, - {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"}, - {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"}, - {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"}, + {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477"}, + {file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55"}, + {file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da"}, + {file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7"}, + {file = "cryptography-42.0.7-cp37-abi3-win32.whl", hash = "sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b"}, + {file = "cryptography-42.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678"}, + {file = "cryptography-42.0.7-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda"}, + {file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1"}, + {file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886"}, + {file = "cryptography-42.0.7-cp39-abi3-win32.whl", hash = "sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda"}, + {file = "cryptography-42.0.7-cp39-abi3-win_amd64.whl", hash = "sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd"}, + {file = "cryptography-42.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9"}, + {file = "cryptography-42.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68"}, + {file = "cryptography-42.0.7.tar.gz", hash = "sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2"}, ] [package.dependencies] @@ -301,6 +301,17 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] +[[package]] +name = "cushy-storage" +version = "1.3.8" +description = "A data local persistence ORM framework." +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "cushy_storage-1.3.8-py3-none-any.whl", hash = "sha256:683f58fea2d05897f02ac24e80b4aff06ab45238e254ecbe933bc5d459c5b53f"}, + {file = "cushy_storage-1.3.8.tar.gz", hash = "sha256:82e5af7329da08d0190d26f4cc9a217eac74c94754570e80429dedf6421c48db"}, +] + [[package]] name = "distlib" version = "0.3.8" @@ -406,13 +417,13 @@ uvicorn = {version = ">=0.29.0", extras = ["standard"]} [[package]] name = "filelock" -version = "3.13.4" +version = "3.14.0" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"}, - {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"}, + {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, + {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, ] [package.extras] @@ -526,13 +537,13 @@ socks = ["socksio (==1.*)"] [[package]] name = "identify" -version = "2.5.35" +version = "2.5.36" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, - {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, + {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"}, + {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"}, ] [package.extras] @@ -763,28 +774,29 @@ files = [ [[package]] name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "4.2.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, + {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, + {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, ] [package.extras] docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] [[package]] name = "pluggy" -version = "1.4.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -833,18 +845,18 @@ files = [ [[package]] name = "pydantic" -version = "2.7.0" +version = "2.7.1" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.7.0-py3-none-any.whl", hash = "sha256:9dee74a271705f14f9a1567671d144a851c675b072736f0a7b2608fd9e495352"}, - {file = "pydantic-2.7.0.tar.gz", hash = "sha256:b5ecdd42262ca2462e2624793551e80911a1e989f462910bb81aef974b4bb383"}, + {file = "pydantic-2.7.1-py3-none-any.whl", hash = "sha256:e029badca45266732a9a79898a15ae2e8b14840b1eabbb25844be28f0b33f3d5"}, + {file = "pydantic-2.7.1.tar.gz", hash = "sha256:e9dbb5eada8abe4d9ae5f46b9939aead650cd2b68f249bb3a8139dbe125803cc"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.18.1" +pydantic-core = "2.18.2" typing-extensions = ">=4.6.1" [package.extras] @@ -852,90 +864,90 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.18.1" +version = "2.18.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.18.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ee9cf33e7fe14243f5ca6977658eb7d1042caaa66847daacbd2117adb258b226"}, - {file = "pydantic_core-2.18.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6b7bbb97d82659ac8b37450c60ff2e9f97e4eb0f8a8a3645a5568b9334b08b50"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df4249b579e75094f7e9bb4bd28231acf55e308bf686b952f43100a5a0be394c"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d0491006a6ad20507aec2be72e7831a42efc93193d2402018007ff827dc62926"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ae80f72bb7a3e397ab37b53a2b49c62cc5496412e71bc4f1277620a7ce3f52b"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58aca931bef83217fca7a390e0486ae327c4af9c3e941adb75f8772f8eeb03a1"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1be91ad664fc9245404a789d60cba1e91c26b1454ba136d2a1bf0c2ac0c0505a"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:667880321e916a8920ef49f5d50e7983792cf59f3b6079f3c9dac2b88a311d17"}, - {file = "pydantic_core-2.18.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f7054fdc556f5421f01e39cbb767d5ec5c1139ea98c3e5b350e02e62201740c7"}, - {file = "pydantic_core-2.18.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:030e4f9516f9947f38179249778709a460a3adb516bf39b5eb9066fcfe43d0e6"}, - {file = "pydantic_core-2.18.1-cp310-none-win32.whl", hash = "sha256:2e91711e36e229978d92642bfc3546333a9127ecebb3f2761372e096395fc649"}, - {file = "pydantic_core-2.18.1-cp310-none-win_amd64.whl", hash = "sha256:9a29726f91c6cb390b3c2338f0df5cd3e216ad7a938762d11c994bb37552edb0"}, - {file = "pydantic_core-2.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9ece8a49696669d483d206b4474c367852c44815fca23ac4e48b72b339807f80"}, - {file = "pydantic_core-2.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a5d83efc109ceddb99abd2c1316298ced2adb4570410defe766851a804fcd5b"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7973c381283783cd1043a8c8f61ea5ce7a3a58b0369f0ee0ee975eaf2f2a1b"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:54c7375c62190a7845091f521add19b0f026bcf6ae674bdb89f296972272e86d"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd63cec4e26e790b70544ae5cc48d11b515b09e05fdd5eff12e3195f54b8a586"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:561cf62c8a3498406495cfc49eee086ed2bb186d08bcc65812b75fda42c38294"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68717c38a68e37af87c4da20e08f3e27d7e4212e99e96c3d875fbf3f4812abfc"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d5728e93d28a3c63ee513d9ffbac9c5989de8c76e049dbcb5bfe4b923a9739d"}, - {file = "pydantic_core-2.18.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f0f17814c505f07806e22b28856c59ac80cee7dd0fbb152aed273e116378f519"}, - {file = "pydantic_core-2.18.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d816f44a51ba5175394bc6c7879ca0bd2be560b2c9e9f3411ef3a4cbe644c2e9"}, - {file = "pydantic_core-2.18.1-cp311-none-win32.whl", hash = "sha256:09f03dfc0ef8c22622eaa8608caa4a1e189cfb83ce847045eca34f690895eccb"}, - {file = "pydantic_core-2.18.1-cp311-none-win_amd64.whl", hash = "sha256:27f1009dc292f3b7ca77feb3571c537276b9aad5dd4efb471ac88a8bd09024e9"}, - {file = "pydantic_core-2.18.1-cp311-none-win_arm64.whl", hash = "sha256:48dd883db92e92519201f2b01cafa881e5f7125666141a49ffba8b9facc072b0"}, - {file = "pydantic_core-2.18.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b6b0e4912030c6f28bcb72b9ebe4989d6dc2eebcd2a9cdc35fefc38052dd4fe8"}, - {file = "pydantic_core-2.18.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3202a429fe825b699c57892d4371c74cc3456d8d71b7f35d6028c96dfecad31"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3982b0a32d0a88b3907e4b0dc36809fda477f0757c59a505d4e9b455f384b8b"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25595ac311f20e5324d1941909b0d12933f1fd2171075fcff763e90f43e92a0d"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14fe73881cf8e4cbdaded8ca0aa671635b597e42447fec7060d0868b52d074e6"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca976884ce34070799e4dfc6fbd68cb1d181db1eefe4a3a94798ddfb34b8867f"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684d840d2c9ec5de9cb397fcb3f36d5ebb6fa0d94734f9886032dd796c1ead06"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:54764c083bbe0264f0f746cefcded6cb08fbbaaf1ad1d78fb8a4c30cff999a90"}, - {file = "pydantic_core-2.18.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:201713f2f462e5c015b343e86e68bd8a530a4f76609b33d8f0ec65d2b921712a"}, - {file = "pydantic_core-2.18.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fd1a9edb9dd9d79fbeac1ea1f9a8dd527a6113b18d2e9bcc0d541d308dae639b"}, - {file = "pydantic_core-2.18.1-cp312-none-win32.whl", hash = "sha256:d5e6b7155b8197b329dc787356cfd2684c9d6a6b1a197f6bbf45f5555a98d411"}, - {file = "pydantic_core-2.18.1-cp312-none-win_amd64.whl", hash = "sha256:9376d83d686ec62e8b19c0ac3bf8d28d8a5981d0df290196fb6ef24d8a26f0d6"}, - {file = "pydantic_core-2.18.1-cp312-none-win_arm64.whl", hash = "sha256:c562b49c96906b4029b5685075fe1ebd3b5cc2601dfa0b9e16c2c09d6cbce048"}, - {file = "pydantic_core-2.18.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:3e352f0191d99fe617371096845070dee295444979efb8f27ad941227de6ad09"}, - {file = "pydantic_core-2.18.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0295d52b012cbe0d3059b1dba99159c3be55e632aae1999ab74ae2bd86a33d7"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56823a92075780582d1ffd4489a2e61d56fd3ebb4b40b713d63f96dd92d28144"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd3f79e17b56741b5177bcc36307750d50ea0698df6aa82f69c7db32d968c1c2"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38a5024de321d672a132b1834a66eeb7931959c59964b777e8f32dbe9523f6b1"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ce426ee691319d4767748c8e0895cfc56593d725594e415f274059bcf3cb76"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2adaeea59849ec0939af5c5d476935f2bab4b7f0335b0110f0f069a41024278e"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9b6431559676a1079eac0f52d6d0721fb8e3c5ba43c37bc537c8c83724031feb"}, - {file = "pydantic_core-2.18.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:85233abb44bc18d16e72dc05bf13848a36f363f83757541f1a97db2f8d58cfd9"}, - {file = "pydantic_core-2.18.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:641a018af4fe48be57a2b3d7a1f0f5dbca07c1d00951d3d7463f0ac9dac66622"}, - {file = "pydantic_core-2.18.1-cp38-none-win32.whl", hash = "sha256:63d7523cd95d2fde0d28dc42968ac731b5bb1e516cc56b93a50ab293f4daeaad"}, - {file = "pydantic_core-2.18.1-cp38-none-win_amd64.whl", hash = "sha256:907a4d7720abfcb1c81619863efd47c8a85d26a257a2dbebdb87c3b847df0278"}, - {file = "pydantic_core-2.18.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:aad17e462f42ddbef5984d70c40bfc4146c322a2da79715932cd8976317054de"}, - {file = "pydantic_core-2.18.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:94b9769ba435b598b547c762184bcfc4783d0d4c7771b04a3b45775c3589ca44"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80e0e57cc704a52fb1b48f16d5b2c8818da087dbee6f98d9bf19546930dc64b5"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76b86e24039c35280ceee6dce7e62945eb93a5175d43689ba98360ab31eebc4a"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a05db5013ec0ca4a32cc6433f53faa2a014ec364031408540ba858c2172bb0"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:250ae39445cb5475e483a36b1061af1bc233de3e9ad0f4f76a71b66231b07f88"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a32204489259786a923e02990249c65b0f17235073149d0033efcebe80095570"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6395a4435fa26519fd96fdccb77e9d00ddae9dd6c742309bd0b5610609ad7fb2"}, - {file = "pydantic_core-2.18.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2533ad2883f001efa72f3d0e733fb846710c3af6dcdd544fe5bf14fa5fe2d7db"}, - {file = "pydantic_core-2.18.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b560b72ed4816aee52783c66854d96157fd8175631f01ef58e894cc57c84f0f6"}, - {file = "pydantic_core-2.18.1-cp39-none-win32.whl", hash = "sha256:582cf2cead97c9e382a7f4d3b744cf0ef1a6e815e44d3aa81af3ad98762f5a9b"}, - {file = "pydantic_core-2.18.1-cp39-none-win_amd64.whl", hash = "sha256:ca71d501629d1fa50ea7fa3b08ba884fe10cefc559f5c6c8dfe9036c16e8ae89"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e178e5b66a06ec5bf51668ec0d4ac8cfb2bdcb553b2c207d58148340efd00143"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:72722ce529a76a4637a60be18bd789d8fb871e84472490ed7ddff62d5fed620d"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fe0c1ce5b129455e43f941f7a46f61f3d3861e571f2905d55cdbb8b5c6f5e2c"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4284c621f06a72ce2cb55f74ea3150113d926a6eb78ab38340c08f770eb9b4d"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a0c3e718f4e064efde68092d9d974e39572c14e56726ecfaeebbe6544521f47"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2027493cc44c23b598cfaf200936110433d9caa84e2c6cf487a83999638a96ac"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:76909849d1a6bffa5a07742294f3fa1d357dc917cb1fe7b470afbc3a7579d539"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ee7ccc7fb7e921d767f853b47814c3048c7de536663e82fbc37f5eb0d532224b"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ee2794111c188548a4547eccc73a6a8527fe2af6cf25e1a4ebda2fd01cdd2e60"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a139fe9f298dc097349fb4f28c8b81cc7a202dbfba66af0e14be5cfca4ef7ce5"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d074b07a10c391fc5bbdcb37b2f16f20fcd9e51e10d01652ab298c0d07908ee2"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c69567ddbac186e8c0aadc1f324a60a564cfe25e43ef2ce81bcc4b8c3abffbae"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:baf1c7b78cddb5af00971ad5294a4583188bda1495b13760d9f03c9483bb6203"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2684a94fdfd1b146ff10689c6e4e815f6a01141781c493b97342cdc5b06f4d5d"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:73c1bc8a86a5c9e8721a088df234265317692d0b5cd9e86e975ce3bc3db62a59"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e60defc3c15defb70bb38dd605ff7e0fae5f6c9c7cbfe0ad7868582cb7e844a6"}, - {file = "pydantic_core-2.18.1.tar.gz", hash = "sha256:de9d3e8717560eb05e28739d1b35e4eac2e458553a52a301e51352a7ffc86a35"}, + {file = "pydantic_core-2.18.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9e08e867b306f525802df7cd16c44ff5ebbe747ff0ca6cf3fde7f36c05a59a81"}, + {file = "pydantic_core-2.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f0a21cbaa69900cbe1a2e7cad2aa74ac3cf21b10c3efb0fa0b80305274c0e8a2"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0680b1f1f11fda801397de52c36ce38ef1c1dc841a0927a94f226dea29c3ae3d"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:95b9d5e72481d3780ba3442eac863eae92ae43a5f3adb5b4d0a1de89d42bb250"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fcf5cd9c4b655ad666ca332b9a081112cd7a58a8b5a6ca7a3104bc950f2038"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b5155ff768083cb1d62f3e143b49a8a3432e6789a3abee8acd005c3c7af1c74"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553ef617b6836fc7e4df130bb851e32fe357ce36336d897fd6646d6058d980af"}, + {file = "pydantic_core-2.18.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89ed9eb7d616ef5714e5590e6cf7f23b02d0d539767d33561e3675d6f9e3857"}, + {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:75f7e9488238e920ab6204399ded280dc4c307d034f3924cd7f90a38b1829563"}, + {file = "pydantic_core-2.18.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ef26c9e94a8c04a1b2924149a9cb081836913818e55681722d7f29af88fe7b38"}, + {file = "pydantic_core-2.18.2-cp310-none-win32.whl", hash = "sha256:182245ff6b0039e82b6bb585ed55a64d7c81c560715d1bad0cbad6dfa07b4027"}, + {file = "pydantic_core-2.18.2-cp310-none-win_amd64.whl", hash = "sha256:e23ec367a948b6d812301afc1b13f8094ab7b2c280af66ef450efc357d2ae543"}, + {file = "pydantic_core-2.18.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:219da3f096d50a157f33645a1cf31c0ad1fe829a92181dd1311022f986e5fbe3"}, + {file = "pydantic_core-2.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc1cfd88a64e012b74e94cd00bbe0f9c6df57049c97f02bb07d39e9c852e19a4"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b7133a6e6aeb8df37d6f413f7705a37ab4031597f64ab56384c94d98fa0e90"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:224c421235f6102e8737032483f43c1a8cfb1d2f45740c44166219599358c2cd"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b14d82cdb934e99dda6d9d60dc84a24379820176cc4a0d123f88df319ae9c150"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2728b01246a3bba6de144f9e3115b532ee44bd6cf39795194fb75491824a1413"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470b94480bb5ee929f5acba6995251ada5e059a5ef3e0dfc63cca287283ebfa6"}, + {file = "pydantic_core-2.18.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:997abc4df705d1295a42f95b4eec4950a37ad8ae46d913caeee117b6b198811c"}, + {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75250dbc5290e3f1a0f4618db35e51a165186f9034eff158f3d490b3fed9f8a0"}, + {file = "pydantic_core-2.18.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4456f2dca97c425231d7315737d45239b2b51a50dc2b6f0c2bb181fce6207664"}, + {file = "pydantic_core-2.18.2-cp311-none-win32.whl", hash = "sha256:269322dcc3d8bdb69f054681edff86276b2ff972447863cf34c8b860f5188e2e"}, + {file = "pydantic_core-2.18.2-cp311-none-win_amd64.whl", hash = "sha256:800d60565aec896f25bc3cfa56d2277d52d5182af08162f7954f938c06dc4ee3"}, + {file = "pydantic_core-2.18.2-cp311-none-win_arm64.whl", hash = "sha256:1404c69d6a676245199767ba4f633cce5f4ad4181f9d0ccb0577e1f66cf4c46d"}, + {file = "pydantic_core-2.18.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:fb2bd7be70c0fe4dfd32c951bc813d9fe6ebcbfdd15a07527796c8204bd36242"}, + {file = "pydantic_core-2.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6132dd3bd52838acddca05a72aafb6eab6536aa145e923bb50f45e78b7251043"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d904828195733c183d20a54230c0df0eb46ec746ea1a666730787353e87182"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9bd70772c720142be1020eac55f8143a34ec9f82d75a8e7a07852023e46617f"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8ed04b3582771764538f7ee7001b02e1170223cf9b75dff0bc698fadb00cf3"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dac87ddb34aaec85f873d737e9d06a3555a1cc1a8e0c44b7f8d5daeb89d86f"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca4ae5a27ad7a4ee5170aebce1574b375de390bc01284f87b18d43a3984df72"}, + {file = "pydantic_core-2.18.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:886eec03591b7cf058467a70a87733b35f44707bd86cf64a615584fd72488b7c"}, + {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca7b0c1f1c983e064caa85f3792dd2fe3526b3505378874afa84baf662e12241"}, + {file = "pydantic_core-2.18.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b4356d3538c3649337df4074e81b85f0616b79731fe22dd11b99499b2ebbdf3"}, + {file = "pydantic_core-2.18.2-cp312-none-win32.whl", hash = "sha256:8b172601454f2d7701121bbec3425dd71efcb787a027edf49724c9cefc14c038"}, + {file = "pydantic_core-2.18.2-cp312-none-win_amd64.whl", hash = "sha256:b1bd7e47b1558ea872bd16c8502c414f9e90dcf12f1395129d7bb42a09a95438"}, + {file = "pydantic_core-2.18.2-cp312-none-win_arm64.whl", hash = "sha256:98758d627ff397e752bc339272c14c98199c613f922d4a384ddc07526c86a2ec"}, + {file = "pydantic_core-2.18.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9fdad8e35f278b2c3eb77cbdc5c0a49dada440657bf738d6905ce106dc1de439"}, + {file = "pydantic_core-2.18.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1d90c3265ae107f91a4f279f4d6f6f1d4907ac76c6868b27dc7fb33688cfb347"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390193c770399861d8df9670fb0d1874f330c79caaca4642332df7c682bf6b91"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:82d5d4d78e4448683cb467897fe24e2b74bb7b973a541ea1dcfec1d3cbce39fb"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4774f3184d2ef3e14e8693194f661dea5a4d6ca4e3dc8e39786d33a94865cefd"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4d938ec0adf5167cb335acb25a4ee69a8107e4984f8fbd2e897021d9e4ca21b"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0e8b1be28239fc64a88a8189d1df7fad8be8c1ae47fcc33e43d4be15f99cc70"}, + {file = "pydantic_core-2.18.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:868649da93e5a3d5eacc2b5b3b9235c98ccdbfd443832f31e075f54419e1b96b"}, + {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:78363590ef93d5d226ba21a90a03ea89a20738ee5b7da83d771d283fd8a56761"}, + {file = "pydantic_core-2.18.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:852e966fbd035a6468fc0a3496589b45e2208ec7ca95c26470a54daed82a0788"}, + {file = "pydantic_core-2.18.2-cp38-none-win32.whl", hash = "sha256:6a46e22a707e7ad4484ac9ee9f290f9d501df45954184e23fc29408dfad61350"}, + {file = "pydantic_core-2.18.2-cp38-none-win_amd64.whl", hash = "sha256:d91cb5ea8b11607cc757675051f61b3d93f15eca3cefb3e6c704a5d6e8440f4e"}, + {file = "pydantic_core-2.18.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ae0a8a797a5e56c053610fa7be147993fe50960fa43609ff2a9552b0e07013e8"}, + {file = "pydantic_core-2.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:042473b6280246b1dbf530559246f6842b56119c2926d1e52b631bdc46075f2a"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a388a77e629b9ec814c1b1e6b3b595fe521d2cdc625fcca26fbc2d44c816804"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25add29b8f3b233ae90ccef2d902d0ae0432eb0d45370fe315d1a5cf231004b"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f459a5ce8434614dfd39bbebf1041952ae01da6bed9855008cb33b875cb024c0"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eff2de745698eb46eeb51193a9f41d67d834d50e424aef27df2fcdee1b153845"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8309f67285bdfe65c372ea3722b7a5642680f3dba538566340a9d36e920b5f0"}, + {file = "pydantic_core-2.18.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f93a8a2e3938ff656a7c1bc57193b1319960ac015b6e87d76c76bf14fe0244b4"}, + {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:22057013c8c1e272eb8d0eebc796701167d8377441ec894a8fed1af64a0bf399"}, + {file = "pydantic_core-2.18.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfeecd1ac6cc1fb2692c3d5110781c965aabd4ec5d32799773ca7b1456ac636b"}, + {file = "pydantic_core-2.18.2-cp39-none-win32.whl", hash = "sha256:0d69b4c2f6bb3e130dba60d34c0845ba31b69babdd3f78f7c0c8fae5021a253e"}, + {file = "pydantic_core-2.18.2-cp39-none-win_amd64.whl", hash = "sha256:d9319e499827271b09b4e411905b24a426b8fb69464dfa1696258f53a3334641"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a1874c6dd4113308bd0eb568418e6114b252afe44319ead2b4081e9b9521fe75"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ccdd111c03bfd3666bd2472b674c6899550e09e9f298954cfc896ab92b5b0e6d"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e18609ceaa6eed63753037fc06ebb16041d17d28199ae5aba0052c51449650a9"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e5c584d357c4e2baf0ff7baf44f4994be121e16a2c88918a5817331fc7599d7"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43f0f463cf89ace478de71a318b1b4f05ebc456a9b9300d027b4b57c1a2064fb"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e1b395e58b10b73b07b7cf740d728dd4ff9365ac46c18751bf8b3d8cca8f625a"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0098300eebb1c837271d3d1a2cd2911e7c11b396eac9661655ee524a7f10587b"}, + {file = "pydantic_core-2.18.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:36789b70d613fbac0a25bb07ab3d9dba4d2e38af609c020cf4d888d165ee0bf3"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3f9a801e7c8f1ef8718da265bba008fa121243dfe37c1cea17840b0944dfd72c"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:3a6515ebc6e69d85502b4951d89131ca4e036078ea35533bb76327f8424531ce"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20aca1e2298c56ececfd8ed159ae4dde2df0781988c97ef77d5c16ff4bd5b400"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:223ee893d77a310a0391dca6df00f70bbc2f36a71a895cecd9a0e762dc37b349"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2334ce8c673ee93a1d6a65bd90327588387ba073c17e61bf19b4fd97d688d63c"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cbca948f2d14b09d20268cda7b0367723d79063f26c4ffc523af9042cad95592"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b3ef08e20ec49e02d5c6717a91bb5af9b20f1805583cb0adfe9ba2c6b505b5ae"}, + {file = "pydantic_core-2.18.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6fdc8627910eed0c01aed6a390a252fe3ea6d472ee70fdde56273f198938374"}, + {file = "pydantic_core-2.18.2.tar.gz", hash = "sha256:2e29d20810dfc3043ee13ac7d9e25105799817683348823f305ab3f349b9386e"}, ] [package.dependencies] @@ -1447,13 +1459,13 @@ test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)" [[package]] name = "virtualenv" -version = "20.25.3" +version = "20.26.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.25.3-py3-none-any.whl", hash = "sha256:8aac4332f2ea6ef519c648d0bc48a5b1d324994753519919bddbb1aff25a104e"}, - {file = "virtualenv-20.25.3.tar.gz", hash = "sha256:7bb554bbdfeaacc3349fa614ea5bff6ac300fc7c335e9facf3a3bcfc703f45be"}, + {file = "virtualenv-20.26.1-py3-none-any.whl", hash = "sha256:7aa9982a728ae5892558bff6a2839c00b9ed145523ece2274fad6f414690ae75"}, + {file = "virtualenv-20.26.1.tar.gz", hash = "sha256:604bfdceaeece392802e6ae48e69cec49168b9c5f4a44e483963f9242eb0e78b"}, ] [package.dependencies] @@ -1636,4 +1648,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.8" -content-hash = "e17c06bc0dfb64dc90ed83f7fd1bee2f74204b8bcc5539a6e3a0cac6b8f90b3c" +content-hash = "1e064562d8c36bdbb4b2aa22c9c80e10e8f447e647e172363141e0cec4bc0de6" diff --git a/pyproject.toml b/pyproject.toml index 024b4b1..913ac4e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,9 @@ pre-commit = "^2.21.0" coverage = "^6.1.2" coverage-badge = "^1.1.0" +[tool.poetry.dependencies.cushy-storage] +version = "1.3.8" +python = "<4.0,>=3.8" [tool.ruff] # https://beta.ruff.rs/docs/settings/ From 674734325aaa9a553adc4027b58ae70750b99228 Mon Sep 17 00:00:00 2001 From: longsihua2003 <1161443436@qq.com> Date: Thu, 9 May 2024 00:21:02 +0800 Subject: [PATCH 5/6] Add web apis, refine code comments, add readme information --- README.md | 62 ++++++++++++++++++++- ecjtu/client.py | 8 --- ecjtu/ecjtu_api/api.py | 2 +- ecjtu/ecjtu_api/auth.py | 19 +++---- ecjtu/ecjtu_api/schema.py | 2 +- ecjtu/utils/cookie.py | 8 +-- examples/ecjtu-api.md | 71 ++++++++++++++++++++---- examples/ecjtu.api.ipynb | 114 -------------------------------------- 8 files changed, 135 insertions(+), 151 deletions(-) delete mode 100644 examples/ecjtu.api.ipynb diff --git a/README.md b/README.md index c3f53e8..644f58e 100644 --- a/README.md +++ b/README.md @@ -261,6 +261,67 @@ async def main(): asyncio.run(main()) ``` +## 提供 web 服务器,提供 API 服务 + +### 启动方法 +1. 通过python代码启动 + ```py + from ecjtu.ecjtu_api.api import start_api_server + + def main(): + start_api_server(port=8080) + + if __name__ == "__main__": + main() + ``` +2. 通过命令行启动 + ```shell + python ecjtu/server.py --port 8080 + ``` + +### 使用方法 +1. 启动之后,命令行会显示如下内容 + ```shell + INFO: Started server process [2545] + INFO: Waiting for application startup. + INFO: Application startup complete. + INFO: Uvicorn running on http://127.0.0.1:8080 (Press CTRL+C to quit) + ``` +2. 此时通过浏览器访问 http://127.0.0.1:8080 可以看到api在线调试文档 + +### 本项目提供的api接口 + +详细信息可以参考源代码当中examples/ecjtu-api.md当中 + +1. 登录 + * post /login + 通过学号和密码进行登录,获取access_token和refresh_token,access_token用于之后的所有请求,refresh_token用于刷新access_token + + * post /refresh_token + 当access_token过期时,可以使用refresh_token刷新access_token。 +2. gpa + + * get /gpa + 获取当前gpa情况 +3. 课表 + * get /schedule + 获取今日课表 + * get /schedule/{date} + 获取指定日期课表 date格式为2024-05-01 + * get /schedule/week + 获取本周课表 +4. 成绩 + * get /score + 获取目前成绩 + * /score/{semester} + 获取指定学期成绩 semester格式为2023.1 +5. 选课情况 + * get /elective_courses + 获取当前选课信息 + * get /elective_courses/{semester} + 获取指定学期选课信息 semester格式为2023.1 + + ## 🧰 本地开发 欢迎贡献代码与二次开发,你可以通过以下方式安装依赖,推荐使用 Conda 作为环境管理工具,首先创建一个新的环境并激活: @@ -281,7 +342,6 @@ poetry install 下面列举了一些未来可能添加的功能,欢迎贡献代码,提出建议。 -- [ ] 添加 web 服务器,提供 API 服务 - [ ] 提供 vercel 一键部署 - [ ] 提供 docker 快速服务部署 - [ ] 增加考试查询 diff --git a/ecjtu/client.py b/ecjtu/client.py index 71fff40..f380b66 100644 --- a/ecjtu/client.py +++ b/ecjtu/client.py @@ -224,10 +224,6 @@ def login(self) -> None: logger.info("Login successful") - def start_api_server(self, port: int = 8000): - # TODO: Start a FastAPI server - pass - class AsyncECJTU(BaseClient[httpx.AsyncClient], httpx.AsyncClient): def __init__(self, stud_id: str, password: str, **kwargs) -> None: @@ -384,7 +380,3 @@ async def login(self) -> None: ) logger.info("Login successful") - - async def start_api_server(self): - # TODO: Start a FastAPI server - pass diff --git a/ecjtu/ecjtu_api/api.py b/ecjtu/ecjtu_api/api.py index 6e9b5cf..f91ba9a 100644 --- a/ecjtu/ecjtu_api/api.py +++ b/ecjtu/ecjtu_api/api.py @@ -23,7 +23,7 @@ def push_docs(): "/login", tags=["登录"], summary="登录", - description="登录并获取token,以下所有接口都需要token才可以使用", + description="登录获取access_token和refresh_token,access_token用于之后的所有请求,refresh_token用于刷新access_token", ) def login(user: schema.UserLoginSchema): try: diff --git a/ecjtu/ecjtu_api/auth.py b/ecjtu/ecjtu_api/auth.py index 193e5d3..db3f1c3 100644 --- a/ecjtu/ecjtu_api/auth.py +++ b/ecjtu/ecjtu_api/auth.py @@ -1,6 +1,7 @@ import base64 import datetime +import httpx from cushy_storage import CushyOrmCache from ecjtu.client import ECJTU @@ -10,18 +11,18 @@ from . import schema -def encode_data(data): +def encode_data(data: str) -> str: # 将数据编码为base64字符串 return base64.b64encode(data.encode()).decode() -def decode_data(encoded_data): +def decode_data(encoded_data: str) -> str: # 解码base64字符串 return base64.b64decode(encoded_data.encode()).decode() # 双token加密 -def create_tokens(stud_id, pwd): +def create_tokens(stud_id: str, pwd: str) -> tuple[str, str]: access_time = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta( minutes=60 ) @@ -56,7 +57,7 @@ def create_tokens(stud_id, pwd): # 刷新access_token -def refresh_access_token(refresh_token): +def refresh_access_token(refresh_token: str) -> str: data = decode_data(refresh_token) print(data) stud_id = data.split(":")[0] @@ -78,16 +79,15 @@ def refresh_access_token(refresh_token): # 验证和读取access_token的stud_id -def get_stud_id(access_token): +def get_stud_id(access_token: str) -> str: try: data = decode_data(access_token) stud_file = CushyOrmCache(get_path()) stud = stud_file.query("FileAuth").filter(token=access_token).first() if not stud: raise Exception("无效令牌") - except Exception as e: - # raise Exception("无效令牌") - raise e + except Exception: + raise Exception("无效令牌") stud_id = data.split(":")[0] token_time = data.split(":")[2] @@ -98,11 +98,10 @@ def get_stud_id(access_token): if current_time > expire_time: raise Exception("令牌已过期,请刷新") return stud_id - # return data.split(':')[0] # 读取cookie -def get_cookie(stud_id): +def get_cookie(stud_id: str) -> httpx.Cookies: stud_file = CushyOrmCache(get_path()) stud = stud_file.query("FileAuth").filter(stud_id=stud_id).first() cookies = list_tocookie(stud.cookie) diff --git a/ecjtu/ecjtu_api/schema.py b/ecjtu/ecjtu_api/schema.py index 4063b05..82fc442 100644 --- a/ecjtu/ecjtu_api/schema.py +++ b/ecjtu/ecjtu_api/schema.py @@ -15,7 +15,7 @@ def __init__( stud_id: Optional[str] = None, token: str = None, cookie: Optional[List[Dict]] = None, - ): + ) -> None: super().__init__() self.stud_id = stud_id self.token = token diff --git a/ecjtu/utils/cookie.py b/ecjtu/utils/cookie.py index 813d095..893fcbf 100644 --- a/ecjtu/utils/cookie.py +++ b/ecjtu/utils/cookie.py @@ -4,19 +4,19 @@ from httpx._types import CookieTypes -def cookies_tolist(cookies: Optional[CookieTypes]): +def cookies_tolist(cookies: Optional[CookieTypes]) -> list: cookies_list = [] for cookie in cookies.jar: - dict = { + cookie_dict = { "name": cookie.name, "value": cookie.value, "domain": cookie.domain, } - cookies_list.append(dict) + cookies_list.append(cookie_dict) return cookies_list -def list_tocookie(cookies_list: list): +def list_tocookie(cookies_list: list) -> httpx.Cookies: cookies = httpx.Cookies() for cookie in cookies_list: cookies.set(**cookie) diff --git a/examples/ecjtu-api.md b/examples/ecjtu-api.md index 046703c..b50e312 100644 --- a/examples/ecjtu-api.md +++ b/examples/ecjtu-api.md @@ -33,7 +33,7 @@ Base URLs: POST /login -登录并获取token,以下所有接口都需要token才可以使用 +登录获取access_token和refresh_token,access_token用于之后的所有请求,refresh_token用于刷新access_token > Body 请求参数 @@ -46,9 +46,40 @@ POST /login ### 请求参数 +|名称|位置|类型|必选|说明| +|---|---|---|---|---| +|body|body|object| 否 |none| + +> 返回示例 + +> 200 Response + +```json +"string" +``` + +### 返回结果 + +|状态码|状态码含义|说明|数据模型| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|Inline| + +### 返回数据结构 + + + +## POST 刷新access_token + +POST /refresh_token + +刷新access_token + +### 请求参数 + |名称|位置|类型|必选|中文名|说明| |---|---|---|---|---|---| -|body|body|[UserLoginSchema](#schemauserloginschema)| 否 | UserLoginSchema|none| +|data|query|string| 否 | Data|none| > 返回示例 @@ -63,7 +94,9 @@ POST /login |状态码|状态码含义|说明|数据模型| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| -|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|Inline| + +### 返回数据结构 # GPA @@ -94,7 +127,9 @@ GET /gpa |状态码|状态码含义|说明|数据模型| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| -|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|Inline| + +### 返回数据结构 # 课表 @@ -125,7 +160,9 @@ GET /schedule |状态码|状态码含义|说明|数据模型| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| -|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|Inline| + +### 返回数据结构 @@ -155,13 +192,15 @@ GET /schedule/{date} |状态码|状态码含义|说明|数据模型| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| -|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|Inline| + +### 返回数据结构 ## GET 获取本周课表 -GET /schedule/week +GET /schedule_week 获取本周课表 @@ -184,7 +223,9 @@ GET /schedule/week |状态码|状态码含义|说明|数据模型| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| -|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|Inline| + +### 返回数据结构 # 成绩 @@ -215,7 +256,9 @@ GET /score |状态码|状态码含义|说明|数据模型| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| -|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|Inline| + +### 返回数据结构 @@ -245,7 +288,9 @@ GET /score/{semester} |状态码|状态码含义|说明|数据模型| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| -|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|Inline| + +### 返回数据结构 # 选课情况 @@ -276,7 +321,9 @@ GET /elective_courses |状态码|状态码含义|说明|数据模型| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| -|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|Inline| + +### 返回数据结构 @@ -306,4 +353,4 @@ GET /elective_courses/{semester} |状态码|状态码含义|说明|数据模型| |---|---|---|---| |200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Successful Response|string| -|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|[HTTPValidationError](#schemahttpvalidationerror)| \ No newline at end of file +|422|[Unprocessable Entity](https://tools.ietf.org/html/rfc2518#section-10.3)|Validation Error|Inline| diff --git a/examples/ecjtu.api.ipynb b/examples/ecjtu.api.ipynb deleted file mode 100644 index ffd520c..0000000 --- a/examples/ecjtu.api.ipynb +++ /dev/null @@ -1,114 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# ecjtu-api\n", - "我们提供了ecjtu的一套服务。\n", - "通过fastapi的形式实现" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 相关接口描述\n", - "\n", - "详细信息可以参考ecjtu-api.md。\n", - "\n", - "1. 登录\n", - " \n", - " post /login \n", - " 通过学号和密码进行登录,获取登录token,其他接口的请求均要token认证\n", - "2. gpa\n", - " \n", - " get /gpa\n", - " 获取当前gpa情况\n", - "3. 课表\n", - " * get /schedule\n", - " 获取今日课表\n", - " * get /schedule/{date}\n", - " 获取指定日期课表 date格式为2024-05-01\n", - " * get /schedule/week\n", - " 获取本周课表\n", - "4. 成绩\n", - " * get /score\n", - " 获取目前成绩\n", - " * /score/{semester}\n", - " 获取指定学期成绩 semester格式为2023.1\n", - "5. 选课情况\n", - " * get /elective_courses\n", - " 获取当前选课信息\n", - " * get /elective_courses/{semester}\n", - " 获取指定学期选课信息 semester格式为2023.1" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 服务的启动\n", - "我们提供了两种服务的启动方式\n", - "\n", - "### 通过python文件启动" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from ecjtu.ecjtu_api.api import start_api_server\n", - "\n", - "def main():\n", - " start_api_server(port=8080)\n", - "\n", - "if __name__ == '__main__':\n", - " main()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 通过命令行启动" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "shellscript" - } - }, - "outputs": [], - "source": [ - "python ecjtu/server.py --port 8080 " - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "ecjtut", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.14" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} From 0a43994f048ad4c1db2d4626cad6414f209740ad Mon Sep 17 00:00:00 2001 From: longsihua2003 <1161443436@qq.com> Date: Fri, 10 May 2024 21:29:21 +0800 Subject: [PATCH 6/6] Add comments --- ecjtu/ecjtu_api/api.py | 165 +++++++++++++++++++++++------- ecjtu/ecjtu_api/auth.py | 65 ++++++++++-- ecjtu/ecjtu_api/middle.py | 8 +- ecjtu/ecjtu_api/respose_result.py | 22 ++-- ecjtu/ecjtu_api/schema.py | 4 +- ecjtu/server.py | 8 +- examples/basic-usage.ipynb | 36 +++---- 7 files changed, 219 insertions(+), 89 deletions(-) diff --git a/ecjtu/ecjtu_api/api.py b/ecjtu/ecjtu_api/api.py index f91ba9a..52d4194 100644 --- a/ecjtu/ecjtu_api/api.py +++ b/ecjtu/ecjtu_api/api.py @@ -3,10 +3,10 @@ from fastapi import FastAPI, Header from fastapi.responses import RedirectResponse +from starlette.responses import JSONResponse from ecjtu.client import ECJTU - -from . import auth, middle, respose_result, schema +from ecjtu.ecjtu_api import auth, middle, respose_result, schema app = FastAPI(title="ECJTU API", description="API for ECJTU") @@ -15,8 +15,8 @@ @app.get("/", include_in_schema=False) def push_docs(): - respose = RedirectResponse(url="/docs") - return respose + response = RedirectResponse(url="/docs") + return response @app.post( @@ -25,7 +25,15 @@ def push_docs(): summary="登录", description="登录获取access_token和refresh_token,access_token用于之后的所有请求,refresh_token用于刷新access_token", ) -def login(user: schema.UserLoginSchema): +def login(user: schema.UserLoginSchema) -> JSONResponse: + """login + + Args: + user (schema.UserLoginSchema): user login info + + Returns: + JSONResponse: response + """ try: access_token, refresh_token = auth.create_tokens(user.stud_id, user.password) except Exception as e: @@ -43,6 +51,15 @@ def login(user: schema.UserLoginSchema): description="刷新access_token", ) def refresh_token(data: str = None): + """refresh access token + + Args: + data(str): refresh token + + Returns: + JSONResponse: access token + + """ try: access_token = auth.refresh_access_token(data) except Exception as e: @@ -50,12 +67,33 @@ def refresh_token(data: str = None): return respose_result.ResponseResult.success({"access_token": access_token}) -# gpa接口 -@app.get("/gpa", tags=["GPA"], summary="获取GPA", description="获取当学期GPA") -def gpa(token: str = Header(None)): +def create_client(token: str) -> ECJTU: + """create client + + Args: + token (str): access token + + Returns: + ECJTU: client + """ stud_id = auth.get_stud_id(token) cookie = auth.get_cookie(stud_id) client = ECJTU(cookie=cookie) + return client + + +@app.get("/gpa", tags=["GPA"], summary="获取GPA", description="获取当学期GPA") +def gpa(token: str = Header(None)): + """get gpa + + Args: + token: access token + + Returns: + JSONResponse: gpa + + """ + client = create_client(token) try: gpa = client.gpa.today() except Exception as e: @@ -63,12 +101,18 @@ def gpa(token: str = Header(None)): return respose_result.ResponseResult.success(dict(gpa)) -# 课表接口 @app.get("/schedule", tags=["课表"], summary="获取当天课表", description="获取当天课表") def schedule(token: str = Header(None)): - stud_id = auth.get_stud_id(token) - cookie = auth.get_cookie(stud_id) - client = ECJTU(cookie=cookie) + """get schedule of today + + Args: + token: access token + + Returns: + JSONResponse: schedule of today + + """ + client = create_client(token) try: schedule = client.scheduled_courses.today() except Exception as e: @@ -86,15 +130,21 @@ def schedule(token: str = Header(None)): description="获取指定日期课表,指定日期格式为yyyy-mm-dd", ) def schedule_date(token: str = Header(None), date: str = None): - # date(str): The date to filter, eg: 2023-01-01 + """get schedule of specified date + + Args: + token: access token + date: date in format yyyy-mm-dd + + Returns: + JSONResponse: schedule of specified date + + """ try: valid_date = datetime.datetime.strptime(date, "%Y-%m-%d").date() except ValueError: return respose_result.ResponseResult.param_error("日期格式错误") - - stud_id = auth.get_stud_id(token) - cookie = auth.get_cookie(stud_id) - client = ECJTU(cookie=cookie) + client = create_client(token) try: scheduled_courses = client.scheduled_courses.filter(date=valid_date) except Exception as e: @@ -109,9 +159,16 @@ def schedule_date(token: str = Header(None), date: str = None): "/schedule_week", tags=["课表"], summary="获取本周课表", description="获取本周课表" ) def schedule_week(token: str = Header(None)): - stud_id = auth.get_stud_id(token) - cookie = auth.get_cookie(stud_id) - client = ECJTU(cookie=cookie) + """get schedule of this week + + Args: + token: access token + + Returns: + JSONResponse: schedule of this week + + """ + client = create_client(token) try: schedule = client.scheduled_courses.this_week() except Exception as e: @@ -125,12 +182,18 @@ def schedule_week(token: str = Header(None)): return respose_result.ResponseResult.success(dict_list) -# 成绩接口 @app.get("/score", tags=["成绩"], summary="获取当前成绩", description="获取当学期成绩") def score(token: str = Header(None)): - stud_id = auth.get_stud_id(token) - cookie = auth.get_cookie(stud_id) - client = ECJTU(cookie=cookie) + """get score of this semester + + Args: + token: access token + + Returns: + JSONResponse: score of this semester + + """ + client = create_client(token) try: score = client.scores.today() except Exception as e: @@ -148,12 +211,19 @@ def score(token: str = Header(None)): description="获取指定学期成绩,semester格式为yyyy.1或yyyy.2", ) def score_semester(token: str = Header(None), semester: str = None): - # semester(Optional[str]): The semester to filter, eg: 2023.1, 2023.2 + """get score of specified semester + + Args: + token: access token + semester: semester in format yyyy.1 or yyyy.2 + + Returns: + JSONResponse: score of specified semester + + """ if not re.match(r"\d{4}\.[12]", semester): return respose_result.ResponseResult.param_error("学期格式错误") - stud_id = auth.get_stud_id(token) - cookie = auth.get_cookie(stud_id) - client = ECJTU(cookie=cookie) + client = create_client(token) try: scores = client.scores.filter(semester=semester) except Exception as e: @@ -164,7 +234,6 @@ def score_semester(token: str = Header(None), semester: str = None): return respose_result.ResponseResult.success(score_list) -# 选课情况接口 @app.get( "/elective_courses", tags=["选课情况"], @@ -172,9 +241,16 @@ def score_semester(token: str = Header(None), semester: str = None): description="获取当前学期选课情况", ) def elective_courses(token: str = Header(None)): - stud_id = auth.get_stud_id(token) - cookie = auth.get_cookie(stud_id) - client = ECJTU(cookie=cookie) + """get elective courses + + Args: + token: access token + + Returns: + JSONResponse: elective courses + + """ + client = create_client(token) try: elective_courses = client.elective_courses.today() except Exception as e: @@ -192,12 +268,18 @@ def elective_courses(token: str = Header(None)): description="获取指定学期选课情况,semester格式为yyyy.1或yyyy.2", ) def elective_courses_semester(token: str = Header(None), semester: str = None): - # semester(Optional[str]): The semester to filter, eg: 2023.1, 2023.2 + """get elective courses of specified semester + + Args: + token: access token + semester: semester in format yyyy.1 or yyyy.2 + + Returns: + JSONResponse: elective courses + """ if not re.match(r"\d{4}\.[12]", semester): return respose_result.ResponseResult.param_error("学期格式错误") - stud_id = auth.get_stud_id(token) - cookie = auth.get_cookie(stud_id) - client = ECJTU(cookie=cookie) + client = create_client(token) try: elective_courses = client.elective_courses.filter(semester=semester) except Exception as e: @@ -208,8 +290,15 @@ def elective_courses_semester(token: str = Header(None), semester: str = None): return respose_result.ResponseResult.success(elective_courses_list) -# 启动api服务 -def start_api_server(port=8080): +def start_api_server(port: int = 8080) -> None: + """start api server + + Args: + port (int, optional): port to run the server on. Defaults to 8080. + + Raises: + Exception: if port is already in use, an exception will be raised. + """ import uvicorn uvicorn.run(app, host="127.0.0.1", port=port) diff --git a/ecjtu/ecjtu_api/auth.py b/ecjtu/ecjtu_api/auth.py index db3f1c3..5c389e8 100644 --- a/ecjtu/ecjtu_api/auth.py +++ b/ecjtu/ecjtu_api/auth.py @@ -5,24 +5,45 @@ from cushy_storage import CushyOrmCache from ecjtu.client import ECJTU +from ecjtu.ecjtu_api import schema from ecjtu.utils.cookie import cookies_tolist, list_tocookie from ecjtu.utils.logger import get_path -from . import schema - def encode_data(data: str) -> str: - # 将数据编码为base64字符串 + """encode to string + + Args: + data (str): string to encode + + Returns: + str: encoded string + """ return base64.b64encode(data.encode()).decode() def decode_data(encoded_data: str) -> str: - # 解码base64字符串 + """decode to string + + Args: + encoded_data (str): encoded string + + Returns: + str: decoded string + """ return base64.b64decode(encoded_data.encode()).decode() -# 双token加密 def create_tokens(stud_id: str, pwd: str) -> tuple[str, str]: + """create access_token and refresh_token + + Args: + stud_id (str): student id + pwd (str): password + + Returns: + tuple[str, str]: access_token and refresh_token + """ access_time = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta( minutes=60 ) @@ -44,20 +65,30 @@ def create_tokens(stud_id: str, pwd: str) -> tuple[str, str]: stud_file = CushyOrmCache(get_path()) stud = stud_file.query("FileAuth").filter(stud_id=stud_id).first() - # 创建用户储存信息 + # if not exist, create a new one if not stud: stud = schema.FileAuth(stud_id, access_token, cookie_list) stud_file.add(stud) return access_token, refresh_token - # 更新用户信息 + # update the old one stud.cookie = cookie_list stud.token = access_token stud_file.update_obj(stud) return access_token, refresh_token -# 刷新access_token def refresh_access_token(refresh_token: str) -> str: + """refresh access_token + + Args: + refresh_token (str): refresh_token + + Returns: + str: access_token + + Raises: + Exception: if token is invalid or expired + """ data = decode_data(refresh_token) print(data) stud_id = data.split(":")[0] @@ -78,8 +109,15 @@ def refresh_access_token(refresh_token: str) -> str: return create_tokens(stud_id, pwd)[0] -# 验证和读取access_token的stud_id def get_stud_id(access_token: str) -> str: + """get stud_id from access_token + + Args: + access_token (str): access_token + + Returns: + str: stud_id + """ try: data = decode_data(access_token) stud_file = CushyOrmCache(get_path()) @@ -100,8 +138,15 @@ def get_stud_id(access_token: str) -> str: return stud_id -# 读取cookie def get_cookie(stud_id: str) -> httpx.Cookies: + """get cookie from stud_id + + Args: + stud_id (str): stud_id + + Returns: + httpx.Cookies: cookies + """ stud_file = CushyOrmCache(get_path()) stud = stud_file.query("FileAuth").filter(stud_id=stud_id).first() cookies = list_tocookie(stud.cookie) diff --git a/ecjtu/ecjtu_api/middle.py b/ecjtu/ecjtu_api/middle.py index c97e98a..b68d032 100644 --- a/ecjtu/ecjtu_api/middle.py +++ b/ecjtu/ecjtu_api/middle.py @@ -1,11 +1,11 @@ from starlette.middleware.base import BaseHTTPMiddleware -from . import auth, respose_result +from ecjtu.ecjtu_api import auth, respose_result class MyMiddleware(BaseHTTPMiddleware): async def dispatch(self, request, call_next): - # 不被拦截的路由 + # not secure routes secure_routes = [ "/refresh_token", "/login", @@ -21,10 +21,10 @@ async def dispatch(self, request, call_next): if path not in secure_routes: header = request.headers.get("token") - # 是否存在token + # if not header, return response if not header: return response - # 是否过期 + # get stud_id from access_token try: stud_id = auth.get_stud_id(header) except Exception as e: diff --git a/ecjtu/ecjtu_api/respose_result.py b/ecjtu/ecjtu_api/respose_result.py index 0bb3722..7672aa6 100644 --- a/ecjtu/ecjtu_api/respose_result.py +++ b/ecjtu/ecjtu_api/respose_result.py @@ -1,13 +1,11 @@ -from typing import Any, Dict, Union +from typing import Any from fastapi.responses import JSONResponse class ResponseResult: @staticmethod - def success( - data: Union[Dict[str, Any], Any] = None, msg: str = "success" - ) -> JSONResponse: + def success(data: Any = None, msg: str = "success") -> JSONResponse: return JSONResponse(content={"code": 200, "msg": msg, "data": data}) @staticmethod @@ -15,33 +13,25 @@ def success_no_data(msg: str = "success") -> JSONResponse: return JSONResponse(content={"code": 200, "msg": msg, "data": None}) @staticmethod - def not_found( - data: Union[Dict[str, Any], Any] = None, msg: str = "not found" - ) -> JSONResponse: + def not_found(data: str = None, msg: str = "not found") -> JSONResponse: return JSONResponse( status_code=404, content={"code": 404, "msg": msg, "data": data} ) @staticmethod - def auth_error( - data: Union[Dict[str, Any], Any] = None, msg: str = "auth error" - ) -> JSONResponse: + def auth_error(data: str = None, msg: str = "auth error") -> JSONResponse: return JSONResponse( status_code=401, content={"code": 401, "msg": msg, "data": data} ) @staticmethod - def param_error( - data: Union[Dict[str, Any], Any] = None, msg: str = "param error" - ) -> JSONResponse: + def param_error(data: str = None, msg: str = "param error") -> JSONResponse: return JSONResponse( status_code=400, content={"code": 400, "msg": msg, "data": data} ) @staticmethod - def error( - data: Union[Dict[str, Any], Any] = None, msg: str = "error" - ) -> JSONResponse: + def error(data: str = None, msg: str = "error") -> JSONResponse: return JSONResponse( status_code=500, content={"code": 500, "msg": msg, "data": data} ) diff --git a/ecjtu/ecjtu_api/schema.py b/ecjtu/ecjtu_api/schema.py index 82fc442..9d904e0 100644 --- a/ecjtu/ecjtu_api/schema.py +++ b/ecjtu/ecjtu_api/schema.py @@ -5,8 +5,8 @@ class UserLoginSchema(BaseModel): - stud_id: str = Field(..., min_length=1) - password: str = Field(..., min_length=1) + stud_id: str = Field(..., min_length=1, description="学号") + password: str = Field(..., min_length=1, description="密码") class FileAuth(BaseORMModel): diff --git a/ecjtu/server.py b/ecjtu/server.py index 8317631..1e9d680 100644 --- a/ecjtu/server.py +++ b/ecjtu/server.py @@ -1,9 +1,15 @@ import argparse -from ecjtu_api.api import start_api_server +from ecjtu.ecjtu_api.api import start_api_server def main(): + """start the api server from the command line + + Usage: + python ecjtu.server.py --port 8000 + + """ parser = argparse.ArgumentParser(description="ECJTU Command Line Interface") parser.add_argument( "--port", type=int, default=8000, help="Port to run the server on" diff --git a/examples/basic-usage.ipynb b/examples/basic-usage.ipynb index 3892378..01a6180 100644 --- a/examples/basic-usage.ipynb +++ b/examples/basic-usage.ipynb @@ -23,7 +23,6 @@ "start_time": "2024-04-19T21:22:41.539373Z" } }, - "outputs": [], "source": [ "from typing import List\n", "\n", @@ -31,7 +30,8 @@ "from ecjtu.models import ScheduledCourse, Score, GPA\n", "\n", "client = ECJTU(stud_id=\"xxx\", password=\"xxx\")" - ] + ], + "outputs": [] }, { "cell_type": "markdown", @@ -89,7 +89,6 @@ }, { "cell_type": "code", - "outputs": [], "source": [ "courses: List[ScheduledCourse] = client.scheduled_courses.today()\n", "print(courses)" @@ -98,7 +97,8 @@ "collapsed": false }, "id": "e2f416384ab1ead5", - "execution_count": null + "execution_count": null, + "outputs": [] }, { "cell_type": "markdown", @@ -112,7 +112,6 @@ }, { "cell_type": "code", - "outputs": [], "source": [ "courses: List[List[ScheduledCourse]] = client.scheduled_courses.this_week()\n", "\n", @@ -125,7 +124,8 @@ "collapsed": false }, "id": "3f83ebf27ada48a0", - "execution_count": null + "execution_count": null, + "outputs": [] }, { "cell_type": "markdown", @@ -139,7 +139,6 @@ }, { "cell_type": "code", - "outputs": [], "source": [ "courses: List[ScheduledCourse] = client.scheduled_courses.filter(date=\"2023-04-15\")\n", "\n", @@ -149,7 +148,8 @@ "collapsed": false }, "id": "7dabdb8809a45c1c", - "execution_count": null + "execution_count": null, + "outputs": [] }, { "cell_type": "markdown", @@ -167,7 +167,6 @@ }, { "cell_type": "code", - "outputs": [], "source": [ "scores: List[Score] = client.scores.today()\n", "print(scores)" @@ -176,7 +175,8 @@ "collapsed": false }, "id": "a1faace016c0a7bd", - "execution_count": null + "execution_count": null, + "outputs": [] }, { "cell_type": "markdown", @@ -190,7 +190,6 @@ }, { "cell_type": "code", - "outputs": [], "source": [ "scores: List[Score] = client.scores.filter(semester=\"2022.1\")\n", "\n", @@ -200,7 +199,8 @@ "collapsed": false }, "id": "a0cf75a1b43d34e6", - "execution_count": null + "execution_count": null, + "outputs": [] }, { "cell_type": "markdown", @@ -224,7 +224,6 @@ }, { "cell_type": "code", - "outputs": [], "source": [ "gpa: GPA = client.gpa.today()\n", "\n", @@ -234,7 +233,8 @@ "collapsed": false }, "id": "2cf50ef9ef5450fb", - "execution_count": null + "execution_count": null, + "outputs": [] }, { "cell_type": "markdown", @@ -248,7 +248,6 @@ }, { "cell_type": "code", - "outputs": [], "source": [ "courses = client.elective_courses.today()\n", "\n", @@ -259,7 +258,8 @@ "collapsed": false }, "id": "b95c850446242749", - "execution_count": null + "execution_count": null, + "outputs": [] }, { "cell_type": "markdown", @@ -273,7 +273,6 @@ }, { "cell_type": "code", - "outputs": [], "source": [ "courses = client.elective_courses.filter(semester=\"2022.1\")\n", "\n", @@ -284,7 +283,8 @@ "collapsed": false }, "id": "c81e6ab4e350f79e", - "execution_count": null + "execution_count": null, + "outputs": [] }, { "cell_type": "markdown",