<commit_before>##Some points to mention...
##
##The model knows nothing about the view or the controller.
##The view knows nothing about the controller or the model.
##The controller understands both the model and the view.
##
##The model uses observables, essentially when important data is changed,
##any interested listener gets notified through a callback mechanism.
##
##The following opens up two windows, one that reports how much money you
##have, and one that has two buttons, one to add money and one to remove
##money.
##
##The important thing is that the controller is set up to monitor changes
##in the model.  In this case the controller notices that you clicked a
##button and modifies the money in the model which then sends out a
##message that it has changed.  The controller notices this and updates
##the widgets.
##
##The cool thing is that anything modifying the model will notify the
##controller.  In this case it is the controller modifying the model, but it
##could be anything else, even another controller off in the distance
##looking at something else.
##
##The main idea is that you give a controller the model and view that it
##needs, but the model's can be shared between controllers so that when
##the model is updated, all associated views are updated. -<NAME>
##
## following is a Tkinter approximation of the original example.

import tkinter as tk
from tkinter.filedialog import asksaveasfilename
import os
from tksimpledialog import Dialog
from amcquestion import AMCQuestion
from amc_question_dialogs import CreateQuestionDialog, EditQuestionDialog


class Observable:
    '''A mutable element of the model that can notify the controller when
    modified
    '''
    def __init__(self, initialValue=None):
        self.data = initialValue
        self.callbacks = {}

    def addCallback(self, func):
        self.callbacks[func] = 1

    def delCallback(self, func):
        del self.callback[func]

    def _docallbacks(self):
        for func in self.callbacks:
             func(self.data)

    def set(self, data):
        self.data = data
        self._docallbacks()

    def get(self):
        return self.data

    def unset(self):
        self.data = None


class Test:
    def __init__(self):
        self.string = ""
        self.indent = 0

    def newLine(self, newlines, string):
        self.string += "\n"*newlines + "  "*self.indent + string

    def increaseIndent(self):
        self.indent += 1

    def decreaseIndent(self):
        self.indent = max(0, self.indent - 1)


def sanitizeLaTeX(s):
    chars_to_escape = ['#', '$', '%', '&', '_', '^', '{', '}']
    segments = s.split('\\')
    for n in range(len(segments)):
        for char in chars_to_escape:
            segments[n] = segments[n].replace(char, '\\' + char)
        segments[n] = segments[n].replace('~', '\\textasciitilde{}')
    sanitized = '\\textbackslash{}'.join(segments)

    return sanitized


