Commit ef9f327f796b63a6873f4170d0b1db691c2fd71e

Authored by Miguel Barão
1 parent d1410958
Exists in master and in 1 other branch dev

- another version not working...

config/logger-debug.yaml
@@ -34,6 +34,11 @@ loggers: @@ -34,6 +34,11 @@ loggers:
34 level: 'DEBUG' 34 level: 'DEBUG'
35 propagate: False 35 propagate: False
36 36
  37 + 'questionfactory':
  38 + handlers: ['default']
  39 + level: 'DEBUG'
  40 + propagate: False
  41 +
37 'tools': 42 'tools':
38 handlers: ['default'] 43 handlers: ['default']
39 level: 'DEBUG' 44 level: 'DEBUG'
demo/demo.yaml 0 → 100644
@@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
  1 +title: Example
  2 +database: students.db
  3 +
  4 +# path prefix applied to the topics
  5 +path: ./demo
  6 +
  7 +# Representation of the edges of the dependency graph.
  8 +# Example: A depends on B and C
  9 +# A:
  10 +# name: Topic A
  11 +# deps:
  12 +# - B
  13 +# - C
  14 +topics:
  15 +
  16 + # topic without dependencies
  17 + math:
  18 + name: Matemática
  19 +
  20 + # topic with one dependency
  21 + solar_system:
  22 + name: Solar system
  23 + deps:
  24 + - math
demo/math/generate-overflow.py 0 → 100755
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
  1 +#!/usr/bin/env python3
  2 +
  3 +from random import randint
  4 +import sys
  5 +
  6 +arg = sys.stdin.read() # read arguments
  7 +
  8 +a,b = (int(n) for n in arg.split(','))
  9 +
  10 +q = '''
  11 +type: checkbox
  12 +text: |
  13 + Indique quais das seguintes adições resultam em overflow quando se considera a adição de números com sinal (complemento para 2) em registos de 8 bits.
  14 +
  15 + Os números foram gerados aleatoriamente no intervalo de {0} a {1}.
  16 +options:
  17 +'''.format(a,b)
  18 +
  19 +correct = []
  20 +for i in range(5):
  21 + x = randint(a, b)
  22 + y = randint(a, b)
  23 + q += '- "`{} + {}`"\n'.format(x, y)
  24 + correct.append(1 if x + y > 127 else -1)
  25 +
  26 +q += 'correct: ' + str(correct)
  27 +
  28 +print(q)
demo/math/questions.yaml 0 → 100644
@@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
  1 +-
  2 + ref: prime_numbers
  3 + type: radio
  4 + title: Números primos
  5 + text: Qual dos seguintes números é primo?
  6 + options:
  7 + - 13
  8 + - 12
  9 + - 14
  10 + - 1, a **unidade**
  11 +
  12 +# # ---------------------------------------------------------------------------
  13 +# -
  14 +# ref: distributive_property
  15 +# type: radio
  16 +# title: Propriedade distributiva
  17 +# text: |
  18 +# Qual das seguintes opções usa a propriedade distributiva para calcular $9\times 2 - 2\times 3$?
  19 +# options:
  20 +# - $2\times(9 - 3)$
  21 +# - $18 - 6 = 12$
  22 +# - $2\times 9 - 2\times 3$
  23 +# - $2\times(9-2\times 3)$
  24 +
  25 +# # ---------------------------------------------------------------------------
  26 +# -
  27 +# ref: math-expressions
  28 +# type: checkbox
  29 +# title: Expressões matemáticas
  30 +# text: Quais das seguintes expressões são verdadeiras?
  31 +# options:
  32 +# - $1 > 0$
  33 +# - $\sqrt{3} > \sqrt{2}$
  34 +# - $e^{i\pi} + 1 = 0$
  35 +# - $\frac{\partial f(x,y)}{\partial z} = 1$
  36 +# - $-1 > 1$
  37 +# # how many points for each checkmark (normalized afterwards):
  38 +# correct: [1, 1, 1, -1, -1]
  39 +
  40 +# # ---------------------------------------------------------------------------
  41 +# -
  42 +# ref: overflow
  43 +# type: generator
  44 +# script: generate-overflow.py
  45 +# # opcional
  46 +# arg: "11,120"
  47 +# # the script should print a question dict in yaml format.
