class Tuxie(form.Form): """Tuxie says""" text = fields.Markdown("Text") width = fields.Expression("Tuxie width", default="400") image = fields.Select("Image", options=["tuxie", "jigglypuff", "pikachu"], default="tuxie") position = fields.Select("Bubble position", options=["left", "right"], default="right") template = """
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())
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
class FractionOperations(form.Form): """Fractions""" template = "Calculate `fraction_1` `operation` `fraction_2` `solution`" fraction_1 = fields.Expression("fraction 1", required=True) fraction_2 = fields.Expression("fraction 2", required=True) operation = fields.Select("operation", options=["+", "-", "×", "÷"], default="+") @fields.computed("Solution") def solution(self): operations = { "+": sympy.Add, "-": lambda a, b: a - b, "×": sympy.Mul, "÷": lambda a, b: a / b, } if self.operation: return sympy.simplify(operations[self.operation](self.fraction_1, self.fraction_2)) a = fields.RandomNumber("a") b = fields.RandomNumber("b") c = fields.RandomNumber("c") d = fields.RandomNumber("d") op = fields.RandomNumber("op", default=["+", "-", "×", "÷"]) def generator(self): self.fraction_1 = self.a / self.b self.fraction_2 = self.c / self.d self.operation = self.op @fields.range_constraint("non-zero numbers", default=True, hidden=True, protected=True) def non_zero_numbers(self): self.a -= {0} self.b -= {0} self.c -= {0} self.d -= {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
class CircleEquation(form.Form): """Circle radius and center""" template = "Find the `info` of the circle `equation` `solution`" equation = fields.Equation("Equation", required=True) info = fields.Select("Info", options=["radius", "center"]) @fields.computed("Radius/Center") def solution(self): (a, h, k, x, y), r = ( sympy.symbols("a h k x y"), sympy.symbols("r", positive=True), ) formula = a * ((x - h) ** 2 + (y - k) ** 2 - r ** 2) equation = sympy.expand(self.equation.args[0] - self.equation.args[1] - formula) system = [equation.coeff(*t) for t in [(x, 2), (y, 2), (x, 1), (y, 1), (x, 0)]] values = sympy.solve(system, (a, r, h, k), dict=True) if not values: raise ValueError(f"{repr(self.equation)} is not an equation for a circle") values = values[0] return values[r] if self.info == "radius" else (values[h], values[k])
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())