diff --git a/src/moodle_to_vikwikiquiz/helpers.py b/src/moodle_to_vikwikiquiz/helpers.py
new file mode 100644
index 0000000..ebec8db
--- /dev/null
+++ b/src/moodle_to_vikwikiquiz/helpers.py
@@ -0,0 +1,19 @@
+from os import system
+from pathlib import Path
+from time import sleep
+
+from send2trash import send2trash # type: ignore
+
+
+def clear_terminal() -> None:
+ system("clear||cls")
+
+
+def wait_for_pastebot_to_recognize_copy() -> None:
+ print("Waiting 2 seconds for Pastebot to recognize it...")
+ sleep(2)
+ print("...done!")
+
+
+def remove_uploaded_files(folder: Path) -> None:
+ send2trash(folder)
diff --git a/src/moodle_to_vikwikiquiz/main.py b/src/moodle_to_vikwikiquiz/main.py
index 5d67ec6..4e64794 100644
--- a/src/moodle_to_vikwikiquiz/main.py
+++ b/src/moodle_to_vikwikiquiz/main.py
@@ -1,20 +1,13 @@
from argparse import ArgumentParser, Namespace
import logging
from pathlib import Path
-from platform import system
from sys import version_info
-from time import sleep
-from urllib.parse import quote, urlencode
-from webbrowser import open_new_tab
-
-# future: delete the comment below when stubs for the package below are available
-from pyperclip import copy # type: ignore
-from send2trash import send2trash # type: ignore
+from .wiki import create_article, get_article_instructions, log_in_to_wiki # type: ignore
from .quiz.illustrations.state_of_illustrations import StateOfIllustrations # type: ignore
from .quiz.grading_types import GradingType # type: ignore
from .quiz.quiz import Quiz # type: ignore
-from .quiz.quiz_helpers import clear_terminal # type: ignore
+from .helpers import clear_terminal, wait_for_pastebot_to_recognize_copy # type: ignore
def main() -> None:
@@ -69,108 +62,6 @@ def main() -> None:
logging.getLogger(__name__).debug("Program finished!")
-def get_article_instructions(
- quiz: Quiz, wiki_domain: str
-) -> tuple[str, dict[str, str], dict[str, str], dict[str, str]]:
- wikitext_instructions = """
-"""
- parameters_for_opening_edit = {
- "action": "edit",
- "summary": "Kvíz bővítése "
- "a https://github.com/gy-mate/moodle-to-vikwikiquiz segítségével importált Moodle-kvíz(ek)ből",
- "preload": "Sablon:Előbetöltés",
- "preloadparams[]": wikitext_instructions,
- }
- clear_terminal()
- return (
- operating_system,
- parameters_for_opening_edit,
- wiki_editor_keys,
- wiki_modifier_keys,
- )
-
-
-def log_in_to_wiki(wiki_domain: str) -> None:
- input(
- """Let's log in to the wiki! Please...
-• if you see the login page, log in
-• when you see the main page of the wiki, return here.
-
-Please press Enter to open the login page..."""
- )
- open_new_tab(f"{wiki_domain}/index.php?title=Speciális:Belépés")
- input("Please press Enter if you've logged in...")
- clear_terminal()
-
-
-def remove_uploaded_files(folder: Path) -> None:
- send2trash(folder)
-
-
def parse_arguments() -> Namespace:
parser = ArgumentParser(
"Convert graded and downloaded Moodle quizzes to a vik.viki quiz wikitext."
@@ -258,102 +149,5 @@ def get_grading() -> GradingType:
clear_terminal()
-def create_article(
- args: Namespace,
- parameters_for_opening_edit: dict[str, str],
- quiz_title: str,
- quiz_wikitext: str,
- wiki_domain: str,
- wiki_modifier_keys: dict[str, str],
- wiki_editor_keys: dict[str, str],
- operating_system: str,
-) -> None:
- if args.new:
- parameters_for_opening_edit_with_paste = parameters_for_opening_edit.copy()
- parameters_for_opening_edit_with_paste.update(
- {
- "preload": "Sablon:Előbetöltés",
- "preloadparams[]": quiz_wikitext,
- }
- )
- parameters_for_opening_edit_with_paste["summary"] = (
- parameters_for_opening_edit_with_paste["summary"].replace(
- "bővítése", "létrehozása"
- )
- )
- url = f"{wiki_domain}/{quiz_title}?{urlencode(parameters_for_opening_edit_with_paste)}"
- if len(url) < 2048:
- return open_article_paste_text(args, quiz_wikitext, url)
- else:
- open_article(args, parameters_for_opening_edit, url)
- else:
- del parameters_for_opening_edit["preload"]
- del parameters_for_opening_edit["preloadparams[]"]
- copy(quiz_wikitext)
- print("\nThe wikitext of the quiz has been copied to the clipboard!")
- url = f"{wiki_domain}/{quote(quiz_title)}?{urlencode(parameters_for_opening_edit)}"
- if not args.new:
- print(
- f"""
-The existing article will now be opened for editing. After that, please...
-• scroll to the bottom of the wikitext in the editor
-• add a new line
-• paste the content of the clipboard in that line
-• click on the 'Előnézet megtekintése' button ({wiki_modifier_keys[operating_system]}-{wiki_editor_keys["Show preview"]})
-• correct the spelling and formatting (if necessary), especially the formulas
-• click on the 'Lap mentése' button ({wiki_modifier_keys[operating_system]}-{wiki_editor_keys["Publish page"]})"""
- )
- input("\nPlease press Enter then follow these instructions...")
- open_new_tab(url)
- print(
- "\nThe edit page of the quiz article has been opened in your browser!", end=" "
- )
- if args.new:
- print("Please follow the instructions there.")
-
-
-def open_article_paste_text(args: Namespace, quiz_wikitext: str, url: str) -> None:
- copy(quiz_wikitext)
- print(
- "\nThe wikitext of the quiz has been copied to the clipboard! "
- "This will be overwritten but you may recall it later if you use an app like Pastebot."
- )
- wait_for_pastebot_to_recognize_copy()
- if args.verbose:
- copy(url)
- print("The URL has been copied to the clipboard!")
- open_new_tab(url)
- print(
- "\nThe edit page of the new quiz article has been opened in your browser with the wikitext pre-filled! "
- "Please upload illustrations manually, if there are any."
- )
- return
-
-
-def open_article(
- args: Namespace, parameters_for_opening_edit: dict[str, str], url: str
-) -> None:
- logging.getLogger(__name__).warning(
- "I can't create the article automatically "
- "because the URL would be too long for some browsers (or the server)."
- )
- if args.verbose:
- copy(url)
- print(
- "\nThis URL has been copied to the clipboard! "
- "It will be overwritten but you may recall it later if you use an app like Pastebot."
- )
- wait_for_pastebot_to_recognize_copy()
- parameters_for_opening_edit["summary"] = parameters_for_opening_edit[
- "summary"
- ].replace("bővítése", "létrehozása")
-
-
-def wait_for_pastebot_to_recognize_copy() -> None:
- print("Waiting 2 seconds for Pastebot to recognize it...")
- sleep(2)
- print("...done!")
-
-
if __name__ == "__main__":
main()
diff --git a/src/moodle_to_vikwikiquiz/quiz/helpers.py b/src/moodle_to_vikwikiquiz/quiz/helpers.py
new file mode 100644
index 0000000..de539ec
--- /dev/null
+++ b/src/moodle_to_vikwikiquiz/quiz/helpers.py
@@ -0,0 +1,20 @@
+from re import sub
+
+from .illustrations.illustration import Illustration # type: ignore
+from .questions.helpers import format_latex_as_wikitext # type: ignore
+from .quiz_element import QuizElement # type: ignore
+from .illustrations.state_of_illustrations import StateOfIllustrations # type: ignore
+from .questions.question import Question # type: ignore
+from .questions.question_types import QuestionType # type: ignore
+
+
+def prettify(text: str) -> str:
+ text = strip_whitespaces(text)
+ text = format_latex_as_wikitext(text)
+ return text
+
+
+def strip_whitespaces(text: str) -> str:
+ text = text.strip("., \n")
+ text = sub(r" \n|\r\n|\s{2}", " ", text)
+ return text
diff --git a/src/moodle_to_vikwikiquiz/quiz/questions/answer.py b/src/moodle_to_vikwikiquiz/quiz/questions/answers/answer.py
similarity index 76%
rename from src/moodle_to_vikwikiquiz/quiz/questions/answer.py
rename to src/moodle_to_vikwikiquiz/quiz/questions/answers/answer.py
index 48b4e27..464c34f 100644
--- a/src/moodle_to_vikwikiquiz/quiz/questions/answer.py
+++ b/src/moodle_to_vikwikiquiz/quiz/questions/answers/answer.py
@@ -1,6 +1,5 @@
-from ..illustrations.illustration import Illustration # type: ignore
-from ..quiz_element import QuizElement # type: ignore
-from ..illustrations.state_of_illustrations import StateOfIllustrations # type: ignore
+from ...illustrations.illustration import Illustration # type: ignore
+from ...quiz_element import QuizElement # type: ignore
class Answer(QuizElement):
diff --git a/src/moodle_to_vikwikiquiz/quiz/questions/answers/helpers.py b/src/moodle_to_vikwikiquiz/quiz/questions/answers/helpers.py
new file mode 100644
index 0000000..9196474
--- /dev/null
+++ b/src/moodle_to_vikwikiquiz/quiz/questions/answers/helpers.py
@@ -0,0 +1,160 @@
+from bs4 import Tag
+
+from ...helpers import prettify # type: ignore
+from .answer import Answer # type: ignore
+from ..question_types import QuestionType # type: ignore
+
+
+def answer_is_correct(
+ answer: Tag,
+ answer_text: str,
+ grade: float,
+ maximum_points: float,
+ correct_answers: set[str | None],
+) -> bool:
+ if correct_answers and answer_text in correct_answers:
+ return True
+ elif "correct" in answer["class"]:
+ return True
+ elif grade == maximum_points:
+ answer_input_element = answer.find("input")
+ assert isinstance(answer_input_element, Tag)
+ if answer_input_element.has_attr("checked"):
+ return True
+ return False
+
+
+def get_correct_answers(
+ answers: set[Answer],
+ grade: float,
+ maximum_points: float,
+ question_text: str,
+ question_type: QuestionType,
+ filename: str,
+) -> None:
+ number_of_current_correct_answers = 0
+ list_of_answers = list(answers)
+ correct_answers: list[Answer] = []
+ for answer in answers:
+ if answer.correct:
+ number_of_current_correct_answers += 1
+ correct_answers.append(answer)
+
+ if number_of_current_correct_answers == len(answers) - 1:
+ for answer in answers:
+ if not answer.correct:
+ answer.correct = True
+ return
+ print(f"File:\t\t{filename}")
+ print(f"Question:\t'{question_text}'")
+ match number_of_current_correct_answers:
+ case 0:
+ print("\nI couldn't determine any correct answers for sure.", end=" ")
+ case 1:
+ print(
+ f"\nI see that answer #{list_of_answers.index(correct_answers[0]) + 1} is correct, "
+ f"but there might be additional correct answers because you only got {grade:g} points out of {maximum_points:g}.",
+ end=" ",
+ )
+ case _:
+ correct_answer_indexes: list[int] = []
+ for correct_answer in correct_answers:
+ correct_answer_indexes.append(list_of_answers.index(correct_answer) + 1)
+ print(
+ f"\nI see that answers {correct_answer_indexes} are correct, "
+ f"but this list may be incomplete because you only got {grade:g} points out of {maximum_points:g}.",
+ end=" ",
+ )
+ print(f"The possible answers are:", end="\n\n")
+ for j, answer in enumerate(list_of_answers):
+ print(f"#{j + 1}\t{answer}")
+ print()
+ get_missing_correct_answers(correct_answers, list_of_answers, question_type)
+
+
+def get_missing_correct_answers(
+ correct_answers: list[Answer],
+ list_of_answers: list[Answer],
+ question_type: QuestionType,
+) -> None:
+ while True:
+ get_input_for_missing_correct_answers(
+ list_of_answers, correct_answers, question_type
+ )
+ for answer in list_of_answers:
+ if answer.correct:
+ return
+ print("Error: no correct answers were provided!", end="\n\n")
+
+
+def get_input_for_missing_correct_answers(
+ answers: list[Answer], correct_answers: list[Answer], question_type: QuestionType
+) -> None:
+ while len(correct_answers) < len(answers):
+ additional_correct_answer = input(
+ f"Please enter a missing correct answer (if there are any remaining) then press Enter: "
+ )
+ if additional_correct_answer == "":
+ break
+ elif not additional_correct_answer.isdigit():
+ print("Error: an integer was expected!", end="\n\n")
+ continue
+ elif int(additional_correct_answer) - 1 not in range(len(answers)):
+ print(
+ "Error: the number is out of the range of possible answers!", end="\n\n"
+ )
+ continue
+ elif int(additional_correct_answer) in correct_answers:
+ print(
+ "Error: this answer is already in the list of correct answers!",
+ end="\n\n",
+ )
+ continue
+ answers[int(additional_correct_answer) - 1].correct = True
+ if question_type == QuestionType.SingleChoice:
+ break
+
+
+def get_correct_answers_if_provided(question: Tag) -> set[str | None]:
+ tag = question.find("div", class_="rightanswer")
+ correct_answers: set[str | None] = set()
+
+ if tag:
+ assert isinstance(tag, Tag)
+ hint_text = prettify(tag.text)
+ single_correct_answer_description_translations = [
+ "A helyes válasz: ",
+ "The correct answer is: ",
+ ]
+ for (
+ correct_answer_description
+ ) in single_correct_answer_description_translations:
+ if correct_answer_description in hint_text:
+ correct_answer = hint_text.removeprefix(correct_answer_description)
+ correct_answers.add(correct_answer)
+ return correct_answers
+
+ multiple_correct_answer_description_translations = [
+ "A helyes válaszok: ",
+ "The correct answers are: ",
+ ]
+ for (
+ correct_answer_description
+ ) in multiple_correct_answer_description_translations:
+ if correct_answer_description in hint_text:
+ correct_answer_tags = tag.find_all("p")
+ for correct_answer_tag in correct_answer_tags:
+ correct_answer = correct_answer_tag.text
+ prettified_answer = prettify(correct_answer)
+ correct_answers.add(prettified_answer)
+ return correct_answers
+
+ if tag.find("img"):
+ pass
+ return correct_answers
+
+ raise NotImplementedError(
+ f"Correct answers could not be extracted from '{hint_text}'!"
+ )
+ else:
+ return correct_answers
diff --git a/src/moodle_to_vikwikiquiz/quiz/quiz_helpers.py b/src/moodle_to_vikwikiquiz/quiz/questions/helpers.py
similarity index 54%
rename from src/moodle_to_vikwikiquiz/quiz/quiz_helpers.py
rename to src/moodle_to_vikwikiquiz/quiz/questions/helpers.py
index e575287..68a9e87 100644
--- a/src/moodle_to_vikwikiquiz/quiz/quiz_helpers.py
+++ b/src/moodle_to_vikwikiquiz/quiz/questions/helpers.py
@@ -1,4 +1,4 @@
-from os import path, system
+from os import path
from pathlib import Path
from re import findall, sub
from urllib.parse import unquote, urlparse
@@ -10,220 +10,68 @@
from plum import dispatch
from pylatexenc.latexencode import unicode_to_latex # type: ignore
-from .illustrations.illustration import Illustration # type: ignore
-from .questions.answer import Answer # type: ignore
-from .quiz_element import QuizElement # type: ignore
-from .illustrations.state_of_illustrations import StateOfIllustrations # type: ignore
-from .questions.question import Question # type: ignore
-from .questions.question_types import QuestionType # type: ignore
+from .question_types import QuestionType # type: ignore
+from ..illustrations.illustration import Illustration # type: ignore
+from ..illustrations.state_of_illustrations import StateOfIllustrations # type: ignore
+from .answers.answer import Answer # type: ignore
+from .question import Question # type: ignore
+from ..quiz_element import QuizElement # type: ignore
-def get_question_type(question: Tag) -> QuestionType:
- if question.find("input", type="radio"):
- return QuestionType.SingleChoice
- elif question.find("input", type="checkbox"):
- return QuestionType.MultipleChoice
- else:
- raise NotImplementedError("Question type not implemented.")
-
-
-def get_grading_of_question(question: Tag) -> tuple[bool, float | None, float]:
- correctly_answered: bool
-
- found_tag = question.find("div", class_="grade")
- assert isinstance(found_tag, Tag)
-
- grading_text = found_tag.text
- numbers_in_capture_groups: list[tuple[str, str]] = findall(
- r"(\d+)([.,]\d+)?", grading_text
- )
- numbers = [
- whole + fraction.replace(",", ".")
- for whole, fraction in numbers_in_capture_groups
- ]
- grade: float | None = None
- match len(numbers):
- case 1:
- maximum_points = float(numbers[0])
- case 2:
- grade = float(numbers[0])
- maximum_points = float(numbers[1])
- case _:
- raise NotImplementedError(
- f"{len(numbers)} grade numbers found in '{grading_text}'!"
- )
- if grade == maximum_points:
- correctly_answered = True
- else:
- correctly_answered = False
- return correctly_answered, grade, maximum_points
-
-
-def get_correct_answers(
- answers: set[Answer],
- grade: float,
- maximum_points: float,
- question_text: str,
- question_type: QuestionType,
- filename: str,
-) -> None:
- number_of_current_correct_answers = 0
- list_of_answers = list(answers)
- correct_answers: list[Answer] = []
- for answer in answers:
- if answer.correct:
- number_of_current_correct_answers += 1
- correct_answers.append(answer)
-
- if number_of_current_correct_answers == len(answers) - 1:
- for answer in answers:
- if not answer.correct:
- answer.correct = True
- return
- print(f"File:\t\t{filename}")
- print(f"Question:\t'{question_text}'")
- match number_of_current_correct_answers:
- case 0:
- print("\nI couldn't determine any correct answers for sure.", end=" ")
- case 1:
- print(
- f"\nI see that answer #{list_of_answers.index(correct_answers[0]) + 1} is correct, "
- f"but there might be additional correct answers because you only got {grade:g} points out of {maximum_points:g}.",
- end=" ",
- )
- case _:
- correct_answer_indexes: list[int] = []
- for correct_answer in correct_answers:
- correct_answer_indexes.append(list_of_answers.index(correct_answer) + 1)
- print(
- f"\nI see that answers {correct_answer_indexes} are correct, "
- f"but this list may be incomplete because you only got {grade:g} points out of {maximum_points:g}.",
- end=" ",
- )
- print(f"The possible answers are:", end="\n\n")
- for j, answer in enumerate(list_of_answers):
- print(f"#{j + 1}\t{answer}")
- print()
- get_missing_correct_answers(correct_answers, list_of_answers, question_type)
+def question_already_exists(
+ existing_question: Question, new_question: Question
+) -> bool:
+ return existing_question == new_question
-def get_missing_correct_answers(
- correct_answers: list[Answer],
- list_of_answers: list[Answer],
- question_type: QuestionType,
+def add_answers_to_existing_question(
+ answers: set[Answer], existing_question: Question
) -> None:
- while True:
- get_input_for_missing_correct_answers(
- list_of_answers, correct_answers, question_type
- )
- for answer in list_of_answers:
- if answer.correct:
- return
- print("Error: no correct answers were provided!", end="\n\n")
+ existing_question.answers.update(answers)
-def get_input_for_missing_correct_answers(
- answers: list[Answer], correct_answers: list[Answer], question_type: QuestionType
-) -> None:
- while len(correct_answers) < len(answers):
- additional_correct_answer = input(
- f"Please enter a missing correct answer (if there are any remaining) then press Enter: "
- )
- if additional_correct_answer == "":
- break
- elif not additional_correct_answer.isdigit():
- print("Error: an integer was expected!", end="\n\n")
- continue
- elif int(additional_correct_answer) - 1 not in range(len(answers)):
- print(
- "Error: the number is out of the range of possible answers!", end="\n\n"
- )
- continue
- elif int(additional_correct_answer) in correct_answers:
- print(
- "Error: this answer is already in the list of correct answers!",
- end="\n\n",
- )
- continue
- answers[int(additional_correct_answer) - 1].correct = True
- if question_type == QuestionType.SingleChoice:
- break
+def get_if_has_illustration(
+ question: Tag, directory: Path, file: Path
+) -> StateOfIllustrations:
+ if question.find("img", class_="img-responsive") or question.find(
+ "img", role="presentation"
+ ):
+ return get_if_illustrations_available(directory, file)
+ else:
+ return StateOfIllustrations.Nil
-def prettify(text: str) -> str:
- text = strip_whitespaces(text)
- text = format_latex_as_wikitext(text)
- return text
+def get_if_illustrations_available(directory: Path, file: Path) -> StateOfIllustrations:
+ asset_folder = directory / f"{file.stem}_files"
+ if path.exists(asset_folder):
+ return StateOfIllustrations.YesAndAvailable
+ else:
+ return StateOfIllustrations.YesButUnavailable
-def strip_whitespaces(text: str) -> str:
- text = text.strip("., \n")
- text = sub(r" \n|\r\n|\s{2}", " ", text)
- return text
+@dispatch
+def format_latex_as_wikitext(latex: Tag) -> str:
+ wikitext = latex.text
+ mathjax = latex.find(class_="MathJax").find("span").text
+ wikitext = wikitext.replace(mathjax, "", 1)
+ wikitext = wikitext.replace(mathjax, f"")
+ return wikitext
-def get_correct_answers_if_provided(question: Tag) -> set[str | None]:
- tag = question.find("div", class_="rightanswer")
- correct_answers: set[str | None] = set()
-
- if tag:
- assert isinstance(tag, Tag)
- hint_text = prettify(tag.text)
- single_correct_answer_description_translations = [
- "A helyes válasz: ",
- "The correct answer is: ",
- ]
- for (
- correct_answer_description
- ) in single_correct_answer_description_translations:
- if correct_answer_description in hint_text:
- correct_answer = hint_text.removeprefix(correct_answer_description)
- correct_answers.add(correct_answer)
- return correct_answers
-
- multiple_correct_answer_description_translations = [
- "A helyes válaszok: ",
- "The correct answers are: ",
- ]
- for (
- correct_answer_description
- ) in multiple_correct_answer_description_translations:
- if correct_answer_description in hint_text:
- correct_answer_tags = tag.find_all("p")
- for correct_answer_tag in correct_answer_tags:
- correct_answer = correct_answer_tag.text
- prettified_answer = prettify(correct_answer)
- correct_answers.add(prettified_answer)
- return correct_answers
-
- if tag.find("img"):
- pass
- return correct_answers
-
- raise NotImplementedError(
- f"Correct answers could not be extracted from '{hint_text}'!"
- )
+@dispatch # type: ignore
+def format_latex_as_wikitext(latex: str) -> str:
+ if findall(latex_start_anchored := r"^\s?\\?\\\(\s?(\s?\\(?=\\))?", latex):
+ wikitext = sub(latex_start_anchored, "", wikitext)
+ return wikitext
def get_question_data(
@@ -255,6 +103,47 @@ def get_question_text(found_tag: Tag) -> str:
return text
+def get_question_type(question: Tag) -> QuestionType:
+ if question.find("input", type="radio"):
+ return QuestionType.SingleChoice
+ elif question.find("input", type="checkbox"):
+ return QuestionType.MultipleChoice
+ else:
+ raise NotImplementedError("Question type not implemented.")
+
+
+def get_grading_of_question(question: Tag) -> tuple[bool, float | None, float]:
+ correctly_answered: bool
+
+ found_tag = question.find("div", class_="grade")
+ assert isinstance(found_tag, Tag)
+
+ grading_text = found_tag.text
+ numbers_in_capture_groups: list[tuple[str, str]] = findall(
+ r"(\d+)([.,]\d+)?", grading_text
+ )
+ numbers = [
+ whole + fraction.replace(",", ".")
+ for whole, fraction in numbers_in_capture_groups
+ ]
+ grade: float | None = None
+ match len(numbers):
+ case 1:
+ maximum_points = float(numbers[0])
+ case 2:
+ grade = float(numbers[0])
+ maximum_points = float(numbers[1])
+ case _:
+ raise NotImplementedError(
+ f"{len(numbers)} grade numbers found in '{grading_text}'!"
+ )
+ if grade == maximum_points:
+ correctly_answered = True
+ else:
+ correctly_answered = False
+ return correctly_answered, grade, maximum_points
+
+
def get_element_illustration(
tag: Tag,
element_text: str,
@@ -345,63 +234,3 @@ def create_upload_filename(
upload_filename += f" – válaszlehetőség {random_hash}"
upload_filename += f" ({quiz_name}){extension}"
return upload_filename
-
-
-@dispatch
-def format_latex_as_wikitext(latex: Tag) -> str:
- wikitext = latex.text
- mathjax = latex.find(class_="MathJax").find("span").text
- wikitext = wikitext.replace(mathjax, "", 1)
- wikitext = wikitext.replace(mathjax, f"{unicode_to_latex(mathjax)}")
- return wikitext
-
-
-@dispatch # type: ignore
-def format_latex_as_wikitext(latex: str) -> str:
- if findall(latex_start_anchored := r"^\s?\\?\\\(\s?(\s?\\(?=\\))?", latex):
- wikitext = sub(latex_start_anchored, "", latex)
- else:
- latex_start = latex_start_anchored.replace(r"^\s?", "")
- wikitext = sub(latex_start, "", latex)
-
- if findall(latex_end_anchored := r"\s*\\?\\\)\s?$", wikitext):
- wikitext = sub(latex_end_anchored, "", wikitext)
- else:
- latex_end = latex_end_anchored.replace(r"\s?$", "")
- wikitext = sub(latex_end, "", wikitext)
- return wikitext
-
-
-def question_already_exists(
- existing_question: Question, new_question: Question
-) -> bool:
- return existing_question == new_question
-
-
-def add_answers_to_existing_question(
- answers: set[Answer], existing_question: Question
-) -> None:
- existing_question.answers.update(answers)
-
-
-def get_if_has_illustration(
- question: Tag, directory: Path, file: Path
-) -> StateOfIllustrations:
- if question.find("img", class_="img-responsive") or question.find(
- "img", role="presentation"
- ):
- return get_if_illustrations_available(directory, file)
- else:
- return StateOfIllustrations.Nil
-
-
-def get_if_illustrations_available(directory: Path, file: Path) -> StateOfIllustrations:
- asset_folder = directory / f"{file.stem}_files"
- if path.exists(asset_folder):
- return StateOfIllustrations.YesAndAvailable
- else:
- return StateOfIllustrations.YesButUnavailable
-
-
-def clear_terminal() -> None:
- system("clear||cls")
diff --git a/src/moodle_to_vikwikiquiz/quiz/questions/question.py b/src/moodle_to_vikwikiquiz/quiz/questions/question.py
index 800d99b..7d2f9b9 100644
--- a/src/moodle_to_vikwikiquiz/quiz/questions/question.py
+++ b/src/moodle_to_vikwikiquiz/quiz/questions/question.py
@@ -2,7 +2,7 @@
from typing_extensions import override
-from .answer import Answer # type: ignore
+from .answers.answer import Answer # type: ignore
from ..grading_types import GradingType # type: ignore
from ..illustrations.illustration import Illustration # type: ignore
from ..quiz_element import QuizElement # type: ignore
diff --git a/src/moodle_to_vikwikiquiz/quiz/quiz.py b/src/moodle_to_vikwikiquiz/quiz/quiz.py
index 69f75de..e08c0ce 100644
--- a/src/moodle_to_vikwikiquiz/quiz/quiz.py
+++ b/src/moodle_to_vikwikiquiz/quiz/quiz.py
@@ -13,14 +13,26 @@
# noinspection PyUnresolvedReferences
from bs4 import BeautifulSoup, Tag
-from .questions.answer import Answer # type: ignore
from .grading_types import GradingType # type: ignore
from .illustrations.illustration import Illustration # type: ignore
from .illustrations.state_of_illustrations import StateOfIllustrations # type: ignore
+from .questions.answers.answer import Answer # type: ignore
+from .questions.answers.helpers import answer_is_correct, get_correct_answers, get_correct_answers_if_provided # type: ignore
+from .questions.helpers import ( # type: ignore
+ add_answers_to_existing_question,
+ get_element_illustration, get_grading_of_question,
+ get_if_has_illustration,
+ get_if_illustrations_available,
+ get_question_data,
+ get_question_text,
+ get_question_type,
+ question_already_exists,
+)
from .questions.question_types import QuestionType # type: ignore
-from .quiz_helpers import * # type: ignore
+from .helpers import * # type: ignore
from .questions.question import Question # type: ignore
from .quiz_element import QuizElement # type: ignore
+from ..helpers import clear_terminal # type: ignore
def move_illustration_to_upload_folder(
diff --git a/src/moodle_to_vikwikiquiz/wiki.py b/src/moodle_to_vikwikiquiz/wiki.py
new file mode 100644
index 0000000..f524c11
--- /dev/null
+++ b/src/moodle_to_vikwikiquiz/wiki.py
@@ -0,0 +1,200 @@
+from argparse import Namespace
+import logging
+from platform import system
+from urllib.parse import quote, urlencode
+from webbrowser import open_new_tab
+
+from pyperclip import copy # type: ignore
+
+from .helpers import clear_terminal, remove_uploaded_files, wait_for_pastebot_to_recognize_copy # type: ignore
+from .quiz.illustrations.state_of_illustrations import StateOfIllustrations # type: ignore
+from .quiz.quiz import Quiz # type: ignore
+
+
+def create_article(
+ args: Namespace,
+ parameters_for_opening_edit: dict[str, str],
+ quiz_title: str,
+ quiz_wikitext: str,
+ wiki_domain: str,
+ wiki_modifier_keys: dict[str, str],
+ wiki_editor_keys: dict[str, str],
+ operating_system: str,
+) -> None:
+ if args.new:
+ parameters_for_opening_edit_with_paste = parameters_for_opening_edit.copy()
+ parameters_for_opening_edit_with_paste.update(
+ {
+ "preload": "Sablon:Előbetöltés",
+ "preloadparams[]": quiz_wikitext,
+ }
+ )
+ parameters_for_opening_edit_with_paste["summary"] = (
+ parameters_for_opening_edit_with_paste["summary"].replace(
+ "bővítése", "létrehozása"
+ )
+ )
+ url = f"{wiki_domain}/{quiz_title}?{urlencode(parameters_for_opening_edit_with_paste)}"
+ if len(url) < 2048:
+ return open_article_paste_text(args, quiz_wikitext, url)
+ else:
+ open_article(args, parameters_for_opening_edit, url)
+ else:
+ del parameters_for_opening_edit["preload"]
+ del parameters_for_opening_edit["preloadparams[]"]
+ copy(quiz_wikitext)
+ print("\nThe wikitext of the quiz has been copied to the clipboard!")
+ url = f"{wiki_domain}/{quote(quiz_title)}?{urlencode(parameters_for_opening_edit)}"
+ if not args.new:
+ print(
+ f"""
+The existing article will now be opened for editing. After that, please...
+• scroll to the bottom of the wikitext in the editor
+• add a new line
+• paste the content of the clipboard in that line
+• click on the 'Előnézet megtekintése' button ({wiki_modifier_keys[operating_system]}-{wiki_editor_keys["Show preview"]})
+• correct the spelling and formatting (if necessary), especially the formulas
+• click on the 'Lap mentése' button ({wiki_modifier_keys[operating_system]}-{wiki_editor_keys["Publish page"]})"""
+ )
+ input("\nPlease press Enter then follow these instructions...")
+ open_new_tab(url)
+ print(
+ "\nThe edit page of the quiz article has been opened in your browser!", end=" "
+ )
+ if args.new:
+ print("Please follow the instructions there.")
+
+
+def open_article_paste_text(args: Namespace, quiz_wikitext: str, url: str) -> None:
+ copy(quiz_wikitext)
+ print(
+ "\nThe wikitext of the quiz has been copied to the clipboard! "
+ "This will be overwritten but you may recall it later if you use an app like Pastebot."
+ )
+ wait_for_pastebot_to_recognize_copy()
+ if args.verbose:
+ copy(url)
+ print("The URL has been copied to the clipboard!")
+ open_new_tab(url)
+ print(
+ "\nThe edit page of the new quiz article has been opened in your browser with the wikitext pre-filled! "
+ "Please upload illustrations manually, if there are any."
+ )
+ return
+
+
+def open_article(
+ args: Namespace, parameters_for_opening_edit: dict[str, str], url: str
+) -> None:
+ logging.getLogger(__name__).warning(
+ "I can't create the article automatically "
+ "because the URL would be too long for some browsers (or the server)."
+ )
+ if args.verbose:
+ copy(url)
+ print(
+ "\nThis URL has been copied to the clipboard! "
+ "It will be overwritten but you may recall it later if you use an app like Pastebot."
+ )
+ wait_for_pastebot_to_recognize_copy()
+ parameters_for_opening_edit["summary"] = parameters_for_opening_edit[
+ "summary"
+ ].replace("bővítése", "létrehozása")
+
+
+def get_article_instructions(
+ quiz: Quiz, wiki_domain: str
+) -> tuple[str, dict[str, str], dict[str, str], dict[str, str]]:
+ wikitext_instructions = """
+"""
+ parameters_for_opening_edit = {
+ "action": "edit",
+ "summary": "Kvíz bővítése "
+ "a https://github.com/gy-mate/moodle-to-vikwikiquiz segítségével importált Moodle-kvíz(ek)ből",
+ "preload": "Sablon:Előbetöltés",
+ "preloadparams[]": wikitext_instructions,
+ }
+ clear_terminal()
+ return (
+ operating_system,
+ parameters_for_opening_edit,
+ wiki_editor_keys,
+ wiki_modifier_keys,
+ )
+
+
+def log_in_to_wiki(wiki_domain: str) -> None:
+ input(
+ """Let's log in to the wiki! Please...
+• if you see the login page, log in
+• when you see the main page of the wiki, return here.
+
+Please press Enter to open the login page..."""
+ )
+ open_new_tab(f"{wiki_domain}/index.php?title=Speciális:Belépés")
+ input("Please press Enter if you've logged in...")
+ clear_terminal()