demo/solar_system/correct-first_3_planets.py 0 → 100755
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +#!/usr/bin/env python3
  2 +
  3 +import re
  4 +import sys
  5 +
  6 +s = sys.stdin.read()
  7 +
  8 +# set of words converted to lowercase
  9 +answer = set(re.findall(r'[\w]+', s.lower()))
  10 +answer.difference_update({'e', 'a', 'planeta', 'planetas'}) # ignore these
  11 +
  12 +# correct set of colors
  13 +planets = set(['mercúrio', 'vénus', 'terra'])
  14 +
  15 +correct = set.intersection(answer, planets) # os que acertei
  16 +wrong = set.difference(answer, planets) # os que errei
  17 +# allnames = set.union(answer, planets)
  18 +
  19 +grade = (len(correct) - len(wrong)) / len(planets)
  20 +
  21 +out = f'grade: {grade}'
  22 +
  23 +if grade < 1.0:
  24 + out += '\ncomments: A resposta correcta é `Mercúrio, Vénus e Terra`.'
  25 +
  26 +print(out)
  27 +exit(0)
0 \ No newline at end of file 28 \ No newline at end of file
demo/solar_system/correct-timeout.py 0 → 100755
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +#!/usr/bin/env python3
  2 +
  3 +import sys
  4 +import time
  5 +
  6 +s = sys.stdin.read()
  7 +
  8 +# generate timeout
  9 +time.sleep(100)
  10 +
  11 +print(0.5)
demo/solar_system/public/planets.png 0 → 100644

419 KB

demo/solar_system/questions.yaml 0 → 100644
@@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
  1 +
  2 +# ---------------------------------------------------------------------------
  3 +-
  4 + ref: solar-system
  5 + type: radio
  6 + title: Sistema solar
  7 + text: Qual é o maior planeta do Sistema Solar?
  8 + options:
  9 + - Mercúrio
  10 + - Marte
  11 + - Júpiter
  12 + - Têm todos o mesmo tamanho
  13 + # opcional
  14 + correct: 2
  15 + shuffle: False
  16 + discount: True
  17 +
  18 +# ---------------------------------------------------------------------------
  19 +-
  20 + ref: home-planet
  21 + type: text
  22 + title: Sistema solar
  23 + text: O nosso planeta chama-se planeta...
  24 + correct: ['Terra', 'terra']
  25 + # opcional
  26 + answer: Não é Marte...
  27 +
  28 +# ---------------------------------------------------------------------------
  29 +-
  30 + ref: saturn
  31 + type: text-regex
  32 + title: Sistema solar
  33 + text: O planeta do nosso sistema solar conhecido por ter aneis chama-se planeta...
  34 + correct: !regex '[Ss]aturno'
  35 +
  36 +# ---------------------------------------------------------------------------
  37 +-
  38 + ref: first_3_planets
  39 + type: textarea
  40 + title: Sistema solar
  41 + text: Escreva o nome dos três planetas mais próximos do Sol. (Exemplo `A, B e C`)
  42 + correct: correct-first_3_planets.py
  43 + # opcional
  44 + lines: 3
  45 + timeout: 5
