正方教务管理系统成绩推送

在这里插入图片描述

项目地址:https://github.com/NianBroken/ZFCheckScores

每隔 30 分钟自动检测成绩是否有更新,若有更新,将通过微信推送及时通知用户。

简介

使用本项目前:

早晨睡醒看一遍教务系统、上厕所看一遍教务系统、刷牙看一遍教务系统、洗脸看一遍教务系统、吃早餐看一遍教务系统、吃午饭看一遍教务系统、睡午觉前看一遍教务系统、午觉醒来看一遍教务系统、出门前看一遍教务系统、吃晚饭看一遍教务系统、洗澡看一遍教务系统、睡觉之前看一遍教务系统

使用本项目后:

成绩更新后自动发通知到微信 以节省您宝贵的时间

测试环境

正方教务管理系统 版本 V8.0.0

如果你的教务系统页面与下图所示的页面完全一致或几乎一致,则代表你可以使用本项目。

在这里插入图片描述

目前支持的功能

  1. 主要功能

    1. 每隔 30 分钟自动检测成绩是否有更新,若有更新,将通过微信推送及时通知用户。
  2. 相较于教务系统增加了哪些功能?

    1. 显示成绩提交时间,即成绩何时被录入教务系统。
    2. 显示成绩提交人姓名,即成绩由谁录入进教务系统。
    3. 成绩信息按时间降序排序,确保最新的成绩始终在最上方,提升用户查阅效率。
    4. 计算 GPA
    5. 计算百分制 GPA

使用方法

1. Fork ZFCheckScores 仓库

ForkCreate fork

2. 开启工作流读写权限

SettingsActionsGeneralWorkflow permissionsRead and write permissionsSave

3. 添加 Secrets

SettingsSecrets and variablesActionsSecretsRepository secretsNew repository secret

Name = Name,Secret = 例子

Name例子说明
URLhttps://www.nianbroken.top教务系统地址
USERNAME2971802058教务系统用户名
PASSWORDY3xhaCkb5PZ4教务系统密码
TOKENJ65KWMBfyDh3YPLpcvm8Pushplus 的 token

4. 开启 Actions

ActionsI understand my workflows, go ahead and enable themCheckScoresEnable workflow

5. 运行程序

ActionsCheckScoresRun workflow

若你的程序正常运行且未报错,那么在此之后,程序将会每隔 30 分钟自动运行一次

若你看不懂上述使用方法,你可以查看详细使用方法

项目地址:https://github.com/NianBroken/ZFCheckScores

程序代码

# 导入必要的库
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 = os.environ.get("URL")
username = os.environ.get("USERNAME")
password = os.environ.get("PASSWORD")
token = os.environ.get("TOKEN")


# 定义一个封装MD5加密的函数
def md5_encrypt(string):
    # 将字符串编码为字节
    byte_string = string.encode()
    # 创建MD5对象
    md5_object = hashlib.md5(byte_string)
    # 返回MD5哈希值的十六进制表示
    return md5_object.hexdigest()


# 初始化相关变量
cookies = {}
base_url = url
raspisanie = []
ignore_type = []
detail_category_type = []
timeout = 5

# 创建教务系统客户端对象
student_client = Client(
    cookies=cookies,
    base_url=base_url,
    raspisanie=raspisanie,
    ignore_type=ignore_type,
    detail_category_type=detail_category_type,
    timeout=timeout,
)

# 如果cookies为空字典,则进行登录
if not cookies:
    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 = student_client.login_with_kaptcha(**verify_data)

        if login_result["code"] != 1000:
            pprint(login_result)
            sys.exit()
        pprint(login_result)

    elif login_result["code"] != 1000:
        pprint(login_result)
        sys.exit()

# 获取个人信息
info = student_client.get_info()["data"]

# 整合个人信息
integrated_info = (
    f"个人信息:\n" f"学号:{info['sid']}\n" f"班级:{info['class_name']}\n" f"姓名:{info['name']}\n"
)

# 加密个人信息
encrypted_info = md5_encrypt(integrated_info)

# 定义info.txt文件路径
info_file_path = "info.txt"

# 初始化运行次数
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"):
        open("grade.txt", "w").close()

    # 清空old_grade.txt文件内容
    with open("old_grade.txt", "w") as old_grade_file:
        old_grade_file.truncate()

    # 将grade.txt文件中的内容写入old_grade.txt文件内。
    with open("grade.txt", "r") as grade_file, open(
        "old_grade.txt", "w"
    ) as old_grade_file:
        old_grade_file.write(grade_file.read())

    # 清空grade.txt文件内容
    with open("grade.txt", "w") as grade_file:
        grade_file.truncate()

    # 获取成绩信息
    grade = student_client.get_grade("")["data"]["courses"]

    # 按照提交时间降序排序
    sorted_grade = sorted(grade, key=lambda x: x["submission_time"], reverse=True)

    # 学分总和
    total_credit = sum(float(course["credit"]) for course in grade)

    # 学分绩点总和
    total_xfjd = sum(float(course["xfjd"]) for course in grade)

    # (百分制成绩*学分)的总和
    sum_of_percentage_grades_multiplied_by_credits = sum(
        float(course["percentage_grades"]) * float(course["credit"]) for course in grade
    )

    # GPA计算 (学分*绩点)的总和/学分总和
    gpa = "{:.2f}".format(total_xfjd / total_credit)

    # 百分制GPA计算 (百分制成绩*学分)的总和/学分总和
    percentage_gpa = "{:.2f}".format(
        sum_of_percentage_grades_multiplied_by_credits / total_credit
    )

    # 整合个人信息
    integrated_info += f"当前GPA:{gpa}\n" f"当前百分制GPA:{percentage_gpa}\n" f"------"

    # 初始化输出成绩信息字符串
    integrated_grade_info = "成绩信息:"

    # 遍历前8条成绩信息
    for i, course in enumerate(sorted_grade[:8]):
        # 整合成绩信息
        integrated_grade_info += (
            f"\n课程ID: {course['course_id']}\n"
            f"课程名称: {course['title']}\n"
            f"任课教师: {course['teacher']}\n"
            f"成绩: {course['grade']}\n"
            f"提交时间: {course['submission_time']}\n"
            f"提交人姓名: {course['name_of_submitter']}\n------"
        )

    # 加密保存成绩
    encrypted_integrated_grade_info = md5_encrypt(integrated_grade_info)

    # 将加密后的成绩信息写入grade.txt文件
    with open("grade.txt", "w") as grade_file:
        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_content = grade_file.read()
    old_grade_content = old_grade_file.read()

# 输出成绩信息
print(f"新成绩:{encrypted_integrated_grade_info}")
print(f"旧成绩:{old_grade_content}")
print("------")

# 整合所有信息
# 注意此处integrated_send_info保存的是未加密的信息,仅用于信息推送
# 若是在 GithubActions 等平台运行,请不要使用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, "教务成绩已更新", integrated_send_info)

    # 解析 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)

项目地址:https://github.com/NianBroken/ZFCheckScores

  • 10
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值