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 | 2 | # BUGS |
| 3 | 3 | |
| 4 | -- /review não mostra imagens porque precisa que teste esteja a decorrer... | |
| 5 | 4 | - usar thread.Lock para aceder a variaveis de estado? |
| 6 | 5 | |
| 7 | 6 | # TODO |
| ... | ... | @@ -23,6 +22,7 @@ |
| 23 | 22 | |
| 24 | 23 | # FIXED |
| 25 | 24 | |
| 25 | +- /review não mostra imagens porque precisa que teste esteja a decorrer... | |
| 26 | 26 | - visualizar um teste ja realizado na página de administração |
| 27 | 27 | - Depois da correcção, mostra testes realizados que não foram realizados pelo próprio |
| 28 | 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 | 175 | return self.online[uid]['student']['name'] |
| 176 | 176 | def get_test(self, uid, default=None): |
| 177 | 177 | return self.online[uid].get('test', default) |
| 178 | + def get_questions_path(self): | |
| 179 | + return self.testfactory['questions_dir'] | |
| 178 | 180 | def get_test_qtypes(self, uid): |
| 179 | 181 | return {q['ref']:q['type'] for q in self.online[uid]['test']['questions']} |
| 180 | 182 | def get_student_grades_from_all_tests(self, uid): | ... | ... |
serve.py
| ... | ... | @@ -256,6 +256,7 @@ class Root(object): |
| 256 | 256 | @require() |
| 257 | 257 | def file(self, ref, name): |
| 258 | 258 | # serve a static file: userid, question ref, file name |
| 259 | + # only works for users running a test | |
| 259 | 260 | uid = cherrypy.session.get(SESSION_KEY) |
| 260 | 261 | filename = self.app.get_file(uid, ref, name) |
| 261 | 262 | return cherrypy.lib.static.serve_file(filename) |
| ... | ... | @@ -275,6 +276,12 @@ class Root(object): |
| 275 | 276 | t = json.load(f) |
| 276 | 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 | 286 | def parse_arguments(): |
| 280 | 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 | 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 | 95 | total_points = sum(q['points'] for q in t['questions']) |
| ... | ... | @@ -105,7 +105,7 @@ |
| 105 | 105 | ${q['title']} |
| 106 | 106 | </h4> |
| 107 | 107 | <p> |
| 108 | - ${md_to_html(q['text'], q['ref'], q['files'])} | |
| 108 | + ${md_to_html_review(q['text'], q)} | |
| 109 | 109 | </p> |
| 110 | 110 | </div> |
| 111 | 111 | % elif q['type'] == 'warning': |
| ... | ... | @@ -115,7 +115,7 @@ |
| 115 | 115 | ${q['title']} |
| 116 | 116 | </h4> |
| 117 | 117 | <p> |
| 118 | - ${md_to_html(q['text'], q['ref'], q['files'])} | |
| 118 | + ${md_to_html_review(q['text'], q)} | |
| 119 | 119 | </p> |
| 120 | 120 | </div> |
| 121 | 121 | % elif q['type'] == 'alert': |
| ... | ... | @@ -125,7 +125,7 @@ |
| 125 | 125 | ${q['title']} |
| 126 | 126 | </h4> |
| 127 | 127 | <p> |
| 128 | - ${md_to_html(q['text'], q['ref'], q['files'])} | |
| 128 | + ${md_to_html_review(q['text'], q)} | |
| 129 | 129 | </p> |
| 130 | 130 | </div> |
| 131 | 131 | |
| ... | ... | @@ -146,15 +146,15 @@ |
| 146 | 146 | </div> |
| 147 | 147 | <div class="panel-body" id="example${i}"> |
| 148 | 148 | <div class="question"> |
| 149 | - ${md_to_html(q['text'], q['ref'], q['files'])} | |
| 149 | + ${md_to_html_review(q['text'], q)} | |
| 150 | 150 | </div> |
| 151 | 151 | |
| 152 | 152 | % if q['type'] == 'radio': |
| 153 | 153 | % for opt in q['options']: |
| 154 | 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 | 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 | 158 | % endif |
| 159 | 159 | |
| 160 | 160 | % endfor |
| ... | ... | @@ -162,9 +162,9 @@ |
| 162 | 162 | % elif q['type'] == 'checkbox': |
| 163 | 163 | % for opt in q['options']: |
| 164 | 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 | 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 | 168 | % endif |
| 169 | 169 | % endfor |
| 170 | 170 | |
| ... | ... | @@ -179,7 +179,7 @@ |
| 179 | 179 | </button> |
| 180 | 180 | <div class="collapse" id="hint-${q['ref']}"> |
| 181 | 181 | <div class="well"> |
| 182 | - ${md_to_html(q['hint'], q['ref'], q['files'])} | |
| 182 | + ${md_to_html_review(q['hint'], q)} | |
| 183 | 183 | </div> |
| 184 | 184 | </div> |
| 185 | 185 | % endif # hint | ... | ... |
tools.py
| 1 | 1 | |
| 2 | - | |
| 3 | 2 | import subprocess |
| 4 | 3 | import logging |
| 5 | 4 | import yaml |
| ... | ... | @@ -58,7 +57,9 @@ def run_script(script, stdin='', timeout=5): |
| 58 | 57 | |
| 59 | 58 | def md_to_html(text, ref=None, files={}): |
| 60 | 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 | 63 | text = text.replace(k, '/file?ref={};name={}'.format(ref, k)) |
| 63 | 64 | return markdown.markdown(text, extensions=[ |
| 64 | 65 | 'markdown.extensions.tables', |
| ... | ... | @@ -67,3 +68,14 @@ def md_to_html(text, ref=None, files={}): |
| 67 | 68 | 'markdown.extensions.def_list', |
| 68 | 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 | + ]) | ... | ... |