factory.py 0 → 100644
@@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
  1 +# QFactory is a class that can generate question instances, e.g. by shuffling
  2 +# options, running a script to generate the question, etc.
  3 +#
  4 +# To generate an instance of a question we use the method generate() where
  5 +# the argument is the reference of the question we wish to produce.
  6 +#
  7 +# Example:
  8 +#
  9 +# # read question from file
  10 +# qdict = tools.load_yaml(filename)
  11 +# qfactory = QFactory(qdict)
  12 +# question = qfactory.generate()
  13 +#
  14 +# # experiment answering one question and correct it
  15 +# question.updateAnswer('42') # insert answer
  16 +# grade = question.correct() # correct answer
  17 +
  18 +# An instance of an actual question is an object that inherits from Question()
  19 +#
  20 +# Question - base class inherited by other classes
  21 +# QuestionInformation - not a question, just a box with content
  22 +# QuestionRadio - single choice from a list of options
  23 +# QuestionCheckbox - multiple choice, equivalent to multiple true/false
  24 +# QuestionText - line of text compared to a list of acceptable answers
  25 +# QuestionTextRegex - line of text matched against a regular expression
  26 +# QuestionTextArea - corrected by an external program
  27 +# QuestionNumericInterval - line of text parsed as a float
  28 +
  29 +# base
  30 +from os import path
  31 +import logging
  32 +
  33 +# project
  34 +from tools import run_script
  35 +from questions import QuestionInformation, QuestionRadio, QuestionCheckbox, QuestionText, QuestionTextRegex, QuestionTextArea, QuestionNumericInterval
  36 +
  37 +# setup logger for this module
  38 +logger = logging.getLogger(__name__)
  39 +
  40 +
  41 +
  42 +# ===========================================================================
  43 +# Question Factory
  44 +# ===========================================================================
  45 +class QFactory(object):
  46 + # Depending on the type of question, a different question class will be
  47 + # instantiated. All these classes derive from the base class `Question`.
  48 + _types = {
  49 + 'radio' : QuestionRadio,
  50 + 'checkbox' : QuestionCheckbox,
  51 + 'text' : QuestionText,
  52 + 'text-regex': QuestionTextRegex,
  53 + 'numeric-interval': QuestionNumericInterval,
  54 + 'textarea' : QuestionTextArea,
  55 + # -- informative panels --
  56 + 'information': QuestionInformation, 'info': QuestionInformation,
  57 + 'warning' : QuestionInformation, 'warn': QuestionInformation,
  58 + 'alert' : QuestionInformation,
  59 + 'success' : QuestionInformation,
  60 + }
  61 +
  62 + def __init__(self, question_dict):
  63 + self.question = question_dict
  64 +
  65 + # -----------------------------------------------------------------------
  66 + # Given a ref returns an instance of a descendent of Question(),
  67 + # i.e. a question object (radio, checkbox, ...).
  68 + # -----------------------------------------------------------------------
  69 + def generate(self):
  70 + logger.debug(f'Generating "{self.question["ref"]}"')
  71 + # Shallow copy so that script generated questions will not replace
  72 + # the original generators
  73 + q = self.question.copy()
  74 +
  75 + # If question is of generator type, an external program will be run
  76 + # which will print a valid question in yaml format to stdout. This
  77 + # output is then yaml parsed into a dictionary `q`.
  78 + if q['type'] == 'generator':
  79 + logger.debug(f' \_ Running script "{q["script"]}"...')
  80 + q.setdefault('arg', '') # optional arguments will be sent to stdin
  81 + script = path.join(q['path'], q['script'])
  82 + out = run_script(script=script, stdin=q['arg'])
  83 + q.update(out)
  84 +
  85 + # Finally we create an instance of Question()
  86 + try:
  87 + qinstance = self._types[q['type']](q) # instance with correct class
  88 + except KeyError as e:
  89 + logger.error(f'Failed to generate question "{q["ref"]}"')
  90 + raise e
  91 + else:
  92 + return qinstance
  93 +
@@ -86,11 +86,11 @@ class StudentKnowledge(object): @@ -86,11 +86,11 @@ class StudentKnowledge(object):
86 # current_question: the current question to be presented 86 # current_question: the current question to be presented
87 # ------------------------------------------------------------------------ 87 # ------------------------------------------------------------------------
88 def init_topic(self, topic=''): 88 def init_topic(self, topic=''):
89 - # self.unlock_topics() # FIXME needed? 89 + logger.debug(f'StudentKnowledge.init_topic({topic})')
90 90
91 if not topic: 91 if not topic:
92 topic = self.recommended_topic() 92 topic = self.recommended_topic()
93 - 93 + print(f'recommended {topic}')
94 94
95 self.current_topic = topic 95 self.current_topic = topic
96 logger.info(f'Topic set to "{topic}"') 96 logger.info(f'Topic set to "{topic}"')
@@ -134,9 +134,11 @@ class StudentKnowledge(object): @@ -134,9 +134,11 @@ class StudentKnowledge(object):
134 134
135 # if answer is wrong, keep same question and add a similar one at the end 135 # if answer is wrong, keep same question and add a similar one at the end
136 else: 136 else:
  137 + print('failed')
137 factory = self.deps.node[self.current_topic]['factory'] 138 factory = self.deps.node[self.current_topic]['factory']
138 self.questions.append(factory[q['ref']].generate()) 139 self.questions.append(factory[q['ref']].generate())
139 140
  141 +
