From 7af37f4ce32c1e2e81a5022a58a71830a1009203 Mon Sep 17 00:00:00 2001 From: NianBroken Date: Wed, 31 Jan 2024 15:22:37 +0800 Subject: [PATCH] Added a time ordering and explained what the Apache-2.0 license means. --- README.md | 9 ++-- firstrun.txt | 1 - grade.txt | 1 - main.py | 124 ++++++++++++++++++++++++++++++++++++-------------- old_grade.txt | 1 - zfn_api.py | 27 ++++++++++- 6 files changed, 120 insertions(+), 43 deletions(-) delete mode 100644 firstrun.txt delete mode 100644 grade.txt delete mode 100644 old_grade.txt diff --git a/README.md b/README.md index 7f4e553..94bfa19 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,14 @@ 1. 主要功能 - 1. 每隔 30 分钟自动检测成绩是否更新 若更新则向微信推送通知 + 1. 每隔 30 分钟自动检测成绩是否有更新,若有更新,将通过微信推送及时通知用户。 2. 相较于教务系统增加了哪些功能? - 1. 显示成绩提交时间(成绩在何时被录入进教务系统) - 2. 显示成绩提交人姓名(成绩被谁录入进教务系统) - 3. 成绩信息按时间排序(最新的成绩在最上方) + 1. 显示成绩提交时间,即成绩何时被录入教务系统。 + 2. 显示成绩提交人姓名,即成绩由谁录入进教务系统。 + 3. 成绩信息按时间降序排序,确保最新的成绩始终在最上方,提升用户查阅效率。 + 4. 计算 GPA ## 使用方法 diff --git a/firstrun.txt b/firstrun.txt deleted file mode 100644 index 02e4a84..0000000 --- a/firstrun.txt +++ /dev/null @@ -1 +0,0 @@ -false \ No newline at end of file diff --git a/grade.txt b/grade.txt deleted file mode 100644 index 2855a0f..0000000 --- a/grade.txt +++ /dev/null @@ -1 +0,0 @@ -7dc2891c3afbd76dca4f11295afc5065 \ No newline at end of file diff --git a/main.py b/main.py index 8dcde2c..9e4e647 100644 --- a/main.py +++ b/main.py @@ -1,22 +1,28 @@ +# 导入必要的库 import base64 import hashlib import os import sys +import shutil +import json from pprint import pprint from zfn_api import Client from pushplus import send_message -# 从环境变量获取教务系统的URL、用户名、密码和TOKEN +# 从环境变量中提取教务系统的URL、用户名、密码和TOKEN url = os.environ.get("URL") username = os.environ.get("USERNAME") password = os.environ.get("PASSWORD") token = os.environ.get("TOKEN") -# 定义一个md5加密的封装函数 +# 定义一个封装MD5加密的函数 def md5_encrypt(string): + # 将字符串编码为字节 byte_string = string.encode() + # 创建MD5对象 md5_object = hashlib.md5(byte_string) + # 返回MD5哈希值的十六进制表示 return md5_object.hexdigest() @@ -29,7 +35,7 @@ detail_category_type = [] timeout = 5 # 创建教务系统客户端对象 -stu = Client( +student_client = Client( cookies=cookies, base_url=base_url, raspisanie=raspisanie, @@ -40,15 +46,18 @@ stu = Client( # 如果cookies为空字典,则进行登录 if not cookies: - login_result = stu.login(username, password) + login_result = student_client.login(username, password) if login_result["code"] == 1001: # 如果需要验证码,获取验证码并进行登录 verify_data = login_result["data"] + # 将验证码图片写入文件 with open(os.path.abspath("kaptcha.png"), "wb") as pic: pic.write(base64.b64decode(verify_data.pop("kaptcha_pic"))) + # 输入验证码 verify_data["kaptcha"] = input("输入验证码:") - login_result = stu.login_with_kaptcha(**verify_data) + # 使用验证码进行登录 + login_result = student_client.login_with_kaptcha(**verify_data) if login_result["code"] != 1000: pprint(login_result) @@ -60,24 +69,37 @@ if not cookies: sys.exit() # 获取个人信息 -info = stu.get_info()["data"] +info = student_client.get_info()["data"] # 整合个人信息 -integrate_info = ( - f"个人信息:\n" f"学号:{info['sid']}\n" f"班级:{info['class_name']}\n" f"姓名:{info['name']}" +integrated_info = ( + f"个人信息:\n" f"学号:{info['sid']}\n" f"班级:{info['class_name']}\n" f"姓名:{info['name']}\n" ) -# 定义firstrun.txt文件变量 -firstrun_file_path = "firstrun.txt" +# 加密个人信息 +encrypted_info = md5_encrypt(integrated_info) -# 如果firstrun.txt文件不存在,创建并写入true -if not os.path.exists(firstrun_file_path): - with open(firstrun_file_path, "w") as firstrun_file: - firstrun_file.write("true") +# 定义info.txt文件路径 +info_file_path = "info.txt" -# 第一次运行则运行两遍,否则运行一遍 -run_count = 2 if open(firstrun_file_path).read() == "true" else 1 +# 初始化运行次数 +run_count = 2 +# 判断info.txt文件是否存在 +if not os.path.exists(info_file_path): + # 如果文件不存在,创建并写入encrypted_info的内容 + with open(info_file_path, "w") as info_file: + info_file.write(encrypted_info) +else: + # 如果文件存在,读取文件内容并比较 + with open(info_file_path, "r") as info_file: + info_file_content = info_file.read() + # 如果info.txt文件中保存的个人信息与获取到的个人信息不一致,则代表是第一次运行程序 + if info_file_content == encrypted_info: + # 非二次运行程序 + run_count = 1 + +# 第一次运行程序则运行两遍,否则运行一遍 for _ in range(run_count): # 如果grade.txt文件不存在,创建文件 if not os.path.exists("grade.txt"): @@ -87,7 +109,7 @@ for _ in range(run_count): with open("old_grade.txt", "w") as old_grade_file: old_grade_file.truncate() - # 将grade.txt文件中的内容写入到old_grade.txt文件内。 + # 将grade.txt文件中的内容写入old_grade.txt文件内。 with open("grade.txt", "r") as grade_file, open( "old_grade.txt", "w" ) as old_grade_file: @@ -98,15 +120,26 @@ for _ in range(run_count): grade_file.truncate() # 获取成绩信息 - grade = stu.get_grade("")["data"]["courses"] + grade = student_client.get_grade("")["data"]["courses"] # 按照提交时间降序排序 sorted_grade = sorted(grade, key=lambda x: x["submission_time"], reverse=True) + # 学分绩点总和 + total_xfjd = sum(float(course["xfjd"]) for course in grade) + + # 学分总和 + total_credit = sum(float(course["credit"]) for course in grade) + + # (学分*绩点)的总和/学分总和 + gpa = "{:.2f}".format(total_xfjd / total_credit) + + integrated_info += f"当前GPA:{gpa}\n------" + # 初始化输出成绩信息字符串 integrated_grade_info = "成绩信息:" - # 遍历前8条按时间降序排序后的成绩列表 + # 遍历前8条成绩信息 for i, course in enumerate(sorted_grade[:8]): # 整合成绩信息 integrated_grade_info += ( @@ -115,41 +148,62 @@ for _ in range(run_count): f"任课教师: {course['teacher']}\n" f"成绩: {course['grade']}\n" f"提交时间: {course['submission_time']}\n" - f"提交人姓名: {course['name_of_submitter']}\n" - f"------" + f"提交人姓名: {course['name_of_submitter']}\n------" ) # 加密保存成绩 - encrypt_integrated_grade_info = md5_encrypt(integrated_grade_info) + encrypted_integrated_grade_info = md5_encrypt(integrated_grade_info) # 将加密后的成绩信息写入grade.txt文件 with open("grade.txt", "w") as grade_file: - grade_file.write(encrypt_integrated_grade_info) + grade_file.write(encrypted_integrated_grade_info) -# 对grade.txt和old_grade.txt两个文件的内容进行比对 +# 读取grade.txt和old_grade.txt文件的内容 with open("grade.txt", "r") as grade_file, open("old_grade.txt", "r") as old_grade_file: - # 读取grade.txt和old_grade.txt文件的内容 grade_content = grade_file.read() old_grade_content = old_grade_file.read() # 输出成绩信息 -print(f"新成绩:{encrypt_integrated_grade_info}") +print(f"新成绩:{encrypted_integrated_grade_info}") print(f"旧成绩:{old_grade_content}") print("------") # 整合所有信息 -integrate_send_info = f"{integrate_info}\n------\n{integrated_grade_info}" +# 注意此处integrated_send_info保存的是未加密的信息,仅用于信息推送 +# 若是在 Github Actions 等平台运行,请不要使用print(integrated_send_info) +integrated_send_info = f"{integrated_info}\n{integrated_grade_info}" -# 输出成绩是否更新 +# 对grade.txt和old_grade.txt两个文件的内容进行比对,输出成绩是否更新 if grade_content == old_grade_content: print("成绩未更新") else: print("成绩已更新") - response_text = send_message(token, "教务成绩已更新", integrate_send_info) - print(response_text) + response_text = send_message(token, "教务成绩已更新", integrated_send_info) -# 更新firstrun.txt文件内容为false -if open(firstrun_file_path).read() == "true": - with open(firstrun_file_path, "w") as firstrun_file: - # 将firstrun.txt文件内容更新为false - firstrun_file.write("false") + # 解析 JSON 数据 + response_dict = json.loads(response_text) + + # 删除 "data" 字段 + if "data" in response_dict: + response_dict.pop("data") + + # 输出响应内容 + print(response_dict) + +# 更新info.txt +with open(info_file_path, "r") as info_file: + info_file_content = info_file.read() + if info_file_content != encrypted_info: + with open("info.txt", "w") as info_file: + info_file.write(encrypted_info) + +# 删除 __pycache__ 缓存目录及其内容 +current_directory = os.getcwd() +cache_folder = os.path.join(current_directory, "__pycache__") +# 检查目录是否存在 +if os.path.exists(cache_folder): + # 删除目录及其内容 + shutil.rmtree(cache_folder) + +# 其他日志 +print(f"run_count:{run_count}") diff --git a/old_grade.txt b/old_grade.txt deleted file mode 100644 index 2855a0f..0000000 --- a/old_grade.txt +++ /dev/null @@ -1 +0,0 @@ -7dc2891c3afbd76dca4f11295afc5065 \ No newline at end of file diff --git a/zfn_api.py b/zfn_api.py index a164f03..f3ad092 100644 --- a/zfn_api.py +++ b/zfn_api.py @@ -210,8 +210,24 @@ class Client: return self._get_info() result = { "sid": info.get("xh"), - "class_name": info.get("bh_id", info.get("xjztdm")), "name": info.get("xm"), + "college_name": info.get("zsjg_id", info.get("jg_id")), + "major_name": info.get("zszyh_id", info.get("zyh_id")), + "class_name": info.get("bh_id", info.get("xjztdm")), + "status": info.get("xjztdm"), + "enrollment_date": info.get("rxrq"), + "candidate_number": info.get("ksh"), + "graduation_school": info.get("byzx"), + "domicile": info.get("jg"), + "postal_code": info.get("yzbm"), + "politics_status": info.get("zzmmm"), + "nationality": info.get("mzm"), + "education": info.get("pyccdm"), + "phone_number": info.get("sjhm"), + "parents_number": info.get("gddh"), + "email": info.get("dzyx"), + "birthday": info.get("csrq"), + "id_number": info.get("zjhm"), } return {"code": 1000, "msg": "获取个人信息成功", "data": result} except exceptions.Timeout: @@ -411,9 +427,18 @@ class Client: "course_id": i.get("kch_id"), "title": i.get("kcmc"), "teacher": i.get("jsxm"), + "class_name": i.get("jxbmc"), + "credit": self.align_floats(i.get("xf")), + "category": i.get("kclbmc"), + "nature": i.get("kcxzmc"), "grade": self.parse_int(i.get("cj")), + "grade_point": self.align_floats(i.get("jd")), + "grade_nature": i.get("ksxz"), + "start_college": i.get("kkbmmc"), + "mark": i.get("kcbj"), "submission_time": i.get("tjsj"), "name_of_submitter": i.get("tjrxm"), + "xfjd": i.get("xfjd"), } for i in grade_items ],