Commit b1b7a3fd8f185093fb79f19619b2d9332dc660dc
1 parent
4f49f07b
Exists in
master
and in
1 other branch
- fixed showing images in /review.
Showing
5 changed files
with
34 additions
and
13 deletions
Show diff stats
BUGS.md
1 | 1 | ||
2 | # BUGS | 2 | # BUGS |
3 | 3 | ||
4 | -- /review não mostra imagens porque precisa que teste esteja a decorrer... | ||
5 | - usar thread.Lock para aceder a variaveis de estado? | 4 | - usar thread.Lock para aceder a variaveis de estado? |
6 | 5 | ||
7 | # TODO | 6 | # TODO |
@@ -23,6 +22,7 @@ | @@ -23,6 +22,7 @@ | ||
23 | 22 | ||
24 | # FIXED | 23 | # FIXED |
25 | 24 | ||
25 | +- /review não mostra imagens porque precisa que teste esteja a decorrer... | ||
26 | - visualizar um teste ja realizado na página de administração | 26 | - visualizar um teste ja realizado na página de administração |
27 | - Depois da correcção, mostra testes realizados que não foram realizados pelo próprio | 27 | - Depois da correcção, mostra testes realizados que não foram realizados pelo próprio |
28 | - detectar se janela perde focus e alertar o prof (http://stackoverflow.com/questions/1060008/is-there-a-way-to-detect-if-a-browser-window-is-not-currently-active) | 28 | - detectar se janela perde focus e alertar o prof (http://stackoverflow.com/questions/1060008/is-there-a-way-to-detect-if-a-browser-window-is-not-currently-active) |
app.py
@@ -175,6 +175,8 @@ class App(object): | @@ -175,6 +175,8 @@ class App(object): | ||
175 | return self.online[uid]['student']['name'] | 175 | return self.online[uid]['student']['name'] |
176 | def get_test(self, uid, default=None): | 176 | def get_test(self, uid, default=None): |
177 | return self.online[uid].get('test', default) | 177 | return self.online[uid].get('test', default) |
178 | + def get_questions_path(self): | ||
179 | + return self.testfactory['questions_dir'] | ||
178 | def get_test_qtypes(self, uid): | 180 | def get_test_qtypes(self, uid): |
179 | return {q['ref']:q['type'] for q in self.online[uid]['test']['questions']} | 181 | return {q['ref']:q['type'] for q in self.online[uid]['test']['questions']} |
180 | def get_student_grades_from_all_tests(self, uid): | 182 | def get_student_grades_from_all_tests(self, uid): |
serve.py
@@ -256,6 +256,7 @@ class Root(object): | @@ -256,6 +256,7 @@ class Root(object): | ||
256 | @require() | 256 | @require() |
257 | def file(self, ref, name): | 257 | def file(self, ref, name): |
258 | # serve a static file: userid, question ref, file name | 258 | # serve a static file: userid, question ref, file name |
259 | + # only works for users running a test | ||
259 | uid = cherrypy.session.get(SESSION_KEY) | 260 | uid = cherrypy.session.get(SESSION_KEY) |
260 | filename = self.app.get_file(uid, ref, name) | 261 | filename = self.app.get_file(uid, ref, name) |
261 | return cherrypy.lib.static.serve_file(filename) | 262 | return cherrypy.lib.static.serve_file(filename) |
@@ -275,6 +276,12 @@ class Root(object): | @@ -275,6 +276,12 @@ class Root(object): | ||
275 | t = json.load(f) | 276 | t = json.load(f) |
276 | return self.template['review'].render(t=t) | 277 | return self.template['review'].render(t=t) |
277 | 278 | ||
279 | + @cherrypy.expose | ||
280 | + @require(name_is('0')) | ||
281 | + def absfile(self, name): | ||
282 | + filename = path.abspath(path.join(self.app.get_questions_path(), name)) | ||
283 | + return cherrypy.lib.static.serve_file(filename) | ||
284 | + | ||
278 | # ============================================================================ | 285 | # ============================================================================ |
279 | def parse_arguments(): | 286 | def parse_arguments(): |
280 | argparser = argparse.ArgumentParser(description='Server for online tests. Enrolled students and tests have to be previously configured. Please read the documentation included with this software before running the server.') | 287 | argparser = argparse.ArgumentParser(description='Server for online tests. Enrolled students and tests have to be previously configured. Please read the documentation included with this software before running the server.') |
templates/review.html
@@ -89,7 +89,7 @@ | @@ -89,7 +89,7 @@ | ||
89 | </div> | 89 | </div> |
90 | 90 | ||
91 | <%! | 91 | <%! |
92 | - from tools import md_to_html | 92 | + from tools import md_to_html_review |
93 | %> | 93 | %> |
94 | <% | 94 | <% |
95 | total_points = sum(q['points'] for q in t['questions']) | 95 | total_points = sum(q['points'] for q in t['questions']) |
@@ -105,7 +105,7 @@ | @@ -105,7 +105,7 @@ | ||
105 | ${q['title']} | 105 | ${q['title']} |
106 | </h4> | 106 | </h4> |
107 | <p> | 107 | <p> |
108 | - ${md_to_html(q['text'], q['ref'], q['files'])} | 108 | + ${md_to_html_review(q['text'], q)} |
109 | </p> | 109 | </p> |
110 | </div> | 110 | </div> |
111 | % elif q['type'] == 'warning': | 111 | % elif q['type'] == 'warning': |
@@ -115,7 +115,7 @@ | @@ -115,7 +115,7 @@ | ||
115 | ${q['title']} | 115 | ${q['title']} |
116 | </h4> | 116 | </h4> |
117 | <p> | 117 | <p> |
118 | - ${md_to_html(q['text'], q['ref'], q['files'])} | 118 | + ${md_to_html_review(q['text'], q)} |
119 | </p> | 119 | </p> |
120 | </div> | 120 | </div> |
121 | % elif q['type'] == 'alert': | 121 | % elif q['type'] == 'alert': |
@@ -125,7 +125,7 @@ | @@ -125,7 +125,7 @@ | ||
125 | ${q['title']} | 125 | ${q['title']} |
126 | </h4> | 126 | </h4> |
127 | <p> | 127 | <p> |
128 | - ${md_to_html(q['text'], q['ref'], q['files'])} | 128 | + ${md_to_html_review(q['text'], q)} |
129 | </p> | 129 | </p> |
130 | </div> | 130 | </div> |
131 | 131 | ||
@@ -146,15 +146,15 @@ | @@ -146,15 +146,15 @@ | ||
146 | </div> | 146 | </div> |
147 | <div class="panel-body" id="example${i}"> | 147 | <div class="panel-body" id="example${i}"> |
148 | <div class="question"> | 148 | <div class="question"> |
149 | - ${md_to_html(q['text'], q['ref'], q['files'])} | 149 | + ${md_to_html_review(q['text'], q)} |
150 | </div> | 150 | </div> |
151 | 151 | ||
152 | % if q['type'] == 'radio': | 152 | % if q['type'] == 'radio': |
153 | % for opt in q['options']: | 153 | % for opt in q['options']: |
154 | % if q['answer'] is not None and str(loop.index) == q['answer']: | 154 | % if q['answer'] is not None and str(loop.index) == q['answer']: |
155 | - ${md_to_html('<i class="fa fa-dot-circle-o" aria-hidden="true"></i> ' + opt, q['ref'], q['files'])} | 155 | + ${md_to_html_review('<i class="fa fa-dot-circle-o" aria-hidden="true"></i> ' + opt, q)} |
156 | % else: | 156 | % else: |
157 | - ${md_to_html('<i class="fa fa-circle-o" aria-hidden="true"></i> ' + opt, q['ref'], q['files'])} | 157 | + ${md_to_html_review('<i class="fa fa-circle-o" aria-hidden="true"></i> ' + opt, q)} |
158 | % endif | 158 | % endif |
159 | 159 | ||
160 | % endfor | 160 | % endfor |
@@ -162,9 +162,9 @@ | @@ -162,9 +162,9 @@ | ||
162 | % elif q['type'] == 'checkbox': | 162 | % elif q['type'] == 'checkbox': |
163 | % for opt in q['options']: | 163 | % for opt in q['options']: |
164 | % if q['answer'] is not None and str(loop.index) in q['answer']: | 164 | % if q['answer'] is not None and str(loop.index) in q['answer']: |
165 | - ${md_to_html('<i class="fa fa-check-square-o" aria-hidden="true"></i> ' + opt, q['ref'], q['files'])} | 165 | + ${md_to_html_review('<i class="fa fa-check-square-o" aria-hidden="true"></i> ' + opt, q)} |
166 | % else: | 166 | % else: |
167 | - ${md_to_html('<i class="fa fa-square-o" aria-hidden="true"></i> ' + opt, q['ref'], q['files'])} | 167 | + ${md_to_html_review('<i class="fa fa-square-o" aria-hidden="true"></i> ' + opt, q)} |
168 | % endif | 168 | % endif |
169 | % endfor | 169 | % endfor |
170 | 170 | ||
@@ -179,7 +179,7 @@ | @@ -179,7 +179,7 @@ | ||
179 | </button> | 179 | </button> |
180 | <div class="collapse" id="hint-${q['ref']}"> | 180 | <div class="collapse" id="hint-${q['ref']}"> |
181 | <div class="well"> | 181 | <div class="well"> |
182 | - ${md_to_html(q['hint'], q['ref'], q['files'])} | 182 | + ${md_to_html_review(q['hint'], q)} |
183 | </div> | 183 | </div> |
184 | </div> | 184 | </div> |
185 | % endif # hint | 185 | % endif # hint |
tools.py
1 | 1 | ||
2 | - | ||
3 | import subprocess | 2 | import subprocess |
4 | import logging | 3 | import logging |
5 | import yaml | 4 | import yaml |
@@ -58,7 +57,9 @@ def run_script(script, stdin='', timeout=5): | @@ -58,7 +57,9 @@ def run_script(script, stdin='', timeout=5): | ||
58 | 57 | ||
59 | def md_to_html(text, ref=None, files={}): | 58 | def md_to_html(text, ref=None, files={}): |
60 | if ref is not None: | 59 | if ref is not None: |
61 | - for k,f in files.items(): | 60 | + # given q['ref'] and q['files'] replaces references to files by a |
61 | + # GET to /file?ref=???;name=??? | ||
62 | + for k in files: | ||
62 | text = text.replace(k, '/file?ref={};name={}'.format(ref, k)) | 63 | text = text.replace(k, '/file?ref={};name={}'.format(ref, k)) |
63 | return markdown.markdown(text, extensions=[ | 64 | return markdown.markdown(text, extensions=[ |
64 | 'markdown.extensions.tables', | 65 | 'markdown.extensions.tables', |
@@ -67,3 +68,14 @@ def md_to_html(text, ref=None, files={}): | @@ -67,3 +68,14 @@ def md_to_html(text, ref=None, files={}): | ||
67 | 'markdown.extensions.def_list', | 68 | 'markdown.extensions.def_list', |
68 | 'markdown.extensions.sane_lists' | 69 | 'markdown.extensions.sane_lists' |
69 | ]) | 70 | ]) |
71 | + | ||
72 | +def md_to_html_review(text, q): | ||
73 | + for k,f in q['files'].items(): | ||
74 | + text = text.replace(k, '/absfile?name={}'.format(q['files'][k])) | ||
75 | + return markdown.markdown(text, extensions=[ | ||
76 | + 'markdown.extensions.tables', | ||
77 | + 'markdown.extensions.fenced_code', | ||
78 | + 'markdown.extensions.codehilite', | ||
79 | + 'markdown.extensions.def_list', | ||
80 | + 'markdown.extensions.sane_lists' | ||
81 | + ]) |