140 # returns answered and corrected question 142 # returns answered and corrected question
141 return q 143 return q
142 144
@@ -154,10 +156,9 @@ class StudentKnowledge(object): @@ -154,10 +156,9 @@ class StudentKnowledge(object):
154 return self.current_topic 156 return self.current_topic
155 157
156 # ------------------------------------------------------------------------ 158 # ------------------------------------------------------------------------
157 - # Return list of tuples (topic, level).  
158 - # Levels are in the interval [0, 1] or None if the topic is locked. 159 + # Return list of {ref: 'xpto', name: 'long name', leve: 0.5}
  160 + # Levels are in the interval [0, 1] if unlocked or None if locked.
159 # Topics unlocked but not yet done have level 0.0. 161 # Topics unlocked but not yet done have level 0.0.
160 - # Example: [('topic_A', 0.9), ('topic_B', None), ...]  
161 # ------------------------------------------------------------------------ 162 # ------------------------------------------------------------------------
162 def get_knowledge_state(self): 163 def get_knowledge_state(self):
163 return [{ 164 return [{
@@ -166,15 +167,6 @@ class StudentKnowledge(object): @@ -166,15 +167,6 @@ class StudentKnowledge(object):
166 'level': self.state[ref]['level'] if ref in self.state else None 167 'level': self.state[ref]['level'] if ref in self.state else None
167 } for ref in self.topic_sequence ] 168 } for ref in self.topic_sequence ]
168 169
169 - # ts = []  
170 - # for ref in self.topic_sequence:  
171 - # ts.append({  
172 - # 'ref': ref,  
173 - # 'name': self.deps.nodes[ref]['name'],  
174 - # 'level': self.state[ref]['level'] if ref in self.state else None  
175 - # })  
176 - # return ts  
177 -  
178 # ------------------------------------------------------------------------ 170 # ------------------------------------------------------------------------
179 def get_topic_progress(self): 171 def get_topic_progress(self):
180 return len(self.finished_questions) / (1 + len(self.finished_questions) + len(self.questions)) 172 return len(self.finished_questions) / (1 + len(self.finished_questions) + len(self.questions))
@@ -15,7 +15,7 @@ import yaml @@ -15,7 +15,7 @@ import yaml
15 # this project 15 # this project
16 from models import Student, Answer, Topic, StudentTopic 16 from models import Student, Answer, Topic, StudentTopic
17 from knowledge import StudentKnowledge 17 from knowledge import StudentKnowledge
18 -from questionfactory import QFactory 18 +from factory import QFactory
19 from tools import load_yaml 19 from tools import load_yaml
20 20
21 # setup logger for this module 21 # setup logger for this module
questionfactory.py
@@ -1,93 +0,0 @@ @@ -1,93 +0,0 @@
1 -# QFactory is a class that can generate question instances, e.g. by shuffling  
2 -# options, running a script to generate the question, etc.  
3 -#  
4 -# To generate an instance of a question we use the method generate() where  
5 -# the argument is the reference of the question we wish to produce.  
6 -#  
7 -# Example:  
8 -#  
9 -# # read question from file  
10 -# qdict = tools.load_yaml(filename)  
11 -# qfactory = QFactory(qdict)  
12 -# question = qfactory.generate()  
13 -#  
14 -# # experiment answering one question and correct it  
15 -# question.updateAnswer('42') # insert answer  
16 -# grade = question.correct() # correct answer  
17 -  
18 -# An instance of an actual question is an object that inherits from Question()  
19 -#  
20 -# Question - base class inherited by other classes  
21 -# QuestionInformation - not a question, just a box with content  
22 -# QuestionRadio - single choice from a list of options  
23 -# QuestionCheckbox - multiple choice, equivalent to multiple true/false  
24 -# QuestionText - line of text compared to a list of acceptable answers  
25 -# QuestionTextRegex - line of text matched against a regular expression  
26 -# QuestionTextArea - corrected by an external program  
27 -# QuestionNumericInterval - line of text parsed as a float  
28 -  
29 -# base  
30 -from os import path  
31 -import logging  
32 -  
33 -# project  
34 -from tools import run_script  
35 -from questions import QuestionInformation, QuestionRadio, QuestionCheckbox, QuestionText, QuestionTextRegex, QuestionTextArea, QuestionNumericInterval  
36 -  
37 -# setup logger for this module  
38 -logger = logging.getLogger(__name__)  
39 -  
40 -  
41 -  
42 -# ===========================================================================  
43 -# Question Factory  
44 -# ===========================================================================  
45 -class QFactory(object):  
46 - # Depending on the type of question, a different question class will be  
47 - # instantiated. All these classes derive from the base class `Question`.  
48 - _types = {  
49 - 'radio' : QuestionRadio,  
50 - 'checkbox' : QuestionCheckbox,  
51 - 'text' : QuestionText,  
52 - 'text-regex': QuestionTextRegex,  
53 - 'numeric-interval': QuestionNumericInterval,  
54 - 'textarea' : QuestionTextArea,  
55 - # -- informative panels --  
56 - 'information': QuestionInformation, 'info': QuestionInformation,  
57 - 'warning' : QuestionInformation, 'warn': QuestionInformation,  
58 - 'alert' : QuestionInformation,  
59 - 'success' : QuestionInformation,  
60 - }  
61 -  
62 - def __init__(self, question_dict):  
63 - self.question = question_dict  
64 -  
65 - # -----------------------------------------------------------------------  
66 - # Given a ref returns an instance of a descendent of Question(),  
67 - # i.e. a question object (radio, checkbox, ...).  
68 - # -----------------------------------------------------------------------  
69 - def generate(self):  
70 - logger.debug(f'Generating "{self.question["ref"]}"')  
71 - # Shallow copy so that script generated questions will not replace  
72 - # the original generators  
73 - q = self.question.copy()  
74 -  
75 - # If question is of generator type, an external program will be run  
76 - # which will print a valid question in yaml format to stdout. This  
77 - # output is then yaml parsed into a dictionary `q`.  
78 - if q['type'] == 'generator':  
79 - logger.debug(f' \_ Running script "{q["script"]}"...')  
80 - q.setdefault('arg', '') # optional arguments will be sent to stdin  
81 - script = path.join(q['path'], q['script'])  
82 - out = run_script(script=script, stdin=q['arg'])  
83 - q.update(out)  
84 -  
85 - # Finally we create an instance of Question()  
86 - try:  
87 - qinstance = self._types[q['type']](q) # instance with correct class  
88 - except KeyError as e:  
89 - logger.error(f'Failed to generate question "{q["ref"]}"')  
90 - raise e  
91 - else:  
92 - return qinstance  
93 -  
@@ -135,7 +135,6 @@ class TopicHandler(BaseHandler): @@ -135,7 +135,6 @@ class TopicHandler(BaseHandler):
135 self.render('topic.html', 135 self.render('topic.html',
136 uid=uid, 136 uid=uid,
137 name=self.learn.get_student_name(uid), 137 name=self.learn.get_student_name(uid),
138 - # title=self.learn.get_title()  
139 ) 138 )
140 139
141 140
@@ -175,7 +174,7 @@ class QuestionHandler(BaseHandler): @@ -175,7 +174,7 @@ class QuestionHandler(BaseHandler):
175 } 174 }
176 175
177 def new_question(self, user): 176 def new_question(self, user):
178 - 177 + logger.debug(f'new_question({user})')
179 # state = self.learn.get_student_state(user) # [{ref, name, level},...] 178 # state = self.learn.get_student_state(user) # [{ref, name, level},...]
180 # current_topic = self.learn.get_student_topic(user) # str 179 # current_topic = self.learn.get_student_topic(user) # str
181 180
@@ -192,6 +191,7 @@ class QuestionHandler(BaseHandler): @@ -192,6 +191,7 @@ class QuestionHandler(BaseHandler):
192 } 191 }
193 192
194 def wrong_answer(self, user): 193 def wrong_answer(self, user):
  194 + logger.debug(f'wrong_answer({user})')
