From fdac04328117b0f8e2f18320e150a5e284370ed2 Mon Sep 17 00:00:00 2001 From: NianBroken Date: Wed, 7 Feb 2024 01:10:50 +0800 Subject: [PATCH] Added the ability to display "Courses with unpublished scores" and "abnormal courses" --- grade.txt | 2 +- main.py | 66 +++++++++++++- old_grade.txt | 2 +- zfn_api.py | 248 ++++++++++++++++++++++++++++++++------------------ 4 files changed, 225 insertions(+), 93 deletions(-) diff --git a/grade.txt b/grade.txt index c5a1cf8..7fb67d0 100644 --- a/grade.txt +++ b/grade.txt @@ -1 +1 @@ -9e81cdbbc3ffd82d042e70b8c52cd7ee \ No newline at end of file +99dcc9ca2a4cc60bcbe64744a91ad38d \ No newline at end of file diff --git a/main.py b/main.py index 63e82a3..7ec0390 100644 --- a/main.py +++ b/main.py @@ -104,6 +104,11 @@ else: # 非第一次运行程序 run_count = 1 + +# 获取已选课程信息 +selected_courses_data = student_client.get_selected_courses().get("data", {}) +selected_courses = selected_courses_data.get("courses", []) + # 第一次运行程序则运行两遍,否则运行一遍 for _ in range(run_count): # 如果grade.txt文件不存在,则创建文件 @@ -161,7 +166,7 @@ for _ in range(run_count): # 整合成绩信息 integrated_grade_info += ( f"\n" - f"课程ID:{course['course_id']}\n" + f"教学班ID:{course['class_id']}\n" f"课程名称:{course['title']}\n" f"任课教师:{course['teacher']}\n" f"成绩:{course['grade']}\n" @@ -180,6 +185,7 @@ for _ in range(run_count): with open("grade.txt", "w") as grade_file: grade_file.write(encrypted_integrated_grade_info) + # 成绩信息不为空时整合GPA信息 if grade: # 整合个人信息 @@ -203,7 +209,62 @@ first_run_text = ( ) # 整合MD5值 -integrated_grade_info += f"\n" f"MD5:{encrypted_integrated_grade_info}" +integrated_grade_info += f"\n" f"当前成绩的MD5值:{encrypted_integrated_grade_info}" + + +if selected_courses: + # 初始化空字典用于存储未公布成绩的课程,按学年学期分组 + ungraded_courses_by_semester = {} + # 初始化空字典用于存储异常的课程,按学年学期分组 + abnormal_courses_by_semester = {} + + # 获取成绩列表中的class_id集合 + grade_class_ids = {course["class_id"] for course in grade} + + # 初始化输出内容 + selected_courses_filtering = "" + + # 遍历selected_courses和grade中的每个课程 + for course in selected_courses + grade: + # 获取课程的class_id和学年学期 + yearsemester_id = course["class_name"].split("(")[1].split(")")[0] + year, semester, seq = yearsemester_id.split("-") + + # 构建年学期名称,例如 "a至b学年第c学期" + yearsemester_name = f"{year}至{semester}学年第{seq}学期" + + # 判断课程是否未公布成绩或为异常课程 + if course["class_id"] not in grade_class_ids: # 未公布成绩 + ungraded_courses_by_semester.setdefault(yearsemester_name, []).append( + f"{course['title'].replace('(', '(').replace(')', ')')} - {course['teacher']}" + ) + elif course["class_id"] not in {course["class_id"] for course in selected_courses}: # 异常课程 + abnormal_courses_by_semester.setdefault(yearsemester_name, []).append( + f"{course['title'].replace('(', '(').replace(')', ')')} - {course['teacher']}" + ) + + # 构建输出内容 + if ungraded_courses_by_semester: # 存在未公布成绩的课程 + selected_courses_filtering += "未公布成绩的课程:" + for i, (semester, courses) in enumerate(ungraded_courses_by_semester.items()): + if i > 0: + selected_courses_filtering += "\n------" + selected_courses_filtering += f"\n{semester}如下:" + for course in courses: + selected_courses_filtering += f"\n{course}" + + if abnormal_courses_by_semester: # 存在异常的课程 + if ungraded_courses_by_semester: # 如果存在未公布成绩的课程,添加分隔线 + selected_courses_filtering += "\n" + selected_courses_filtering += "------\n异常的课程:" + for i, (semester, courses) in enumerate(abnormal_courses_by_semester.items()): + if i > 0: + selected_courses_filtering += "\n------" + selected_courses_filtering += f"\n{semester}如下:" + for course in courses: + selected_courses_filtering += f"\n{course}" +else: + selected_courses_filtering = "已选课程详细为空" # 工作流信息 workflow_info = ( @@ -225,6 +286,7 @@ workflow_info = ( integrated_send_info = ( f"{integrated_info}\n" f"{integrated_grade_info}\n" + f"{selected_courses_filtering}\n" f"{workflow_info}" ) diff --git a/old_grade.txt b/old_grade.txt index c5a1cf8..7fb67d0 100644 --- a/old_grade.txt +++ b/old_grade.txt @@ -1 +1 @@ -9e81cdbbc3ffd82d042e70b8c52cd7ee \ No newline at end of file +99dcc9ca2a4cc60bcbe64744a91ad38d \ No newline at end of file diff --git a/zfn_api.py b/zfn_api.py index c4d3eba..8ff0bf8 100644 --- a/zfn_api.py +++ b/zfn_api.py @@ -47,12 +47,12 @@ class Client: self.kaptcha_url = urljoin(self.base_url, "kaptcha") self.headers = requests.utils.default_headers() self.headers["Referer"] = self.login_url - self.headers[ - "User-Agent" - ] = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36" - self.headers[ - "Accept" - ] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3" + self.headers["User-Agent"] = ( + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36" + ) + self.headers["Accept"] = ( + "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3" + ) self.sess = requests.Session() self.sess.keep_alive = False self.cookies = cookies @@ -100,7 +100,11 @@ class Client: return {"code": 1002, "msg": "用户名或密码不正确"} return {"code": 998, "msg": tips.text()} self.cookies = self.sess.cookies.get_dict() - return {"code": 1000, "msg": "登录成功", "data": {"cookies": self.cookies}} + return { + "code": 1000, + "msg": "登录成功", + "data": {"cookies": self.cookies}, + } # 需要验证码,返回相关页面验证信息给用户,TODO: 增加更多验证方式 need_verify = True req_kaptcha = self.sess.get( @@ -130,7 +134,10 @@ class Client: AttributeError, ): traceback.print_exc() - return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"} + return { + "code": 2333, + "msg": "请重试,若多次失败可能是系统错误维护或需更新接口", + } except Exception as e: traceback.print_exc() msg = "获取验证码时未记录的错误" if need_verify else "登录时未记录的错误" @@ -175,7 +182,11 @@ class Client: } self.cookies = route_cookies else: - return {"code": 1000, "msg": "登录成功", "data": {"cookies": self.cookies}} + return { + "code": 1000, + "msg": "登录成功", + "data": {"cookies": self.cookies}, + } except exceptions.Timeout: return {"code": 1003, "msg": "登录超时"} except ( @@ -184,7 +195,10 @@ class Client: AttributeError, ): traceback.print_exc() - return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"} + return { + "code": 2333, + "msg": "请重试,若多次失败可能是系统错误维护或需更新接口", + } except Exception as e: traceback.print_exc() return {"code": 999, "msg": "验证码登录时未记录的错误:" + str(e)} @@ -237,7 +251,10 @@ class Client: AttributeError, ): traceback.print_exc() - return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"} + return { + "code": 2333, + "msg": "请重试,若多次失败可能是系统错误维护或需更新接口", + } except Exception as e: traceback.print_exc() return {"code": 999, "msg": "获取个人信息时未记录的错误:" + str(e)} @@ -284,22 +301,32 @@ class Client: # "status": "无" if pending_result.get("学籍状态:") == '' else pending_result["学籍状态:"], # "entry_date": "无" if pending_result.get("入学日期:") == '' else pending_result["入学日期:"], # "graduation_school": "无" if pending_result.get("毕业中学:") == '' else pending_result["毕业中学:"], - "domicile": "无" - if pending_result.get("籍贯:") == "" - else pending_result["籍贯:"], - "phone_number": "无" - if pending_result.get("手机号码:") == "" - else pending_result["手机号码:"], + "domicile": ( + "无" + if pending_result.get("籍贯:") == "" + else pending_result["籍贯:"] + ), + "phone_number": ( + "无" + if pending_result.get("手机号码:") == "" + else pending_result["手机号码:"] + ), "parents_number": "无", - "email": "无" - if pending_result.get("电子邮箱:") == "" - else pending_result["电子邮箱:"], - "political_status": "无" - if pending_result.get("政治面貌:") == "" - else pending_result["政治面貌:"], - "national": "无" - if pending_result.get("民族:") == "" - else pending_result["民族:"], + "email": ( + "无" + if pending_result.get("电子邮箱:") == "" + else pending_result["电子邮箱:"] + ), + "political_status": ( + "无" + if pending_result.get("政治面貌:") == "" + else pending_result["政治面貌:"] + ), + "national": ( + "无" + if pending_result.get("民族:") == "" + else pending_result["民族:"] + ), # "education": "无" if pending_result.get("培养层次:") == '' else pending_result["培养层次:"], # "postal_code": "无" if pending_result.get("邮政编码:") == '' else pending_result["邮政编码:"], # "grade": int(pending_result["学号:"][0:4]), @@ -308,15 +335,21 @@ class Client: # 如果在个人信息页面获取到了学院班级 result.update( { - "college_name": "无" - if pending_result.get("学院名称:") == "" - else pending_result["学院名称:"], - "major_name": "无" - if pending_result.get("专业名称:") == "" - else pending_result["专业名称:"], - "class_name": "无" - if pending_result.get("班级名称:") == "" - else pending_result["班级名称:"], + "college_name": ( + "无" + if pending_result.get("学院名称:") == "" + else pending_result["学院名称:"] + ), + "major_name": ( + "无" + if pending_result.get("专业名称:") == "" + else pending_result["专业名称:"] + ), + "class_name": ( + "无" + if pending_result.get("班级名称:") == "" + else pending_result["班级名称:"] + ), } ) else: @@ -348,15 +381,21 @@ class Client: pending_result[key] = value result.update( { - "college_name": "无" - if pending_result.get("学院:") is None - else pending_result["学院:"], - "major_name": "无" - if pending_result.get("专业:") is None - else pending_result["专业:"], - "class_name": "无" - if pending_result.get("班级:") is None - else pending_result["班级:"], + "college_name": ( + "无" + if pending_result.get("学院:") is None + else pending_result["学院:"] + ), + "major_name": ( + "无" + if pending_result.get("专业:") is None + else pending_result["专业:"] + ), + "class_name": ( + "无" + if pending_result.get("班级:") is None + else pending_result["班级:"] + ), } ) return {"code": 1000, "msg": "获取个人信息成功", "data": result} @@ -368,24 +407,30 @@ class Client: AttributeError, ): traceback.print_exc() - return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"} + return { + "code": 2333, + "msg": "请重试,若多次失败可能是系统错误维护或需更新接口", + } except Exception as e: traceback.print_exc() return {"code": 999, "msg": "获取个人信息时未记录的错误:" + str(e)} - def get_grade(self, year: int, term: int = 0, use_personal_info: bool = False): + def get_grade(self, year: int = 0, term: int = 0, use_personal_info: bool = False): """ 获取成绩 use_personal_info: 是否使用获取个人信息接口获取成绩 """ url = urljoin( self.base_url, - "cjcx/cjcx_cxDgXscj.html?doType=query&gnmkdm=N305005" - if use_personal_info - else "cjcx/cjcx_cxXsgrcj.html?doType=query&gnmkdm=N305005", + ( + "cjcx/cjcx_cxDgXscj.html?doType=query&gnmkdm=N305005" + if use_personal_info + else "cjcx/cjcx_cxXsgrcj.html?doType=query&gnmkdm=N305005" + ), ) temp_term = term term = term**2 * 3 + year = "" if year == 0 else year term = "" if term == 0 else term data = { "xnm": str(year), # 学年数 @@ -423,18 +468,13 @@ class Client: "count": len(grade_items), "courses": [ { - "course_id": i.get("kch_id"), "title": i.get("kcmc"), "teacher": i.get("jsxm"), "class_name": i.get("jxbmc"), + "class_id": i.get("jxb_id"), "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"), @@ -452,7 +492,10 @@ class Client: AttributeError, ): traceback.print_exc() - return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"} + return { + "code": 2333, + "msg": "请重试,若多次失败可能是系统错误维护或需更新接口", + } except Exception as e: traceback.print_exc() return {"code": 999, "msg": "获取成绩时未记录的错误:" + str(e)} @@ -519,7 +562,10 @@ class Client: AttributeError, ): traceback.print_exc() - return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"} + return { + "code": 2333, + "msg": "请重试,若多次失败可能是系统错误维护或需更新接口", + } except Exception as e: traceback.print_exc() return {"code": 999, "msg": "获取课表时未记录的错误:" + str(e)} @@ -602,7 +648,10 @@ class Client: AttributeError, ): traceback.print_exc() - return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"} + return { + "code": 2333, + "msg": "请重试,若多次失败可能是系统错误维护或需更新接口", + } except Exception as e: traceback.print_exc() return {"code": 999, "msg": "获取学业情况时未记录的错误:" + str(e)} @@ -739,7 +788,10 @@ class Client: AttributeError, ): traceback.print_exc() - return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"} + return { + "code": 2333, + "msg": "请重试,若多次失败可能是系统错误维护或需更新接口", + } except Exception as e: traceback.print_exc() return {"code": 999, "msg": "获取成绩总表pdf时未记录的错误:" + str(e)} @@ -813,7 +865,10 @@ class Client: AttributeError, ): traceback.print_exc() - return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"} + return { + "code": 2333, + "msg": "请重试,若多次失败可能是系统错误维护或需更新接口", + } except Exception as e: traceback.print_exc() return {"code": 999, "msg": "获取课程表pdf时未记录的错误:" + str(e)} @@ -859,21 +914,35 @@ class Client: AttributeError, ): traceback.print_exc() - return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"} + return { + "code": 2333, + "msg": "请重试,若多次失败可能是系统错误维护或需更新接口", + } except Exception as e: traceback.print_exc() return {"code": 999, "msg": "获取消息时未记录的错误:" + str(e)} - def get_selected_courses(self, year: int, term: int): + def get_selected_courses(self, year: int = 0, term: int = 0): """获取已选课程信息""" try: url = urljoin( self.base_url, - "xsxk/zzxkyzb_cxZzxkYzbChoosedDisplay.html?gnmkdm=N253512", + "/xsxxxggl/xsxxwh_cxXsxkxx.html?gnmkdm=N100801", ) temp_term = term term = term**2 * 3 - data = {"xkxnm": str(year), "xkxqm": str(term)} + year = "" if year == 0 else year + term = "" if term == 0 else term + data = { + "xnm": str(year), + "xqm": str(term), + "_search": "false", + "queryModel.showCount": 5000, + "queryModel.currentPage": 1, + "queryModel.sortName": "", + "queryModel.sortOrder": "asc", + "time": 1, + } req_selected = self.sess.post( url, data=data, @@ -893,22 +962,12 @@ class Client: "count": len(selected), "courses": [ { - "course_id": i.get("kch"), "class_id": i.get("jxb_id"), - "do_id": i.get("do_jxb_id"), + "class_name": i.get("jxbmc"), "title": i.get("kcmc"), - "teacher_id": (re.findall(r"(.*?\d+)/", i.get("jsxx")))[0], - "teacher": (re.findall(r"/(.*?)/", i.get("jsxx")))[0], - "credit": float(i.get("xf", 0)), - "category": i.get("kklxmc"), - "capacity": int(i.get("jxbrs", 0)), - "selected_number": int(i.get("yxzrs", 0)), - "place": self.get_place(i.get("jxdd")), - "time": self.get_course_time(i.get("sksj")), - "optional": int(i.get("zixf", 0)), - "waiting": i.get("sxbj"), + "teacher": i.get("jsxm"), } - for i in selected + for i in selected["items"] ], } return {"code": 1000, "msg": "获取已选课程成功", "data": result} @@ -920,7 +979,10 @@ class Client: AttributeError, ): traceback.print_exc() - return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"} + return { + "code": 2333, + "msg": "请重试,若多次失败可能是系统错误维护或需更新接口", + } except Exception as e: traceback.print_exc() return {"code": 999, "msg": f"获取已选课程时未记录的错误:{str(e)}"} @@ -1066,7 +1128,6 @@ class Client: "count": len(temp_list), "courses": [ { - "course_id": j["kch_id"], "class_id": j.get("jxb_id"), "do_id": j.get("do_jxb_id"), "title": j.get("kcmc"), @@ -1091,7 +1152,10 @@ class Client: AttributeError, ): traceback.print_exc() - return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"} + return { + "code": 2333, + "msg": "请重试,若多次失败可能是系统错误维护或需更新接口", + } except Exception as e: traceback.print_exc() return {"code": 999, "msg": f"获取板块课信息时未记录的错误:{str(e)}"} @@ -1151,7 +1215,10 @@ class Client: AttributeError, ): traceback.print_exc() - return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"} + return { + "code": 2333, + "msg": "请重试,若多次失败可能是系统错误维护或需更新接口", + } except Exception as e: traceback.print_exc() return {"code": 999, "msg": f"选课时未记录的错误:{str(e)}"} @@ -1191,7 +1258,10 @@ class Client: AttributeError, ): traceback.print_exc() - return {"code": 2333, "msg": "请重试,若多次失败可能是系统错误维护或需更新接口"} + return { + "code": 2333, + "msg": "请重试,若多次失败可能是系统错误维护或需更新接口", + } except Exception as e: traceback.print_exc() return {"code": 999, "msg": f"选课时未记录的错误:{str(e)}"} @@ -1238,7 +1308,7 @@ class Client: try: data_list = [(th.text).strip() for th in ths] return data_list[6] - except: + except IndexError: return None @classmethod @@ -1409,7 +1479,9 @@ class Client: ): repetIndex.append(index) # 满足条件记录索引 count += 1 # 记录当前对比课程的索引 - if len(repetIndex) % 2 != 0: # 暂时考虑一天两个时段上同一门课,不满足条件不进行修改 + if ( + len(repetIndex) % 2 != 0 + ): # 暂时考虑一天两个时段上同一门课,不满足条件不进行修改 return schedule for r in range(0, len(repetIndex), 2): # 索引数组两两成对,故步进2循环 fir = repetIndex[r] @@ -1479,14 +1551,12 @@ class Client: if __name__ == "__main__": from pprint import pprint - import json - import base64 import sys import os - base_url = "https://www.nianbroken.top" # 教务系统URL - sid = "NianBroken" # 学号 - password = "NianBroken" # 密码 + base_url = "https://www.nianbroken.top/" # 教务系统URL + sid = "2971802058" # 学号 + password = "2971802058" # 密码 lgn_cookies = ( { # "insert_cookie": "",