class Model:
    def __init__(self):
        self.myQuestions = Observable([])
        self.saveFile = Observable(os.getcwd() + "/amc_questions_output.tex")
        self.papertypes = ["letterpaper", "a4paper"]
        self.papertype = self.papertypes[0]

    def addQuestion(self, question):
        self.myQuestions.set(self.myQuestions.get() +
            [question])

    def removeQuestion(self, index):
        questions = self.myQuestions.get()
        del questions[index]
        self.myQuestions.set(questions)

    def changeQuestion(self, index, question):
        questions = self.myQuestions.get()
        questions[index] = question
        self.myQuestions.set(questions)

    def setFile(self, filename):
        self.saveFile.set(filename)

    def newLine(newlines, indent, string):
        return "\n"*newlines + "  "*indent + string

    def writeTest(self):
        test = Test()
        test.newLine(0, "%Created with AMC Question Creator")
        papertype = self.papertypes[0]
        test.newLine(1, "\\documentclass[" + papertype + "]{article}")

        test.newLine(2, "\\usepackage[utf8x]{input}")
        test.newLine(1, "\\usepackage[T1]{fontenc}")

        # Select options for the AMC package
        options = ""
        box = True
        completemulti = True
        separateanswersheet = True
        if box:
            options += ",box"
        if completemulti:
            options += ",completemulti"
        if separateanswersheet:
            options += ",separateanswersheet"
        options = options[1:] # Remove leading comma

        test.newLine(2,
            "\\usepackage[" + options + "]{automultiplechoice}")

        test.newLine(2, "\\begin{document}")

        # Imagine this must seed a pseudorandom number generator. Am not
        # randomizing questions right now, so will not use.
        random_seed = ""
        # random_seed = "1237893"
        if len(random_seed) > 0:
            test.newLine(2, indent, "\\AMCrandomseed{" + random_seed + "}")

        # Redefine how questions are defined on answer sheet
        form_question_format = "\\vspace{AMCformVSpace}\\par " + \
            "{\\sc Question #1:} "
        if len(form_question_format) > 0:
            test.newLine(2,
                "\n\n\\def\\AMCformQuestion#1{" + form_question_format + "}")

        copies = 10
        test.newLine(2, "\\onecopy{" + str(copies) + "}{")

        test.newLine(2, "%%% beginning of the test sheet header:")

        title = "QCM"
        test.newLine(2, "\\noindent{\\bf " + sanitizeLaTeX(title) +
            " \\hfill TEST}")

        date = "Mar. 26, 2014"
        test.newLine(2, "\\vspace*{.5cm}")
        test.newLine(1, "\\begin{minipage}{.4\\linewidth}")
        test.increaseIndent()
        test.newLine(1, "\\centering\\large\\bf Test\\\\ Examination on" +
            sanitizeLaTeX(date))
        test.decreaseIndent()
        test.newLine(1, "\\end{minipage}")

        # Write the questions themselves
        questions = self.myQuestions.get()
        for question in questions:
            test.newLine(2, "\\begin{question}{" + question.get_label() + "}")
            test.increaseIndent()
            test.newLine(1, sanitizeLaTeX(question.get_question()))<commit_msg>Commented sanitizeLaTeX and added missing space before test date.<commit_after>##Some points to mention...
##
##The model knows nothing about the view or the controller.
##The view knows nothing about the controller or the model.
##The controller understands both the model and the view.
##
##The model uses observables, essentially when important data is changed,
##any interested listener gets notified through a callback mechanism.
##
##The following opens up two windows, one that reports how much money you
##have, and one that has two buttons, one to add money and one to remove
##money.
##
##The important thing is that the controller is set up to monitor changes
##in the model.  In this case the controller notices that you clicked a
##button and modifies the money in the model which then sends out a
##message that it has changed.  The controller notices this and updates
##the widgets.
##
##The cool thing is that anything modifying the model will notify the
##controller.  In this case it is the controller modifying the model, but it
##could be anything else, even another controller off in the distance
##looking at something else.
##
##The main idea is that you give a controller the model and view that it
##needs, but the model's can be shared between controllers so that when
##the model is updated, all associated views are updated. -<NAME>
##
## following is a Tkinter approximation of the original example.

import tkinter as tk
from tkinter.filedialog import asksaveasfilename
import os
from tksimpledialog import Dialog
from amcquestion import AMCQuestion
from amc_question_dialogs import CreateQuestionDialog, EditQuestionDialog


class Observable:
    '''A mutable element of the model that can notify the controller when
    modified
    '''
    def __init__(self, initialValue=None):
        self.data = initialValue
        self.callbacks = {}

    def addCallback(self, func):
        self.callbacks[func] = 1

    def delCallback(self, func):
        del self.callback[func]

    def _docallbacks(self):
        for func in self.callbacks:
             func(self.data)

    def set(self, data):
        self.data = data
        self._docallbacks()

    def get(self):
        return self.data

    def unset(self):
        self.data = None


class Test:
    def __init__(self):
        self.string = ""
        self.indent = 0

    def newLine(self, newlines, string):
        self.string += "\n"*newlines + "  "*self.indent + string

    def increaseIndent(self):
        self.indent += 1

    def decreaseIndent(self):
        self.indent = max(0, self.indent - 1)