195 progress = self.learn.get_student_progress(user) # in the current topic 195 progress = self.learn.get_student_progress(user) # in the current topic
196 return { 196 return {
197 'method': 'shake', 197 'method': 'shake',
@@ -201,6 +201,8 @@ class QuestionHandler(BaseHandler): @@ -201,6 +201,8 @@ class QuestionHandler(BaseHandler):
201 } 201 }
202 202
203 def finished_topic(self, user): 203 def finished_topic(self, user):
  204 + logger.debug(f'finished_topic({user})')
  205 +
204 # state = self.learn.get_student_state(user) # all topics 206 # state = self.learn.get_student_state(user) # all topics
205 # current_topic = self.learn.get_student_topic(uid) 207 # current_topic = self.learn.get_student_topic(uid)
206 208
@@ -214,7 +216,7 @@ class QuestionHandler(BaseHandler): @@ -214,7 +216,7 @@ class QuestionHandler(BaseHandler):
214 216
215 @tornado.web.authenticated 217 @tornado.web.authenticated
216 def get(self): 218 def get(self):
217 - logging.debug('QuestionHandler.get') 219 + logging.debug('QuestionHandler.get()')
218 user = self.current_user 220 user = self.current_user
219 221
220 question = self.learn.get_student_question(user) 222 question = self.learn.get_student_question(user)
@@ -232,9 +234,10 @@ class QuestionHandler(BaseHandler): @@ -232,9 +234,10 @@ class QuestionHandler(BaseHandler):
232 # handles answer posted 234 # handles answer posted
233 @tornado.web.authenticated 235 @tornado.web.authenticated
234 def post(self): 236 def post(self):
235 - logging.debug('QuestionHandler.post') 237 + logging.debug('QuestionHandler.post()')
236 user = self.current_user 238 user = self.current_user
237 239
  240 + print(self.get_body_arguments())
