Exemplo n.º 1
0
class Slideshow(Model):
    """Slideshow model

    This model aims to represent a slideshow.

    Attributes
    ----------
    title : str
        Title of the whole slideshow
    authors :
        Authors (format to be determined)
    children : list
        Slides exactly as they are stored in the database
        (i.e. without the solutions or the fields' data).
    """

    _collection = "slideshows"

    title = fields.Field("Title")
    authors = fields.Field("Authors")
    children = []

    @fields.computed("Slug", field=fields.Field)
    def slug(self):
        return slugify.slugify(self.title) if self.title else None
Exemplo n.º 2
0
class Vote(Model):

    _collection = "votes"

    user = fields.Field("User")
    survey = fields.Field("Survey name")
    value = fields.Field("Value", default=False)
    edit_count = fields.Field("Count")
class FactoriseQuestion(form.Form):
    """Factorisation Question"""

    name = fields.Field("Survey name")
    answer = fields.Expression("Your answer", nosave=True, editable=True)
    expression = fields.Expression("Expression", required=True)

    template = """
        <div>Factorise `expression`</div>
        <survey
            :config="config"
            :name="payload.name"
            :showStats="config.authState.loggedIn"
            :correct="computed.correct"
            :value="payload.answer">
            Your answer: `answer`
        </survey>
    """

    @fields.computed("Correct", field=fields.Boolean)
    def correct(self):
        if (not self.answer
                or sympy.simplify(self.answer - self.expression) != 0
                or self.answer.func != sympy.Mul):
            return False
        for term in self.answer.args:
            if sympy.factor(term).func == sympy.Mul:
                return False
        return True

    def validate(self):
        self.expression = sympy.expand(self.expression)
        if not self.name:
            self.name = str(uuid.uuid1())
class SimplificationQuestion(form.Form):
    """Simplification Question"""

    name = fields.Field("Survey name")
    answer = fields.Expression("Your answer", nosave=True, editable=True)
    expression = fields.Expression("Expression")

    template = """
        <div>Simplify `expression`</div>
        <survey
            :config="config"
            :name="payload.name"
            :showStats="config.authState.loggedIn"
            :correct="computed.correct"
            :value="payload.answer">
            Your answer: `answer`
        </survey>
    """

    @fields.computed("Correct", field=fields.Boolean)
    def correct(self):
        if not self.answer:
            return False
        return str(self.answer) == str(sympy.simplify(self.expression))

    def validate(self):
        if not self.name:
            self.name = str(uuid.uuid1())
