def execute(state, plugin, argument): question = state.question if question == None: return statistics.question_stats() if question.stats.given == 0: return s = '<table><tr><td>' s += '<p class="given">%d</p>' % question.stats.given s += '<p class="view">%s</p>' % \ absolute_and_relative(question.stats.view, question.stats.given) s += '</td><td>' s += '<p class="good">%s</p>' % \ absolute_and_relative(question.stats.good, question.stats.given) s += '<p class="bad">%s</p>' % \ absolute_and_relative(question.stats.bad, question.stats.given) s += '</td><td>' s += '<p class="indice">%s</p>' % \ absolute_and_relative(question.stats.indice, question.stats.given) s += '<p class="time">%s (%s)</p>' % ( utilities.time_format(question.student_time), utilities.time_format(question.student_time / (0.1 + question.stats.given))) s += '<p class="comment">%s</p>' % \ absolute_and_relative(question.stats.nr_comment, question.stats.given) s += '</td></tr></table>' return s
def histogram_good(state): stats = statistics.question_stats() div = int(state.form.get("value", "10")) histo = [0] * (1 + stats.max_good_answers // div) for s in stats.all_students: histo[s.the_number_of_good_answers // div] += 1 t = "<pre>" for h in range(len(histo)): t += ("%3d-%3d : " % (h * div, (h + 1) * div - 1)) + "*" * histo[h] + "\n" t += '</pre>' t += '<p class="histobad"></p>' histo = [0] * (1 + stats.max_bad_answers // div) for s in stats.all_students: histo[s.the_number_of_bad_answers // div] += 1 t += "<pre>" for h in range(len(histo)): t += ("%3d-%3d : " % (h * div, (h + 1) * div - 1)) + "*" * histo[h] + "\n" t += '</pre>' return t
def execute(state, plugin, argument): if argument == None: return '' stats = statistics.question_stats() nr_students = float(len(stats.all_students)) s = [] for question in questions.questions.values(): norme = float(question.stats.given) if question.stats.given == 0: continue s.append([ question.a_href(), "%6.3f" % (question.stats.given / nr_students), "%6.3f" % (question.stats.view / norme), "%6.3f" % (question.stats.good / norme), "%6.3f" % (question.stats.bad / norme), "%6.3f" % (question.stats.indice / norme), utilities.time_format(question.student_time / norme), "%6.3f" % (question.stats.nr_comment / norme), "%5d" % question.perfect_time, ]) if s: plugin.heart_content = \ utilities.sortable_table(plugin.sort_column, s, url = "%s&%s=1" % (plugin.plugin.css_name, plugin.plugin.css_name)) state.question = None return ''
def execute(state, plugin, argument): if not state.question: return stats = statistics.question_stats() columns = set() for s in stats.all_students: a = s.answer(state.question.name) if a.answered: for k in a.grades: if a.grades[k] != '0': columns.add(k) columns = sorted(columns) table = [] for s in stats.all_students: a = s.answer(state.question.name) if not a.answered: continue table.append([utilities.answer_format(a.answered)] + [a.grades.get(c, 0) for c in columns]) return utilities.sortable_table(plugin.sort_column, table, url=plugin.plugin.css_name, titles=['X'] + columns)
def execute(state, plugin, argument): if argument == None: return '' stats = statistics.question_stats() comments = [] for s in stats.all_students: for a in s.answers.values(): q = questions.a_href(a.question).replace(' ',' ') for c in a.comments: comments.append( [ q, cgi.escape(c[1]), s.a_href(body=c[1]), utilities.date_format(c[0]).replace(' ',' ')]) plugin.heart_content = \ utilities.sortable_table(plugin.sort_column, comments, url = "%s&%s=1" % (plugin.plugin.css_name, plugin.plugin.css_name) ) state.question = None return ''
def execute(state, plugin, argument): if argument == None: return '' stats = statistics.question_stats() too_quick = collections.defaultdict(int) too_quick2 = collections.defaultdict(int) too_quick4 = collections.defaultdict(int) for s in stats.sorted_students: for question_name, answer in s.answers.items(): if not answer.answered: continue question = questions.questions[question_name] average_time = question.student_time / question.stats.given if answer.time_searching < average_time / 10: too_quick[s] += 1 if answer.time_searching < average_time / 20: too_quick2[s] += 1 if answer.time_searching < average_time / 40: too_quick4[s] += 1 plugin.heart_content = utilities.sortable_table( plugin.sort_column, [[s.a_href(), nb, too_quick2[s], too_quick4[s]] for s, nb in too_quick.items()], url="%s&%s=1" % (plugin.plugin.css_name, plugin.plugin.css_name)) state.question = None return ''
def execute(state, plugin, argument): if not state.question: return stats = statistics.question_stats() if state.question.stats.given > 2: t = state.question.student_time / state.question.stats.given return utilities.duration(t)
def execute(state, dummy_plugin, dummy_argument): stats = statistics.question_stats() t = [] for q in questions.questions.values(): if not hasattr(q, 'autoeval_level'): continue if not getattr(q, 'stats', False): continue if q.stats.given == 0: continue if q.student_time: d = 'x' time_searching = -1 if q is state.question: d = '<var class="me">x</var>' elif q.name in state.student.answers: a = state.student.answers[q.name] if a.answered: d = '<var class="ok">x</var>' time_searching = a.time_searching else: if a.nr_asked: d = '<var class="bad">x</var>' t.append((q.autoeval_level_average, math.log(1 + q.student_time / q.stats.given), d, q.name + '<script>histogram(' + repr(q.autoeval_level) + ',' + repr(time_searching) + ')</script>')) for s in stats.all_students: if not hasattr(s, 'autoeval_level'): continue if s.the_number_of_given_questions == 0: continue if s.the_time_searching: t.append( (s.autoeval_level, math.log(1 + s.the_time_searching / s.the_number_of_given_questions), s is state.student and '<var class="me">·</var>' or '·', s is state.student and state.student.name or '')) if not len(t): return '' pos.ymin = min(i[0] for i in t) pos.ymax = max(i[0] for i in t) pos.xmin = 0 pos.xmax = max(i[1] for i in t) s = [] for i in range(int(pos.ymin), int(pos.ymax) + 1): s.append(pos(i, 0, ("%2d" % i).replace(' ', ' '))) for i in t: s.append(pos(*i)) return '\n'.join(s) + '<br>' * height + ' ' * 40
def execute(state, plugin, argument): if not argument: return '' stats = statistics.question_stats() content = [] if stats.max_good_answers == 0: max_good_answers = 1. else: max_good_answers = float(stats.max_good_answers) if stats.max_bad_answers == 0: max_bad_answers = 1. else: max_bad_answers = float(stats.max_bad_answers) if stats.max_given_indices == 0: max_given_indices = 1. else: max_given_indices = float(stats.max_given_indices) teachers = set() for s in stats.all_students: teachers.update(s.grading_teachers()) for s in stats.all_students: grades = s.grades() content.append(( s.filename, s.the_number_of_good_answers / max_good_answers, s.the_number_of_bad_answers / max_bad_answers, s.the_number_of_given_indices / max_given_indices, (s.the_time_searching + s.the_time_after) / 3600., s.points(), ) + tuple(grades.get(teacher, 0) for teacher in teachers)) content.sort() header = plugin.tip.split('\\A')[1:-1] header.append('Points') header += list(teachers) formater = "%s" + ", %5.3f" * (len(header) - 1) t = [ ','.join(header), ','.join(configuration.explain_grade.get(i, '') for i in header) ] for c in content: t.append(formater % c) return 'text/csv; charset=UTF-8', '\n'.join(t).encode("utf-8")
def execute(state, plugin, argument): if state.question == None: return teacher = state.student.filename if argument: the_student, grade = argument.split(',') if the_student.startswith('*'): the_student = the_student[1:] student.students[the_student].set_why(state.question.name, teacher, grade) else: student.students[the_student].set_grade(state.question.name, teacher, grade) return 'image/png', b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\x0fIDAT\x08\x1d\x01\x04\x00\xfb\xff\x00\x00\xff\x00\x02\x02\x01\x00\x0b\x0e\xaa\xaf\x00\x00\x00\x00IEND\xaeB`\x82' stats = statistics.question_stats() lines = [] for s in stats.all_students: a = s.answer(state.question.name) if not a.answered: continue last = int(a.grades.get(teacher, '-1')) why = a.why.get(teacher, '') lines.append([ utilities.answer_format(a.answered), '<span><!-- %2d -->' % last + ''.join( '<input name="%s" type="radio" value="%d" onclick="question_correction(event)"%s> %d' % (s.filename, i, ['', ' checked'][i == last], i) + { 0: '', 1: '', 2: '<br>' }[i % 3] for i in range(6)) + '</span>', '<textarea rows="2" cols="40" name="*%s" onchange="question_correction(event)">%s</textarea>' % (s.filename, cgi.escape(why)), ]) if len(lines) == 0: return return ('<noscript>!!!!!!!JAVASCRIPT NEEDED!!!!!!</noscript>' + utilities.sortable_table( plugin.sort_column, lines, url=plugin.plugin.css_name, html_class="question_correction_table", ))
def execute(state, plugin, argument): if not argument: return '' stats = statistics.question_stats() for s in state.steal_identity(tuple(stats.all_students)): for a in s.answers.values(): if a.answered and a.question in questions.questions: s.writable = True # Want to change the log state.question = questions.questions[a.question] s.check_answer(a.answered, state) state.question = None plugin.heart_content = '<p class="grade_recompute"></p>' return ''
def execute(state, plugin, argument): if argument == None: return '' stats = statistics.question_stats() pairs = [] for s in stats.sorted_students: for ss in stats.sorted_students: if id(s) > id(ss): try: if ss.name not in s.nr_answer_same_time: continue after, before = s.nr_answer_same_time[ss.name] pairs.append((after, before, (100 * after) / s.the_number_of_good_answers, (100 * before) / ss.the_number_of_good_answers, s, ss)) except KeyError: pass if len(pairs) == 0: plugin.heart_content = '<p class="no_pairs_found"></p>' return '' pairs.sort(key=lambda x: x[0] + x[1]) pairs.reverse() average = (sum(list(zip(*pairs))[1]) + sum(list(zip(*pairs))[2])) / len(pairs) average2 = sum((i[1]+i[2])**2 for i in pairs) / len(pairs) stddev = (average2 - average*average) ** 0.5 st = [] for after, before, nn1, nn2, s1, s2 in pairs: st.append([s1.a_href(), s2.a_href(), after, before, '%.2f' % nn1, '%.2f' % nn2]) if max(nn1, nn2) < average + stddev/2: # not interesting break plugin.heart_content = utilities.sortable_table( plugin.sort_column, st, url = "%s&%s=1" % (plugin.plugin.css_name, plugin.plugin.css_name)) state.question = None return ''
def execute(state, plugin, argument): if argument == None: return '' stats = statistics.question_stats() content = [] for s in stats.all_students: nr_good_answers = (styles[s.warning_nr_good_answers], s.the_number_of_good_answers) nr_bad_answers = (styles[s.warning_nr_bad_answers], s.the_number_of_bad_answers) nr_given_indices = (styles[s.warning_nr_given_indices], s.the_number_of_given_indices) time_after = (styles[s.warning_time_after], utilities.time_format(s.the_time_after)) line = [ s.a_href(), nr_good_answers, s.the_number_of_given_questions, nr_bad_answers, nr_given_indices, s.the_number_of_comment, utilities.time_format(s.the_time_searching), time_after, utilities.date_format(s.the_time_first), utilities.date_format(s.the_time_last), "%3.1f" % s.nr_of_same_time_normed, int(s.the_time_variance), s.logs and s.logs[-1][1] or '?', ] # line.append('<img src="?action=question_pixel_map_see_other&student=%s">' % s.filename) content.append(line) plugin.heart_content = \ utilities.sortable_table(plugin.sort_column, content, url = "%s&%s=1" % (plugin.plugin.css_name, plugin.plugin.css_name)) state.question = None return ''
def execute(state, plugin, argument): if state.question == None: return stats = statistics.question_stats() comments = [] for s in stats.all_students: for a in s.answers.values(): if a.question != state.question.name: continue for c in a.comments: comments.append( [cgi.escape(c[1]), s.mailto(body=str(a.question) + " " + c[1]), utilities.date_format(c[0])]) if len(comments) == 0: return None return utilities.sortable_table(plugin.sort_column, comments, url=plugin.plugin.css_name )
def execute(state, plugin, argument): if not argument: p = state.plugins_dict['answered_other'] if p.heart_content is not None: p.heart_content = ( '<p><a class="delete_one" href="?action_destroy_student=' + state.form['answered_other'] + '"> </a>' + p.heart_content) return '' stats = statistics.question_stats() roles = set() for e in stats.all_students: try: for role in eval(utilities.read(e.roles_filename)): roles.add(role) except: pass if argument == '1': students = [ e for e in stats.all_students if e.the_number_of_good_answers < 2 ] else: students = [student.student(argument)] s = [] for e in students: if e == state.student: continue if e.filename in roles: continue s.append('%s(%s)' % (e.name, e.the_number_of_good_answers)) e.destroy() plugin.heart_content = ('<p class="deleted"><br>' + '<br>\n'.join(s)) state.question = None return ''
def generate(name): sys.path.append(os.path.join(os.getcwd())) from QUENLIG import statistics from QUENLIG import configuration import main from QUENLIG import questions import cgi configuration.session = main.Session(name) configuration.root = os.getcwd() configuration.version = os.path.basename(os.getcwd()) configuration.session.init() os.chdir(configuration.session.dir) stats = statistics.question_stats() ba = [] filename = 'xxx-' + name + '.html' print('\nGenerate', os.path.join(os.getcwd(), filename)) f = open(filename, 'w') f.write(''' <body onclick="compute();"> <style> pre { margin: 0.1em } td { vertical-align:top } #rank { position: fixed ; right:0px; top: 0px; } #rank TD { font-size: 80% } TD.radio { white-space: nowrap ; } table.top > TBODY > TR > TD { border-top: 1px solid black ; border-left: 1px solid black ; } table.top { border-spacing: 0px ; border: 1px solid black; } @media print { #rank { position: inherit ; } #rank TD { font-size: 100% ;} } table.points { text-align: center ; } </style> ''') students = list(stats.all_students) students.sort(lambda x,y: cmp(x.name, y.name)) f.write("""<script><!-- var win ; function compute() { if ( win == undefined || win.closed ) { win = window.open() ; if ( ! win ) return; win.document.open('text/html') ; win.document.write('Total:<div id="top"></div>'); win.document.close() ; } t = document.getElementsByTagName('input') ; students = [] ; for(var i = 0; i < t.length; i++) { if ( t[i].checked ) { name = t[i].name.split('/')[0] ; if ( students[name] != undefined ) students[name] = Number(students[name]) + Number(t[i].value) ; else students[name] = Number(t[i].value) ; } } s = '<table border class="top">' ; for(i in students) s += '<tr><td>' + i + '</td><td>' + students[i] + '</td></tr>' ; s += '</table>' ; win.document.getElementById('top').innerHTML = s ; } var note_pas = 1 ; var note_nb = 5 ; function radio(name, question, answer) { document.write('<tr><td class="radio"><table class="points"><tr>') ; for(var i=0; i<note_nb; i++) document.write('<td>' + i*note_pas + '</td>') ; document.write('</tr><tr>') ; for(var i=0; i<note_nb; i++) document.write('<td><input type="radio" name="' + name + '/' + question + '" value="' + i*note_pas + '"></td>') ; document.write('</tr></table></td><td>' + name + '</td><td>' + answer + '</td></tr>') ; } --></script> """) for q in questions.sorted_questions: q = q.name f.write('<h1>' + q + '</h1>\n') f.write('<p>' + questions.questions[q].question('') + '</p>\n') f.write('<table class="top">\n') t = [] for s in students: for a in s.answers.values(): if a.question == q and a.answered: t.append( (s.name, q, cgi.escape(a.answered) .replace('\r\n','\n') .replace('\n','<br>') .replace('\\','\\\\') .replace('"', '\\"')) ) break t.sort(key=lambda x: x[2].strip().lower()) for tt in t: f.write('<script>radio("%s","%s","%s");</script>\n' % tt) f.write('</table>') f.close()
def plot_svg(url_base): width = 55 height = 24 text_height = 6 bar_text_height = text_height / 2 x_spacing = 3 y_spacing = 3 line_spacing = (height - 3 * text_height) / 4 line_decal = text_height + line_spacing border_width = 1 x_margin = width * 1.5 x_margin += 2 * width # Name of required and used_by list of questions y_margin = height * 1.4 stats = statistics.question_stats() h = statistics.histogram_level() h.sort() svg = Svg(text_height, bar_text_height, border_width, h[-1] * (width + x_spacing) + 2 * x_margin, (2 + questions.sorted_questions[-1].level) * (height + y_spacing) + 2 * y_margin, url_base) barplot = BarPlot(svg, width, height, 4, bar_text_height) level = -1 y = y_margin for q in questions.sorted_questions: if q.level != level: x = x_margin y += height + y_spacing level = q.level opacity = (q.stats.given + 1.) / (len(stats.all_students) + 1) barplot.opacity = "opacity:%5.3f;fill-opacity:%5.3f;" % (opacity, opacity) svg.g_start(x, y) barplot.background(border_width) if q.stats.given: barplot.bar(0, q.stats.good, q.stats.given, svg_class='good') barplot.bar(1, q.stats.bad, q.stats.given, svg_class='bad', pixel=2) barplot.bar(2, q.stats.indices, q.stats.given, svg_class='indice') barplot.bar(3, int(q.stats.time_searching / q.stats.given), 300, svg_class='time', pixel=2) if q.stats.nr_comment: barplot.nr_comments(q.stats.nr_comment) t = [q.world] + split_text(q.short_name) barplot.textes(t, line_decal, url=q.url()) t = q.required.names() barplot.textes(t, bar_text_height, opacity='fill-opacity:0;', align='e', svg_class='bar e') t = q.used_by barplot.textes(t, bar_text_height, opacity='fill-opacity:0;', align='s', svg_class='bar s') x += width + x_spacing svg.g_end() return svg.end()
def execute(state, dummy_plugin, dummy_argument): if state.question == None: return if not state.threaded_run: return # To be fast stats = statistics.question_stats() uncanonize = {} arcs = collections.defaultdict(lambda: collections.defaultdict(int)) for s in stats.all_students: if state.question.name not in s.answers: continue for dummy in state.steal_identity([s]): a = s.answers[state.question.name] last_comment = is_first + state.question.get_question(state) for c_orig in a.bad_answers: c = state.question.canonize(c_orig, state) uncanonize[c] = c_orig commented = is_comment + re_comment.sub( "\n", s.answer_commented(a.question, c_orig, state)) arcs[last_comment][c] += 1 arcs[c][commented] += 1 last_comment = commented if a.answered: c = is_good + state.question.canonize(a.answered, state) commented = is_done + re_comment.sub( "\n", s.check_answer(a.answered, state)[1]) commented = re.sub("(?s)<u:sequence.*", "", commented) arcs[last_comment][c] += 1 arcs[c][commented] += 1 uncanonize[c] = is_good + a.answered else: arcs[last_comment][a.answered] += 1 s = '''digraph "%s" { node[height="0.2",width="0.2",shape=rectangle, margin="0.025", label="",style="filled", fillcolor="white", fontsize="8"]; graph[charset="UTF-8", orientation="P",ranksep=0.5,sep=0,nodesep=0.05]; ''' % state.question.name nodes = set() for node, others in arcs.items(): nodes.add(node) for other in others: nodes.add(other) for node in nodes: str_node = str(uncanonize.get(node, node)).replace('\\', '\\\\').replace( '"', '\\"').replace('\n', '\\n') if str(node).startswith(is_comment): s += '%s [ label="%s"];\n' % (H(node), str_node[1:]) elif str(node).startswith(is_good): s += '%s [ label="%s", style="filled",fillcolor="#88FF88" ];\n' % ( H(node), str_node[1:]) elif str(node).startswith(is_first): s += '%s [ label="%s" ];\n' % (H(node), str_node[1:]) elif str(node).startswith(is_done): s += '%s [ label="%s", style="filled",fillcolor="#00FF00" ];\n' % ( H(node), str_node[1:]) elif node is False: s += '%s [ style="filled",fillcolor="#FF0000" ];\n' % (H(node), ) else: s += '%s [ label="%s", style="filled",fillcolor="#FF8888" ];\n' % ( H(node), str_node) for node, others in arcs.items(): for arc, nb in others.items(): s += '%s -> %s [penwidth="%d"];\n' % (H(node), H(arc), int((2 * nb)**0.5)) s += '}' f = open("xxx.dot", "w") f.write(s) f.close() p = subprocess.Popen(["dot", "-Tsvg", "xxx.dot"], stdout=subprocess.PIPE) svg = p.communicate()[0].decode("utf-8") svg = re.sub(' (width|height)="[0-9]*pt"', '', svg) return '<div style="text-align:left; width: 20em" onclick="this.style.width = this.offsetWidth * 1.5 + \'px\'">' + svg + '</div>'