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 | + ]) |