def sanitizeLaTeX(s):
    """Converts LaTeX control characters from text input to the appropriate
    LaTeX commands (usually involves escaping with backslash).

    """

    # Characters to escape with a backslash
    chars_to_escape = ['#', '$', '%', '&', '_', '^', '{', '}']

    # Split the string at literal backslashes to avoid problem of replacing the
    # backslashes inserted later as part of escape sequences.
    segments = s.split('\\')
    for n in range(len(segments)):
        for char in chars_to_escape:
            segments[n] = segments[n].replace(char, '\\' + char)

        # Replace characters that require more than simple backslash escapes.
        segments[n] = segments[n].replace('~', '\\textasciitilde{}')

    # Replace the literal backslashes used to split the string with proper
    # escape sequence.
    sanitized = '\\textbackslash{}'.join(segments)

    return sanitized


class Model:
    def __init__(self):
        self.myQuestions = Observable([])
        self.saveFile = Observable(os.getcwd() + "/amc_questions_output.tex")
        self.papertypes = ["letterpaper", "a4paper"]
        self.papertype = self.papertypes[0]

    def addQuestion(self, question):
        self.myQuestions.set(self.myQuestions.get() +
            [question])

    def removeQuestion(self, index):
        questions = self.myQuestions.get()
        del questions[index]
        self.myQuestions.set(questions)

    def changeQuestion(self, index, question):
        questions = self.myQuestions.get()
        questions[index] = question
        self.myQuestions.set(questions)

    def setFile(self, filename):
        self.saveFile.set(filename)

    def newLine(newlines, indent, string):
        return "\n"*newlines + "  "*indent + string

    def writeTest(self):
        test = Test()
        test.newLine(0, "%Created with AMC Question Creator")
        papertype = self.papertypes[0]
        test.newLine(1, "\\documentclass[" + papertype + "]{article}")

        test.newLine(2, "\\usepackage[utf8x]{input}")
        test.newLine(1, "\\usepackage[T1]{fontenc}")

        # Select options for the AMC package
        options = ""
        box = True
        completemulti = True
        separateanswersheet = True
        if box:
            options += ",box"
        if completemulti:
            options += ",completemulti"
        if separateanswersheet:
            options += ",separateanswersheet"
        options = options[1:] # Remove leading comma

        test.newLine(2,
            "\\usepackage[" + options + "]{automultiplechoice}")

        test.newLine(2, "\\begin{document}")

        # Imagine this must seed a pseudorandom number generator. Am not
        # randomizing questions right now, so will not use.
        random_seed = ""
        # random_seed = "1237893"
        if len(random_seed) > 0:
            test.newLine(2, indent, "\\AMCrandomseed{" + random_seed + "}")

        # Redefine how questions are defined on answer sheet
        form_question_format = "\\vspace{AMCformVSpace}\\par " + \
            "{\\sc Question #1:} "
        if len(form_question_format) > 0:
            test.newLine(2,
                "\n\n\\def\\AMCformQuestion#1{" + form_question_format + "}")

        copies = 10
        test.newLine(2, "\\onecopy{" + str(copies) + "}{")

        test.newLine(2, "%%% beginning of the test sheet header:")

        title = "QCM"
        test.newLine(2, "\\noindent{\\bf " + sanitizeLaTeX(title) +
            " \\hfill TEST}")

        date = "Mar. 26, 2014"
        test.newLine(2, "\\vspace*{.5cm}")
        test.newLine(1, "\\begin{minipage}{.4\\linewidth}")
        test.increaseIndent()
        test.newLine(1, "\\centering\\large\\bf Test\\\\ Examination on " +
            sanitizeLaTeX(date))
        test.decreaseIndent()
        test.newLine(1, "\\end{minipage}")

        # Write the questions themselves
        questions = self.myQuestions.get()
        for question in questions:
            test.newLine(2, "\\begin{question}{" + question.get_label() + "}")
            test.increaseIndent()
            test.newLine(1, sanitizeLaTeX(question.get_question()))
{'fraction_non_alphanumeric': 0.11499497331992885, 'fraction_numerical': 0.007578686876498338, 'mean_word_length': 2.663456090651558, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '699507', 'n_tokens_mistral': 3804, 'n_tokens_neox': 3629, 'n_words': 1164}