238 # if self.learn.get_student_question(user) is None: 241 # if self.learn.get_student_question(user) is None:
239 242
240 answer = self.get_body_argument('answer') 243 answer = self.get_body_argument('answer')
templates/question-text.html
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 {% block answer %} 3 {% block answer %}
4 <fieldset data-role="controlgroup"> 4 <fieldset data-role="controlgroup">
5 {% if question['answer'] %} 5 {% if question['answer'] %}
6 - <input type="text" class="form-control" id="answer" name="answer" value="{{ question['answer'][0] }}" autofocus> 6 + <input type="text" class="form-control" id="answer" name="answer" value="{{ question['answer'] }}" autofocus>
7 {% else %} 7 {% else %}
8 <input type="text" class="form-control" id="answer" name="answer" value="" autofocus> 8 <input type="text" class="form-control" id="answer" name="answer" value="" autofocus>
9 {% end %} 9 {% end %}
templates/topic.html
@@ -82,13 +82,15 @@ @@ -82,13 +82,15 @@
82 82
83 <div id="notifications"></div> 83 <div id="notifications"></div>
84 84
85 - <form action="/question" method="post" id="question_form" autocomplete="off">  
86 - {% module xsrf_form_html() %} 85 + <div id="content">
  86 + <form action="/question" method="post" id="question_form" autocomplete="off">
  87 + {% module xsrf_form_html() %}
87 88
88 - <div id="question_div"></div> 89 + <div id="question_div"></div>
89 90
90 - </form>  
91 - <button class="btn btn-primary" id="submit" data-toggle="tooltip" data-placement="right" title="Shift-Enter">Continuar</button> 91 + </form>
  92 + <button class="btn btn-primary" id="submit" data-toggle="tooltip" data-placement="right" title="Shift-Enter">Continuar</button>
  93 + </div>
92 </div> 94 </div>
93 95
94 <!-- ===================================================================== --> 96 <!-- ===================================================================== -->
@@ -153,8 +155,8 @@ @@ -153,8 +155,8 @@
153 155
154 case "finished_topic": 156 case "finished_topic":
155 $('#topic_progress').css('width', '100%').attr('aria-valuenow', 100); 157 $('#topic_progress').css('width', '100%').attr('aria-valuenow', 100);
156 - $("#container").html('<img src="/static/trophy.png" alt="trophy" class="img-fluid mx-auto my-5 d-block" width="25%">');  
157 - $("#container").animateCSS('tada'); 158 + $("#content").html('<img src="/static/trophy.png" alt="trophy" class="img-fluid mx-auto my-5 d-block" width="25%">');
  159 + $("#content").animateCSS('tada');
158 setTimeout(function(){ window.location.replace('/'); }, 2000); 160 setTimeout(function(){ window.location.replace('/'); }, 2000);
159 break; 161 break;
160 } 162 }