Exemplo n.º 5
0
class Youtube(form.Form):
    """YouTube"""

    name = "YouTube"
    url = fields.Field("URL")
    zoom = fields.Expression("Zoom", default="1.0")
    template = """
        <div v-if="config.edit">
            URL: `url`
            Zoom: `zoom`
        </div>
        `video`
    """

    @property
    def height(self):
        return 315 * self.zoom

    @property
    def width(self):
        return 560 * self.zoom

    @fields.computed("Youtube Video", field=fields.Html, nohide=True)
    def video(self):
        """Get HTML code of embedded YouTube video"""
        url = self.url.replace("watch?v=", "embed/")
        return f"""
Exemplo n.º 6
0
class Survey(form.Form):
    """Marked question"""

    name = fields.Field("Survey name")
    answer = fields.Expression("Your answer", nosave=True, editable=True)
    correct_answer = fields.Expression("Correct Answer", required=True)
    max_error = fields.Expression("Maximal error", default=0)

    template = """
        <p v-if="config.edit">
            Correct answer: `correct_answer`
            Tolerated error: `max_error`
        </p>
        <survey
            :name="payload.name"
            :showStats="config.authState.loggedIn"
            :correct="computed.correct"
            :value="payload.answer">
            `answer`
        </survey>
    """

    @fields.computed("Correct", field=fields.Boolean)
    def correct(self):
        if self.answer is None:
            return False
        return (
            sympy.Abs(sympy.simplify(self.answer - self.correct_answer))
            <= self.max_error
        )

    def validate(self):
        if not self.name:
            self.name = str(uuid.uuid1())
Exemplo n.º 7
0
class CustomForm(form.Form):
    template = "Solve `equation`"
    equation = fields.Equation("Equation", default="x^3")
    required = fields.Field("Required field", required=True)

    def validate(self):
        if str(self.equation) != "Eq(x**2, 0)":
            raise AttributeError("Wrong equation")
Exemplo n.º 8
0
class Survey(form.Form):
    """Marked question"""

    name = fields.Field("Survey name")
    answer = fields.Expression("Your answer", nosave=True, editable=True)
    correct_answer = fields.Expression("Correct Answer", required=True)
    max_error = fields.Expression("Maximal error", default=0)
    before = fields.Markdown("Before field text", default="Your answer:")
    after = fields.Markdown("After field text")
    question = fields.Markdown("Question")
    marking_type = fields.Select(
        "Marking type",
        options=["Default", "Numerical", "Fraction"],
        default="Default",
    )

    template = """
        <p v-if="config.edit || payload.question">`question`</p>
        <widget-settings v-if="config.edit">
            <config-option name="Correct Answer">`correct_answer`</config-option>
            <config-option name="Tolerated error">`max_error`</config-option>
            <config-option name="Marking type">`marking_type`</config-option>
        </widget-settings>
        <survey
            :config="config"
            :name="payload.name"
            :showStats="config.authState.loggedIn"
            :correct="computed.correct"
            :value="payload.answer">
            <span v-if="config.edit || payload.before">`before`</span>
            `answer`
            <span v-if="config.edit || payload.after">`after`</span>
        </survey>
    """

    _total_marks = 1

    @property
    def _marks(self):
        return 1 if self.correct else 0

    @fields.computed("Correct", field=fields.Boolean)
    def correct(self):
        if self.answer is None:
            return False
        elif self.marking_type == "Numerical":
            numbers = sympy.core.numbers
            if not self.answer.func in [numbers.Float, numbers.Integer]:
                return False
        elif self.marking_type == "Fraction":
            if not re.search(r"^[0-9\s/]*$", self._answer):
                return False
        return (sympy.Abs(sympy.nsimplify(self.answer - self.correct_answer))
                <= self.max_error)

    def validate(self):
        if not self.name:
            self.name = str(uuid.uuid1())
Exemplo n.º 9
0
class Foo(form.Form):
    field = fields.Field("Field", desc="description")
    equation = fields.Equation("Equation")
    real = fields.Expression("Real number")
    markdown = fields.Markdown("Markdown")
    matrix = fields.Matrix("Matrix")
    email = fields.Email("Email")
    password = fields.Password("Email")
    protected = fields.Field("Protected", default="Protected", protected=True)
    select = fields.Select("Select", options=["Hi", "Hello"])

    @fields.constraint("Contraint", default=True)
    def constraint(self):
        return self.real > 0

    @fields.computed("Computed")
    def computed_field(self):
        return self.field
Exemplo n.º 10
0
class Geogebra(form.Form):
    """Geogebra"""

    name = "Geogebra"
    url = fields.Field("URL", required=True)
    width = fields.Field("Width", default=800)
    height = fields.Field("Width", default=600)
    template = """
        <div v-if="config.edit">
            `url` (`width`x`height`)
        </div>
        `geogebra`
    """

    @fields.computed("Geogebra", field=fields.Html, nohide=True)
    def geogebra(self):
        """Get HTML code of embedded YouTube video"""
        url = self.url.split("/")[-1]
        url = f"https://www.geogebra.org/material/iframe/id/{url}"
        url += f"/width/{self.width}/height/{self.height}"
        url += "/ai/false/smb/false/stb/false"
        return f"""
