| Conditions | 21 |
| Total Lines | 62 |
| Code Lines | 51 |
| Lines | 0 |
| Ratio | 0 % |
| Changes | 0 | ||
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like quiz.Quiz.import_questions() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | import contextlib |
||
| 34 | def import_questions(self, directory: str) -> None: |
||
| 35 | for subdir, dirs, files in os.walk(directory): |
||
| 36 | for file in files: |
||
| 37 | file_path = os.path.join(subdir, file) |
||
| 38 | with open(file_path, "rb") as source_file: |
||
| 39 | webpage = BeautifulSoup(source_file, "html.parser") |
||
| 40 | |||
| 41 | multichoice_questions = webpage.find_all("div", class_="multichoice") |
||
| 42 | for question in multichoice_questions: |
||
| 43 | correctly_answered: bool |
||
| 44 | grading_text = question.find("div", class_="grade").text |
||
| 45 | numbers = re.findall(r"\d+\.\d+", grading_text) |
||
| 46 | grade = float(numbers[0]) |
||
| 47 | maximum_points = float(numbers[1]) |
||
| 48 | if grade == maximum_points: |
||
| 49 | correctly_answered = True |
||
| 50 | else: |
||
| 51 | correctly_answered = False |
||
| 52 | |||
| 53 | question_text = question.find("div", class_="qtext").text |
||
| 54 | answers = question.find("div", class_="answer") |
||
| 55 | answer_texts: list[str] = [] |
||
| 56 | correct_answers: list[int] = [] |
||
| 57 | i = 1 |
||
| 58 | for answer in answers: |
||
| 59 | with contextlib.suppress(TypeError): |
||
| 60 | answer_texts.append(answer.find("div", class_="ml-1").text.rstrip(".")) |
||
| 61 | if "correct" in answer["class"]: |
||
| 62 | correct_answers.append(i) |
||
| 63 | i += 1 |
||
| 64 | if not correctly_answered: |
||
| 65 | print(f""" |
||
| 66 | |||
| 67 | Question: '{question_text}' |
||
| 68 | |||
| 69 | I see that answers {correct_answers} are correct, but this list may be incomplete because you only got {grade:g} points out of {maximum_points:g}. |
||
| 70 | |||
| 71 | The answers are:""") |
||
| 72 | for j, answer in enumerate(answer_texts): |
||
| 73 | print(f"#{j + 1}\t{answer}") |
||
| 74 | print() |
||
| 75 | while True: |
||
| 76 | additional_correct_answer = input( |
||
| 77 | f"Please enter a missing correct answer (if there is any remaining) then press Enter: ") |
||
| 78 | if additional_correct_answer == "" or len(correct_answers) == len(answer_texts) - 1: |
||
| 79 | break |
||
| 80 | correct_answers.append(int(additional_correct_answer)) |
||
| 81 | illustration = True if question.find("img", class_="img-responsive") else False |
||
| 82 | |||
| 83 | for existing_question in self.questions: |
||
| 84 | if existing_question.text == question_text: |
||
| 85 | for k, answer in enumerate(answer_texts): |
||
| 86 | if answer not in existing_question.answers: |
||
| 87 | existing_question.answers.append(answer) |
||
| 88 | if k + 1 in correct_answers: |
||
| 89 | existing_question.correct_answers.append(len(existing_question.answers)) |
||
| 90 | break |
||
| 91 | else: |
||
| 92 | self.questions.add( |
||
| 93 | Question(q_type=QuestionType.MultipleChoice, text=question_text, illustration=illustration, |
||
| 94 | answers=answer_texts, |
||
| 95 | correct_answers=correct_answers)) |
||
| 96 |