Commit b1b7a3fd8f185093fb79f19619b2d9332dc660dc

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

- fixed showing images in /review.

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)
@@ -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):
@@ -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
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=&#39;&#39;, timeout=5): @@ -58,7 +57,9 @@ def run_script(script, stdin=&#39;&#39;, 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 + ])