Added a time ordering and explained what the Apache-2.0 license means.

This commit is contained in:
NianBroken
2024-01-31 15:22:37 +08:00 Unverified
parent d524fbfbd3
commit 7af37f4ce3
6 changed files with 120 additions and 43 deletions
+5 -4
View File
@@ -20,13 +20,14 @@
1. 主要功能 1. 主要功能
1. 每隔 30 分钟自动检测成绩是否更新 若更新则向微信推送通知 1. 每隔 30 分钟自动检测成绩是否更新,若有更新,将通过微信推送及时通知用户。
2. 相较于教务系统增加了哪些功能? 2. 相较于教务系统增加了哪些功能?
1. 显示成绩提交时间成绩何时被录入教务系统 1. 显示成绩提交时间,即成绩何时被录入教务系统
2. 显示成绩提交人姓名成绩谁录入进教务系统 2. 显示成绩提交人姓名,即成绩谁录入进教务系统
3. 成绩信息按时间排序(最新的成绩在最上方 3. 成绩信息按时间降序排序,确保最新的成绩始终在最上方,提升用户查阅效率。
4. 计算 GPA
## 使用方法 ## 使用方法
-1
View File
@@ -1 +0,0 @@
false
-1
View File
@@ -1 +0,0 @@
7dc2891c3afbd76dca4f11295afc5065
+89 -35
View File
@@ -1,22 +1,28 @@
# 导入必要的库
import base64 import base64
import hashlib import hashlib
import os import os
import sys import sys
import shutil
import json
from pprint import pprint from pprint import pprint
from zfn_api import Client from zfn_api import Client
from pushplus import send_message from pushplus import send_message
# 从环境变量取教务系统的URL、用户名、密码和TOKEN # 从环境变量中提取教务系统的URL、用户名、密码和TOKEN
url = os.environ.get("URL") url = os.environ.get("URL")
username = os.environ.get("USERNAME") username = os.environ.get("USERNAME")
password = os.environ.get("PASSWORD") password = os.environ.get("PASSWORD")
token = os.environ.get("TOKEN") token = os.environ.get("TOKEN")
# 定义一个md5加密的封装函数 # 定义一个封装MD5加密的函数
def md5_encrypt(string): def md5_encrypt(string):
# 将字符串编码为字节
byte_string = string.encode() byte_string = string.encode()
# 创建MD5对象
md5_object = hashlib.md5(byte_string) md5_object = hashlib.md5(byte_string)
# 返回MD5哈希值的十六进制表示
return md5_object.hexdigest() return md5_object.hexdigest()
@@ -29,7 +35,7 @@ detail_category_type = []
timeout = 5 timeout = 5
# 创建教务系统客户端对象 # 创建教务系统客户端对象
stu = Client( student_client = Client(
cookies=cookies, cookies=cookies,
base_url=base_url, base_url=base_url,
raspisanie=raspisanie, raspisanie=raspisanie,
@@ -40,15 +46,18 @@ stu = Client(
# 如果cookies为空字典,则进行登录 # 如果cookies为空字典,则进行登录
if not cookies: if not cookies:
login_result = stu.login(username, password) login_result = student_client.login(username, password)
if login_result["code"] == 1001: if login_result["code"] == 1001:
# 如果需要验证码,获取验证码并进行登录 # 如果需要验证码,获取验证码并进行登录
verify_data = login_result["data"] verify_data = login_result["data"]
# 将验证码图片写入文件
with open(os.path.abspath("kaptcha.png"), "wb") as pic: with open(os.path.abspath("kaptcha.png"), "wb") as pic:
pic.write(base64.b64decode(verify_data.pop("kaptcha_pic"))) pic.write(base64.b64decode(verify_data.pop("kaptcha_pic")))
# 输入验证码
verify_data["kaptcha"] = input("输入验证码:") 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: if login_result["code"] != 1000:
pprint(login_result) pprint(login_result)
@@ -60,24 +69,37 @@ if not cookies:
sys.exit() sys.exit()
# 获取个人信息 # 获取个人信息
info = stu.get_info()["data"] info = student_client.get_info()["data"]
# 整合个人信息 # 整合个人信息
integrate_info = ( integrated_info = (
f"个人信息:\n" f"学号:{info['sid']}\n" f"班级:{info['class_name']}\n" f"姓名:{info['name']}" 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 # 定义info.txt文件路径
if not os.path.exists(firstrun_file_path): info_file_path = "info.txt"
with open(firstrun_file_path, "w") as firstrun_file:
firstrun_file.write("true")
# 第一次运行则运行两遍,否则运行一遍 # 初始化运行次数
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): for _ in range(run_count):
# 如果grade.txt文件不存在,创建文件 # 如果grade.txt文件不存在,创建文件
if not os.path.exists("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: with open("old_grade.txt", "w") as old_grade_file:
old_grade_file.truncate() old_grade_file.truncate()
# 将grade.txt文件中的内容写入old_grade.txt文件内。 # 将grade.txt文件中的内容写入old_grade.txt文件内。
with open("grade.txt", "r") as grade_file, open( with open("grade.txt", "r") as grade_file, open(
"old_grade.txt", "w" "old_grade.txt", "w"
) as old_grade_file: ) as old_grade_file:
@@ -98,15 +120,26 @@ for _ in range(run_count):
grade_file.truncate() 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) 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 = "成绩信息:" integrated_grade_info = "成绩信息:"
# 遍历前8条按时间降序排序后的成绩列表 # 遍历前8条成绩信息
for i, course in enumerate(sorted_grade[:8]): for i, course in enumerate(sorted_grade[:8]):
# 整合成绩信息 # 整合成绩信息
integrated_grade_info += ( integrated_grade_info += (
@@ -115,41 +148,62 @@ for _ in range(run_count):
f"任课教师: {course['teacher']}\n" f"任课教师: {course['teacher']}\n"
f"成绩: {course['grade']}\n" f"成绩: {course['grade']}\n"
f"提交时间: {course['submission_time']}\n" f"提交时间: {course['submission_time']}\n"
f"提交人姓名: {course['name_of_submitter']}\n" f"提交人姓名: {course['name_of_submitter']}\n------"
f"------"
) )
# 加密保存成绩 # 加密保存成绩
encrypt_integrated_grade_info = md5_encrypt(integrated_grade_info) encrypted_integrated_grade_info = md5_encrypt(integrated_grade_info)
# 将加密后的成绩信息写入grade.txt文件 # 将加密后的成绩信息写入grade.txt文件
with open("grade.txt", "w") as grade_file: 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两个文件的内容进行比对
with open("grade.txt", "r") as grade_file, open("old_grade.txt", "r") as old_grade_file:
# 读取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_content = grade_file.read() grade_content = grade_file.read()
old_grade_content = old_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(f"旧成绩:{old_grade_content}")
print("------") 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: if grade_content == old_grade_content:
print("成绩未更新") print("成绩未更新")
else: else:
print("成绩已更新") print("成绩已更新")
response_text = send_message(token, "教务成绩已更新", integrate_send_info) response_text = send_message(token, "教务成绩已更新", integrated_send_info)
print(response_text)
# 更新firstrun.txt文件内容为false # 解析 JSON 数据
if open(firstrun_file_path).read() == "true": response_dict = json.loads(response_text)
with open(firstrun_file_path, "w") as firstrun_file:
# 将firstrun.txt文件内容更新为false # 删除 "data" 字段
firstrun_file.write("false") 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}")
-1
View File
@@ -1 +0,0 @@
7dc2891c3afbd76dca4f11295afc5065
+26 -1
View File
@@ -210,8 +210,24 @@ class Client:
return self._get_info() return self._get_info()
result = { result = {
"sid": info.get("xh"), "sid": info.get("xh"),
"class_name": info.get("bh_id", info.get("xjztdm")),
"name": info.get("xm"), "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} return {"code": 1000, "msg": "获取个人信息成功", "data": result}
except exceptions.Timeout: except exceptions.Timeout:
@@ -411,9 +427,18 @@ class Client:
"course_id": i.get("kch_id"), "course_id": i.get("kch_id"),
"title": i.get("kcmc"), "title": i.get("kcmc"),
"teacher": i.get("jsxm"), "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": 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"), "submission_time": i.get("tjsj"),
"name_of_submitter": i.get("tjrxm"), "name_of_submitter": i.get("tjrxm"),
"xfjd": i.get("xfjd"),
} }
for i in grade_items for i in grade_items
], ],