Exemplo n.º 11
0
class Widget(form.Form):

    a = fields.RandomNumber("a")
    b = fields.RandomNumber("b")

    field = fields.Field("Field")

    @fields.constraint("Constraint")
    def constraint(self):
        return self.a == self.b

    @fields.range_constraint("Range constraint")
    def range_constraint(self):
        self.a = sympy.symbols("a b c")

    def generator(self):
        self.field = sympy.Eq(self.a, self.b, evaluate=False)
Exemplo n.º 12
0
class Youtube(form.Form):
    """YouTube"""

    name = "YouTube"
    url = fields.Field("URL")
    template = """
        <div v-if="config.edit">
            `url`
        </div>
        `video`
    """

    @fields.computed("Youtube Video", field=fields.Html, nohide=True)
    def video(self):
        """Get HTML code of embedded YouTube video"""
        url = self.url.replace("watch?v=", "embed/")
        return f"""
Exemplo n.º 13
0
class Image(form.Form):
    """Image"""

    src = fields.Field("Image URL", required=True)
    height = fields.Expression("Original height", default=0)
    width = fields.Expression("Original width", default=0)
    zoom = fields.Expression("Zoom", default=1)
    position = fields.Select("Position",
                             options=["left", "right", "center"],
                             default="center")

    template = """
        <widget-settings v-if="config.edit">
            <config-option name="Zoom">`zoom`</config-option>
            <config-option name="Position">`position`</config-option>
        </widget-settings>
        `image`
    """

    @fields.computed("Image", field=fields.Html, nohide=True)
    def image(self):
        style = f"float:{self.position}" if self.position != "center" else ""
        return f"""
            <img src="{self.src}"
                width="{self.zoom * self.width}"
                style="{style}"
                height="{self.zoom * self.height}"/>"""

    def validate(self):
        if "," in self.src:
            data = self.src.split(",")
            image_hash = hashlib.sha1(data[1].encode("utf-8")).hexdigest()
            ext = mimetypes.guess_extension(data[0][5:data[0].find(";")])
            folder = os.environ.get("STORAGE", "./storage/")
            filename = image_hash + ext
            with open(folder + filename, "wb") as f:
                image = base64.b64decode(data[1])
                f.write(image)
            self.src = "/storage/" + filename
        if not self.width:
            path = os.environ.get("STORAGE", "./storage/") + self.src[9:]
            self.width, self.height = PIL.Image.open(path).size
            self.zoom = 1
Exemplo n.º 14
0
class Image(form.Form):
    """Image"""

    src = fields.Field("Image URL", required=True)
    height = fields.Expression("Original height", default=0)
    width = fields.Expression("Original width", default=0)
    zoom = fields.Expression("Zoom", default=1)

    template = """
        <div v-if="config.edit">
            Zoom: `zoom`
        </div>
        <div>
            `image`
        </div>
    """

    @fields.computed("Image", field=fields.Html, nohide=True)
    def image(self):
        return (f"""<img src="{self.src}" """ +
                f"""width="{self.zoom * self.width}" """ +
                f"""height="{self.zoom * self.height}"/>""")

    def validate(self):
        if "," in self.src:
            data = self.src.split(",")
            image_hash = hashlib.sha1(data[1].encode("utf-8")).hexdigest()
            ext = mimetypes.guess_extension(data[0][5:data[0].find(";")])
            folder = os.environ.get("STORAGE", "./storage/")
            filename = image_hash + ext
            with open(folder + filename, "wb") as f:
                image = base64.b64decode(data[1])
                f.write(image)
            self.src = "/storage/" + filename
        if not self.width:
            path = os.environ.get("STORAGE", "./storage/") + self.src[9:]
            self.width, self.height = PIL.Image.open(path).size
            self.zoom = 1
class StandardFormQuestion(form.Form):
    """Standard form question"""

    name = fields.Field("Survey name")
    answer = fields.Expression("Your answer", nosave=True, editable=True)
    expression = fields.Expression("Expression")

    template = """
        <p>Convert `expression` to standard form</p>
        <survey
            :config="config"
            :name="payload.name"
            :showStats="config.authState.loggedIn"
            :correct="computed.correct"
            :value="payload.answer">
            `answer`
        </survey>
    """

    @fields.computed("Correct", field=fields.Boolean)
    def correct(self):
        if not self.answer:
            return False
        if self.answer.func == sympy.Pow:
            if self.answer.args[0] != 10:
                return False
        else:
            if self.answer.func != sympy.Mul or len(self.answer.args) != 2:
                return False
            x, power = self.answer.args[0], self.answer.args[1]
            if (x not in sympy.Interval.Ropen(1, 10) or power.func != sympy.Pow
                    or power.args[0] != 10):
                return False
        return sympy.simplify(self.answer - self.expression) == 0

    def validate(self):
        if not self.name:
            self.name = str(uuid.uuid1())
Exemplo n.º 16
0
class Triangle(form.Form):
    """Draw a triangle"""

    template = """
        Vertices labels: `A`, `B`, `C`<br>
        Lengths: `a`, `b`, `c`<br>
        Angles: `alpha`, `beta`, `gamma`<br>
    """

    A = fields.Expression("A")
    B = fields.Expression("B")
    C = fields.Expression("C")

    a = fields.Expression("a")
    b = fields.Expression("b")
    c = fields.Expression("c")

    alpha = fields.Expression("alpha")
    beta = fields.Expression("beta")
    gamma = fields.Expression("gamma")
    obtuse = fields.Field("Obtuse (sine law ambiguity)", default=False)

    @fields.computed("Triangle", field=fields.Html)
    @fields.figure
    def triangle(self):
        # Prepare figure
        pyplot.axis("off")
        pyplot.grid(b=None)
        pyplot.gca().set_aspect("equal")
        # Draw triangle
        x, y = zip(*(self.vertices + [numpy.array([0, 0])]))
        pyplot.plot(x, y)
        # Labelling
        m = numpy.sum(self.vertices, axis=0) / 3
        edges = zip(self.vertices, self.vertices[1:] + self.vertices[:1])
        positions = (self.vertices + [numpy.sum(e, axis=0) / 2
                                      for e in edges] + self.vertices)
        labels = [
            getattr(self, attr) for attr in
            ["A", "B", "C", "a", "b", "c", "beta", "gamma", "alpha"]
        ]
        signs = [1 if i < 6 else -1 for i in range(9)]
        for label, position, sign in zip(labels, positions, signs):
            if label:
                label = fr"${sympy.latex(label)}$"
                direction = (position - m) / numpy.linalg.norm(position - m)
                pyplot.text(*(position + 0.3 * sign * direction),
                            label,
                            fontsize=13)

    @property
    def vertices(self):
        lengths = [self.a, self.b, self.c]
        angles = [self.alpha, self.beta, self.gamma]

        def missing(expressions):
            return [
                i for i, e in enumerate(expressions)
                if not e or getattr(e, "func", "") == sympy.Symbol
            ]

        missing_quantities = len(missing(lengths + angles))

        def cosine_law(lengths, angles, index):
            (c, a, b), gamma = lengths[index:] + lengths[:index], angles[index]
            x, find_angle = sympy.Dummy("x"), not bool(gamma)
            gamma, c = gamma or x, c or x
            equation = sympy.Eq(c**2,
                                a**2 + b**2 - 2 * a * b * sympy.cos(gamma))
            domain = sympy.Interval.open(0,
                                         sympy.pi if find_angle else sympy.oo)
            sol = [x for x in sympy.solve(equation) if x in domain]
            if not sol:
                raise ValueError(
                    "The lengths and angles must satisfy the law of cosines")
            locals()["angles" if find_angle else "lengths"][index] = sol[0]

        def sine_law(lengths, angles, i, j):
            a, b, alpha, beta = lengths[i], lengths[j], angles[i], angles[j]
            x, find_angle = sympy.Dummy("x"), not bool(beta)
            b, beta = b or x, beta or x
            equation = sympy.Eq(sympy.sin(alpha) / a, sympy.sin(beta) / b)
            domain = sympy.Interval.open(0,
                                         sympy.pi if find_angle else sympy.oo)
            sol = [x for x in sympy.solve(equation) if x in domain]
            if not sol:
                raise ValueError(
                    "The lengths and angles must satisfy the law of sines")
            sol = [
                x for x in sol if x +
                sum([a for a in angles if a and not a.free_symbols]) < sympy.pi
            ]
            sol = sol[1] if self.obtuse and len(sol) == 2 else sol[0]
            locals()["angles" if find_angle else "lengths"][j] = sol

        while missing_quantities:
            missing_info = [len(missing(el)) for el in zip(lengths, angles)]
            if len(missing(angles)) == 1:
                angle = sympy.pi - sum(
                    [a for a in angles if a and not a.free_symbols])
                if angle not in sympy.Interval.open(0, sympy.pi):
                    raise ValueError(
                        "The angles sum cannot exceed 180 degrees")
                angles[missing(angles)[0]] = angle
            elif not missing(lengths) and missing(angles):
                cosine_law(lengths, angles, missing(angles)[0])
            elif len(missing(lengths)) == 1 and missing(
                    lengths)[0] not in missing(angles):
                cosine_law(lengths, angles, missing(lengths)[0])
            elif {0, 1} <= set(missing_info):
                sine_law(lengths, angles, missing_info.index(0),
                         missing_info.index(1))
            else:
                raise ValueError("Not enough information to find the vertices")
            missing_quantities -= 1

        equations = [
            sympy.Eq(
                (lengths[2] * sympy.cos(angles[1])).evalf(5),
                (lengths[0] - lengths[1] * sympy.cos(angles[2])).evalf(5),
            ),
            sympy.Eq(
                (lengths[2] * sympy.sin(angles[1])).evalf(5),
                (lengths[1] * sympy.sin(angles[2])).evalf(5),
            ),
            sympy.Eq(sum(angles).evalf(5), sympy.pi.evalf(5)),
        ]
        if not all(equations):
            raise ValueError(
                "There are no triangles satisfying the conditions")
        if self.obtuse and not [a for a in angles if a > sympy.pi / 2]:
            raise ValueError("Could not find an appropriate obtuse triangle")

        return [
            numpy.array([0, 0]),
            numpy.array([lengths[0].evalf(), 0], dtype=numpy.float64),
            numpy.array(
                [
                    (lengths[2] * sympy.cos(angles[1])).evalf(),
                    (lengths[2] * sympy.sin(angles[1])).evalf(),
                ],
                dtype=numpy.float64,
            ),
        ]
Exemplo n.º 17
0
class MultipleChoice(form.Form):
    """Multiple Choice"""

    name = fields.Field("Survey name")
    answer = fields.Select("Your answer",
                           nosave=True,
                           options=["", "A", "B", "C", "D", "E"])
    correct_answer = fields.Select("Correct Answer",
                                   options=["A", "B", "C", "D", "E"],
                                   default="A")
    question = fields.Markdown("Question")

    option_a = fields.Markdown("Option A")
    option_b = fields.Markdown("Option B")
    option_c = fields.Markdown("Option C")
    option_d = fields.Markdown("Option D")
    option_e = fields.Markdown("Option E")

    @property
    def template(self):
        buttons = []
        for ltr in ["a", "b", "c", "d", "e"]:
            buttons.append(f"""
                <span v-if="!config.edit">
                    <span v-if="payload.option_{ltr}">
                        <b-button @click="$set(payload, 'answer', '{ltr.upper()}')"
                            type="is-primary"
                            v-if="'{ltr.upper()}' === payload.answer">
                            `option_{ltr}`
                        </b-button>
                        <b-button @click="$set(payload, 'answer', '{ltr.upper()}')" v-else>
                            `option_{ltr}`
                        </b-button>
                    </span>
                </span>
                <span v-else>
                    <b-button>`option_{ltr}`</b-button>
                </span>
            """)
        return f"""
            <p v-if="config.edit || payload.question">`question`</p>
            <widget-settings v-if="config.edit">
                <config-option name="Correct Answer">`correct_answer`</config-option>
            </widget-settings>
            <survey
                :config="config"
                :name="payload.name"
                :showStats="config.authState.loggedIn"
                :correct="computed.correct"
                :max-attempts="1"
                :value="payload.answer">
                <span class="buttons are-large">
                    {"".join(buttons)}
                </span>
            </survey>
    """

    @property
    def _marks(self):
        return 1 if self.correct else 0

    @fields.computed("Correct", field=fields.Boolean)
    def correct(self):
        return self.answer == self.correct_answer

    def validate(self):
        if not self.name:
            self.name = str(uuid.uuid1())
Exemplo n.º 18
0
class Identity(Model):
    _collection = "identity"

    username = fields.Field("Username")
    nickname = fields.Field("Nickname")
Exemplo n.º 19
0
class Form(Model):
    _collection = "forms"

    email = fields.Field("Email adress")
    url = fields.Field("Resource URL")
Exemplo n.º 20
0
class Equation(form.Form):
    """Equation"""

    name = fields.Field("Survey name")
    answer = fields.ExpressionList("Your answer", nosave=True, editable=True)
    equation = fields.Equation("Equation", required=True)
    x = fields.Expression("Solve for", default="x", required=True)
    template = """
        Solve `equation` <span v-if="config.edit || payload.x !== 'x'">for `x`</span>
        <div v-if="!payload.marked_question">`solution`</div>
        <div v-else>
            <survey
                :config="config"
                :name="payload.name"
                :showStats="config.authState.loggedIn"
                :correct="computed.correct"
                :value="payload.answer">
                Solution(s): `x` = `answer`
            </survey>
        </div>
        <div v-if="config.edit">
            `show_graph`
            `marked_question`
        </div>
        <div v-if="payload.show_graph">`graph`</div>
    """
    show_graph = fields.Boolean("Show graph", default=False)
    marked_question = fields.Boolean("Marked question", default=False)
    h = fields.Expression("h", default="3")

    @fields.computed("Correct", field=fields.Boolean)
    def correct(self):
        if not self.answer:
            return False
        # Prevent user from just copying the equation
        for sol in self.answer:
            if len(sol.atoms(sympy.Symbol)):
                return False
        solution = sympy.solveset(self.equation, self.x)
        sol_count = len(solution.args)
        return len(solution.intersect(
            sympy.FiniteSet(*self.answer)).args) >= sol_count

    @fields.computed("Solution")
    def solution(self):
        answer = sympy.solveset(self.equation, self.x)
        if answer.func == sympy.FiniteSet and len(answer.args) <= 3:
            answer = [
                sympy.Eq(self.x, answer.args[i])
                for i in range(len(answer.args))
            ]
        return answer

    @fields.computed("Plot", field=fields.Html, nohide=True)
    def graph(self):
        if not self.show_graph:
            return ""
        args = {"functions": [self.equation.args[0], self.equation.args[1]]}
        if isinstance(self.solution, list) and len(self.solution) in [1, 2]:
            if len(self.solution) == 1:
                solution = self.solution[0].args[1]
            if len(self.solution) == 2:
                x1, x2 = self.solution[0].args[1], self.solution[1].args[1]
                solution = (x1 + x2) / 2
                self.h = x2 - solution + 2
            args.update({
                "x_min": solution - self.h,
                "x_max": solution + self.h
            })
        return plot.Plot(**args).plot

    def validate(self):
        if self.marked_question:
            if self.show_graph:
                self.show_graph = False
            if not self.name:
                self.name = str(uuid.uuid1())