diff --git a/fxxkstar.py b/fxxkstar.py
index 8b52bf9..793d195 100644
--- a/fxxkstar.py
+++ b/fxxkstar.py
@@ -874,8 +874,11 @@ def medias_deal(self, card_info: dict, course_id, clazz_id, chapter_id) -> None:
f.write(mod.paper_html)
print("[Work] ", mod.title, mod.work_id, " saved")
- if not mod.is_marked:
- questions = mod.parse_paper(mod.paper_html)
+ questions = mod.parse_paper(mod.paper_html)
+ if mod.is_marked:
+ if G_VERBOSE:
+ print(questions)
+ else:
# uncertain_questions = mod.correct_answers(
# questions, mod.work_id, card_url)
if G_VERBOSE:
@@ -1266,68 +1269,191 @@ def load(self):
@staticmethod
def parse_paper(paper_page_html: str) -> List[dict]:
"Parsing the questions and answers in the page html into dict"
+
soup = BeautifulSoup(paper_page_html, "lxml")
- form1 = soup.find("form", id="form1")
- question_divs = form1.find_all("div", class_="TiMu")
- title_tags = ["【单选题】", "【多选题】", "【填空题】", "【判断题】"]
+
+ top_div = soup.find("div", class_="ZyTop")
+ status_el = top_div.select("h3 span")
+ status_title = status_el[0].text.strip() if status_el else ''
+ assert status_title in ["待做", "已完成"]
+ marked = status_title == "已完成"
+ score = -1
+
+ if marked:
+ # [ 已完成,
+ # 本次成绩:100,
+ # 100 ]
+ assert len(status_el) == 3
+ score = int(status_el[2].text.strip())
+ assert f"本次成绩:{score}" in status_el[1].text.strip()
+
+ q_div = soup.find("div", id="ZyBottom")
+ question_divs = q_div.find_all("div", class_="TiMu")
+ title_tags = ["【单选题】", "【多选题】", "【填空题】", "【判断题】", "【简答题】"]
questions = []
for question_div in question_divs:
question_title = question_div.select(
".Zy_TItle > .clearfix,.Cy_TItle > .clearfix")[0].text.strip()
- for title_tag in title_tags:
- if question_title.startswith(title_tag):
- question_title = question_title[len(title_tag):].strip()
+ question_tag = ""
+ question_type = -1
+ for i in range(len(title_tags)):
+ if question_title.startswith(title_tags[i]):
+ question_tag = title_tags[i]
+ question_type = i
+ question_title = question_title[len(question_tag):].strip()
break
- answertype_node = question_div.find(
- "input", id=re.compile("answertype"))
- question_type = int(answertype_node.get("value"))
- question_id = answertype_node.get("id")[10:]
+ assert question_type >= 0
+ assert question_tag in title_tags
+
question = {
"topic": question_title,
- "type": question_type,
- "question_id": question_id,
+ "type": question_type, # choice: 0, multiple: 1, fill: 2, judge: 3, short: 4
+ "options": None, # type: List[dict], may not exist
+ "correct": None, # correct options, only available if marked
+ "wrong": None, # wrong options, only available if marked
+ "answer": None, # answer string, only available if marked
+ "is_correct": None, # is_correct, only available if marked
+ "correct_answer": None, # correct answer string, only available if marked, may not exist
+ "question_id": None, # question id, only available if not finished
}
- if question_type == 0 or question_type == 1:
+
+ if not marked: # parse question_id
+ answertype_node = question_div.find(
+ "input", id=re.compile("answertype"))
+ assert question_type == int(answertype_node.get("value"))
+ question_id = answertype_node.get("id")[10:]
+ question["question_id"] = question_id
+ else: # parse answer
+ answer_el = question_div.select(".Py_answer")[0]
+
+ answer_mark_el = answer_el.select("i.fr")
+ if answer_mark_el:
+ answer_mark_classlist: list = answer_mark_el[0].get(
+ "class")
+ if "dui" in answer_mark_classlist:
+ question["is_correct"] = True
+ elif "cuo" in answer_mark_classlist:
+ question["is_correct"] = False
+ else:
+ print("[WARN] module_work, parse_paper, unexpected answer_mark_classlist:",
+ answer_mark_classlist)
+ assert False
+
+ answer_result_el = answer_el.select("span")
+ answer: str = answer_result_el[0].text.strip()
+ if answer.startswith("正确答案:"):
+ question["correct_answer"] = answer[len("正确答案:"):].strip()
+ assert len(answer_result_el) > 2
+ answer = answer_result_el[1].text.strip()
+ assert answer.startswith("我的答案:")
+ elif answer.startswith("我的答案:"):
+ pass
+ else:
+ assert False
+ question["answer"] = answer[len("我的答案:"):].strip()
+
+ # parse options
+ if question_type == 0 or question_type == 1: # Choice
options = []
selected = []
- option_nodes = question_div.select("ul.fl li")
- for option_node in option_nodes:
- option_input_node = option_node.select(
- "label.fl.before input")[0]
- option = option_input_node.get("value")
- content = option_node.select("a.fl.after")[0].text.strip()
- option_info = {"option": option, "content": content}
- options.append(option_info)
- if option_input_node.get("checked") == "true":
- selected.append(option_info)
+ option_nodes = question_div.select("ul.Zy_ulTop li")
+
+ if marked:
+ for option_node in option_nodes:
+ option: str = option_node.select(
+ "i.fl")[0].text.strip()
+ content: str = option_node.select(
+ "a.fl")[0].text.strip()
+ assert len(option) == 2 and option.endswith("、") # A、
+ option = option[:-1]
+ option_info = {"option": option, "content": content}
+ options.append(option_info)
+
+ not_selected = []
+ for option_info in options:
+ if option_info["option"] in question["answer"]:
+ selected.append(option_info)
+ else:
+ not_selected.append(option_info)
+ if question["is_correct"] is not None:
+ if question["is_correct"] == True:
+ question["correct"] = selected
+ elif question_type == 0:
+ question["wrong"] = selected
+
+ assert len(selected) == len(question["answer"])
+ else:
+ for option_node in option_nodes:
+ option_input_node = option_node.select(
+ "label.fl.before input")[0]
+ option = option_input_node.get("value")
+ content = option_node.select("a.fl.after")[
+ 0].text.strip()
+ option_info = {"option": option, "content": content}
+ options.append(option_info)
+ if option_input_node.get("checked") == "true":
+ selected.append(option_info)
question['options'] = options
if selected.__len__() > 0:
question['answers'] = selected
- elif question_type == 3:
- choices_node = question_div.find_all(
- "input", attrs={"name": f"answer{question_id}"})
+
+ elif question_type == 3: # Judge
selected = []
- for choice_node in choices_node:
- if choice_node.get("checked") == "true":
- judge = choice_node.get("value")
- if judge == "true":
- selected.append({"option": True, "content": True})
- elif judge == "false":
- selected.append(
- {"option": False, "content": False})
- else:
- selected.append(
- {"option": judge, "content": judge})
- break
+ if marked:
+ answer = question["answer"]
+ assert answer == "√" or answer == "×"
+ if answer == "√":
+ selected.append({"option": True, "content": True})
+ elif answer == "×":
+ selected.append({"option": False, "content": False})
+ if question["is_correct"] is not None:
+ if question["is_correct"] == True:
+ question["correct"] = selected
+ elif question["is_correct"] == False:
+ correct_judge = not selected[0]["option"]
+ question["correct"] = [
+ {"option": correct_judge, "content": correct_judge}]
+ assert len(selected) == 1
+ else:
+ choices_node = question_div.find_all(
+ "input", attrs={"name": f"answer{question_id}"})
+ assert len(choices_node) == 2
+ for choice_node in choices_node:
+ if choice_node.get("checked") == "true":
+ judge = choice_node.get("value")
+ assert judge in ["true", "false"]
+ if judge == "true":
+ selected.append(
+ {"option": True, "content": True})
+ elif judge == "false":
+ selected.append(
+ {"option": False, "content": False})
+ break
if len(selected) > 0:
question['answers'] = selected
- elif question_type == 2:
- content = question_div.find(
- attrs={"name": f"answer{question_id}"}).get("value")
+ elif question_type == 2: # Fill
+ content = None
+ if marked:
+ content = question["answer"]
+ else:
+ content = question_div.find(
+ attrs={"name": f"answer{question_id}"}).get("value")
if content:
question['answers'] = [{"option": "一", "content": content}]
+ if question["correct_answer"]:
+ question['correct'] = [
+ {"option": "一", "content": question["correct_answer"]}]
+ elif question["is_correct"] == True:
+ question["correct"] = question["answers"]
else:
print("not support question type:", question_type)
+
+ empty_properties = []
+ for key in question.keys():
+ if question[key] == None:
+ empty_properties.append(key)
+ for key in empty_properties:
+ del question[key]
questions.append(question)
return questions
@@ -1621,7 +1747,11 @@ def upload_answers(self, answers: List[dict], confirm_submit=False) -> bool:
answered_html = WorkModule.render_paper(self.paper_html, answers)
if WorkModule.module_work_submit(self.fxxkstar, answered_html, do_submit=confirm_submit):
time.sleep(0.2)
- self.load()
+ if confirm_submit:
+ self.load() # reload the page to get the result
+ result = WorkModule.parse_paper(self.paper_html)
+ if G_VERBOSE:
+ print(result)
return True
else:
return False