Compare commits

...

6 Commits

3 changed files with 124 additions and 90 deletions

View File

@ -17,6 +17,7 @@ services:
- ./ref-test/app/editor/static:/usr/share/nginx/html/editor/static:ro - ./ref-test/app/editor/static:/usr/share/nginx/html/editor/static:ro
- ./ref-test/app/quiz/static:/usr/share/nginx/html/quiz/static:ro - ./ref-test/app/quiz/static:/usr/share/nginx/html/quiz/static:ro
- ./ref-test/app/view/static:/usr/share/nginx/html/view/static:ro - ./ref-test/app/view/static:/usr/share/nginx/html/view/static:ro
- ./ref-test/app/analysis/static:/usr/share/nginx/html/analysis/static:ro
ports: ports:
- 80:80 - 80:80
- 443:443 - 443:443

View File

@ -45,6 +45,11 @@ server {
alias /usr/share/nginx/html/view/static/; alias /usr/share/nginx/html/view/static/;
} }
location ^~ /admin/analysis/static/ {
include /etc/nginx/mime.types;
alias /usr/share/nginx/html/analysis/static/;
}
# Proxy to the main app for all other requests # Proxy to the main app for all other requests
location / { location / {
include /etc/nginx/conf.d/proxy_headers.conf; include /etc/nginx/conf.d/proxy_headers.conf;

View File

@ -8,105 +8,133 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h1>Analysis</h1> <h1>Analysis by {{ type[0]|upper }}{{ type[1:] }}</h1>
<div class="container"> <div class="container">
<p class="lead"> <p class="lead">
Analysis for {{ type }} {{ subject }}. The analysis section displays statistics for all test results as well as answers to individual questions.
Analysis reports can be generated per exam or per question dataset to identify common mistakes or patterns in answers.
</p> </p>
</div> <div class="input-group mb-3">
<div class="container"> <span class="input-group-text">
<h3> {% if type == 'exam' %}
Question List Exam Code
</h3> {% elif type == 'dataset' %}
<div class="container dataset-metadata"> Dataset Name
{% endif %}
</span>
<span class="form-control">
{{ subject }}
</span>
</div>
<div class="input-group mb-3">
<span class="input-group-text">Total Entries</span>
<span class="form-control">
{{ analysis.entries }}
</span>
</div>
<div class="input-group mb-3">
<span class="input-group-text">Passed</span>
<span class="form-control">
{{ analysis.grades.merit + analysis.grades.pass }} ({{ ((analysis.grades.merit + analysis.grades.pass)*100/analysis.entries)|round(2) }} &percnt;)
</span>
</div>
<div class="mb-3">
<span class="badge rounded-pill progress-bar-striped bg-success">Merit: {{ analysis.grades.merit }}</span> <span class="badge rounded-pill bg-primary progress-bar-striped">Pass: {{ analysis.grades.pass }}</span> <span class="badge rounded-pill progress-bar-striped bg-danger">Fail: {{ analysis.grades.fail }}</span>
<div class="my-1 progress">
<div class="progress-bar progress-bar-striped bg-success" role="progressbar" style="width: {{ (analysis.grades.merit*100/analysis.entries)|round(2) }}%" aria-valuenow="{{ analysis.grades.merit }}" aria-valuemin="0" aria-valuemax="{{ analysis.entries }}">{{ (analysis.grades.merit*100/analysis.entries)|round(2) }} &percnt;</div>
<div class="progress-bar progress-bar-striped" role="progressbar" style="width: {{ (analysis.grades.pass*100/analysis.entries)|round(2) }}%" aria-valuenow="{{ analysis.grades.pass }}" aria-valuemin="0" aria-valuemax="{{ analysis.entries }}">{{ (analysis.grades.pass*100/analysis.entries)|round(2) }} &percnt;</div>
<div class="progress-bar progress-bar-striped bg-danger" role="progressbar" style="width: {{ (analysis.grades.fail*100/analysis.entries)|round(2) }}%" aria-valuenow="{{ analysis.grades.fail }}" aria-valuemin="0" aria-valuemax="{{ analysis.entries }}">{{ (analysis.grades.fail*100/analysis.entries)|round(2) }} &percnt;</div>
</div>
</div>
<div class="input-group mb-3">
<span class="input-group-text">Mean Score</span>
<span class="form-control">
{{ analysis.scores.mean|round(2) }} &percnt;
</span>
</div>
<div class="input-group mb-3">
<span class="input-group-text">Standard Deviation</span>
<span class="form-control">
{% if analysis.scores.stdev %}
{{ analysis.scores.stdev|round(2) }}
{% else %}
{{ None }}
{% endif %}
</span>
</div>
<div class="input-group mb-3">
<span class="input-group-text">Median Score</span>
<span class="form-control">
{{ analysis.scores.median|round(2) }} &percnt;
</span>
</div>
{% if type == 'exam' %}
<div class="input-group mb-3"> <div class="input-group mb-3">
<span class="input-group-text">Dataset Name</span> <span class="input-group-text">Dataset Name</span>
<span class="form-control"> <span class="form-control">
{{ dataset.get_name() }} {{ dataset.get_name() }}
</span> </span>
</div> </div>
<div class="input-group mb-3"> {% endif %}
<span class="input-group-text">Author</span> </div>
<span class="form-control"> <div class="container">
{{ dataset.creator.get_username() }} <table id="analysis-table" class="table table-striped" style="width:100%">
</span> <thead>
</div> <th data-priority="1">
<div class="input-group mb-3"> Question
<span class="input-group-text">Last Updated</span> </th>
<span class="form-control"> <th data-priority="1">
{{ dataset.date.strftime('%d %b %Y %H:%M') }} Percent Correct
</span> </th>
</div> <th data-priority="2">
{% if dataset.default %} Answers
<div class="input-group mb-3"> </th>
<span class="input-group-text"> <th data-priority="3">
<input type="checkbox" aria-label="Default" class="dataset-default" checked disabled> Tags
</span> </th>
<span class="form-control"> </thead>
Default Dataset <tbody>
</select> {% for question in questions %}
</div> <tr class="table-row">
{% endif %} <td>
</div> {{ question.q_no + 1 }}
<div class="container"> </td>
<table id="analysis-table" class="table table-striped" style="width:100%"> <td class="cell-percentage">
<thead> {{ ((analysis.answers[question.q_no][question.correct] or 0)*100/(analysis.answers[question.q_no].values())|sum())|round(2) }}
<th data-priority="1"> </td>
Question <td>
</th> <table style="width:100%">
<th data-priority="1"> {% for option in question.options %}
Percent Correct <tr>
</th> <td style="width:50%">
<th data-priority="2"> {{ option[1] }}
Answers </td>
</th> <td>
<th data-priority="3"> {% if question.correct == option[0] %}
Tags <div class="progress">
</th> <div class="progress-bar bg-success progress-bar-striped" role="progressbar" style="width: {{ (analysis.answers[question.q_no][option[0]] or 0)*100/(analysis.answers[question.q_no].values())|sum() }}%;" aria-valuenow="{{ (analysis.answers[question.q_no][option[0]] or 0)*100/(analysis.answers[question.q_no].values())|sum() }}" aria-valuemin="0" aria-valuemax="100">{{ ((analysis.answers[question.q_no][option[0]] or 0)*100/(analysis.answers[question.q_no].values())|sum())|round(2) }}%</div>
</thead> </div>
<tbody> {% else %}
{% for question in questions %} <div class="progress">
<tr class="table-row"> <div class="progress-bar bg-danger progress-bar-striped" role="progressbar" style="width: {{ (analysis.answers[question.q_no][option[0]] or 0)*100/(analysis.answers[question.q_no].values())|sum() }}%;" aria-valuenow="{{ (analysis.answers[question.q_no][option[0]] or 0)*100/(analysis.answers[question.q_no].values())|sum() }}" aria-valuemin="0" aria-valuemax="100">{{ ((analysis.answers[question.q_no][option[0]] or 0)*100/(analysis.answers[question.q_no].values())|sum())|round(2) }}%</div>
<td> </div>
{{ question.q_no + 1 }} {% endif %}
</td> </td>
<td class="cell-percentage"> </tr>
{{ ((analysis.answers[question.q_no][question.correct] or 0)*100/(analysis.answers[question.q_no].values())|sum())|round(2) }} {% endfor %}
</td> </table>
<td> </td>
<table style="width:100%"> <td>
{% for option in question.options %} <ul>
<tr> {% for tag in question.tags %}
<td style="width:50%"> <li>{{ tag|safe }}</li>
{{ option[1] }} {% endfor %}
</td> </ul>
<td> </td>
{% if question.correct == option[0] %} </tr>
<div class="progress"> {% endfor %}
<div class="progress-bar bg-success progress-bar-striped" role="progressbar" style="width: {{ (analysis.answers[question.q_no][option[0]] or 0)*100/(analysis.answers[question.q_no].values())|sum() }}%;" aria-valuenow="{{ (analysis.answers[question.q_no][option[0]] or 0)*100/(analysis.answers[question.q_no].values())|sum() }}" aria-valuemin="0" aria-valuemax="100">{{ ((analysis.answers[question.q_no][option[0]] or 0)*100/(analysis.answers[question.q_no].values())|sum())|round(2) }}%</div> </tbody>
</div> </table>
{% else %}
<div class="progress">
<div class="progress-bar bg-danger progress-bar-striped" role="progressbar" style="width: {{ (analysis.answers[question.q_no][option[0]] or 0)*100/(analysis.answers[question.q_no].values())|sum() }}%;" aria-valuenow="{{ (analysis.answers[question.q_no][option[0]] or 0)*100/(analysis.answers[question.q_no].values())|sum() }}" aria-valuemin="0" aria-valuemax="100">{{ ((analysis.answers[question.q_no][option[0]] or 0)*100/(analysis.answers[question.q_no].values())|sum())|round(2) }}%</div>
</div>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
</td>
<td>
<ul>
{% for tag in question.tags %}
<li>{{ tag|safe }}</li>
{% endfor %}
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div> </div>
{% endblock %} {% endblock %}