Compare commits
7 Commits
v1.2.4
...
developmen
Author | SHA1 | Date | |
---|---|---|---|
25fb3fdda4 | |||
fa104d7d1b | |||
cb747b4832 | |||
845fdcdf8d | |||
54e1653cb5 | |||
716206dc65 | |||
a8eda4078d |
@@ -17,7 +17,6 @@ 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
|
||||||
|
@@ -45,11 +45,6 @@ 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;
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
FROM python:3.10-slim
|
FROM python:3.13-slim
|
||||||
ARG DATA=./data/
|
ARG DATA=./data/
|
||||||
ENV DATA=$DATA
|
ENV DATA=$DATA
|
||||||
WORKDIR /ref-test
|
WORKDIR /ref-test
|
||||||
|
@@ -61,9 +61,7 @@ def create_app():
|
|||||||
app.register_blueprint(view, url_prefix='/admin/view')
|
app.register_blueprint(view, url_prefix='/admin/view')
|
||||||
app.register_blueprint(analysis, url_prefix='/admin/analysis')
|
app.register_blueprint(analysis, url_prefix='/admin/analysis')
|
||||||
|
|
||||||
"""Create Database Tables before First Request"""
|
"""Create Database Tables when creating app"""
|
||||||
@app.before_first_request
|
|
||||||
def _create_database_tables():
|
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
db.create_all()
|
db.create_all()
|
||||||
|
|
||||||
|
@@ -8,73 +8,44 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>Analysis by {{ type[0]|upper }}{{ type[1:] }}</h1>
|
<h1>Analysis</h1>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<p class="lead">
|
<p class="lead">
|
||||||
The analysis section displays statistics for all test results as well as answers to individual questions.
|
Analysis for {{ type }} {{ subject }}.
|
||||||
Analysis reports can be generated per exam or per question dataset to identify common mistakes or patterns in answers.
|
|
||||||
</p>
|
</p>
|
||||||
<div class="input-group mb-3">
|
|
||||||
<span class="input-group-text">
|
|
||||||
{% if type == 'exam' %}
|
|
||||||
Exam Code
|
|
||||||
{% elif type == 'dataset' %}
|
|
||||||
Dataset Name
|
|
||||||
{% endif %}
|
|
||||||
</span>
|
|
||||||
<span class="form-control">
|
|
||||||
{{ subject }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group mb-3">
|
<div class="container">
|
||||||
<span class="input-group-text">Total Entries</span>
|
<h3>
|
||||||
<span class="form-control">
|
Question List
|
||||||
{{ analysis.entries }}
|
</h3>
|
||||||
</span>
|
<div class="container dataset-metadata">
|
||||||
</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) }} %)
|
|
||||||
</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) }} %</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) }} %</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) }} %</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) }} %
|
|
||||||
</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) }} %
|
|
||||||
</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">
|
||||||
|
<span class="input-group-text">Author</span>
|
||||||
|
<span class="form-control">
|
||||||
|
{{ dataset.creator.get_username() }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<span class="input-group-text">Last Updated</span>
|
||||||
|
<span class="form-control">
|
||||||
|
{{ dataset.date.strftime('%d %b %Y %H:%M') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% if dataset.default %}
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<span class="input-group-text">
|
||||||
|
<input type="checkbox" aria-label="Default" class="dataset-default" checked disabled>
|
||||||
|
</span>
|
||||||
|
<span class="form-control">
|
||||||
|
Default Dataset
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -136,6 +107,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block script %}
|
{% block script %}
|
||||||
|
@@ -43,7 +43,7 @@ def _fetch_questions():
|
|||||||
data_path = dataset.get_file()
|
data_path = dataset.get_file()
|
||||||
with open(data_path, 'r') as data_file:
|
with open(data_path, 'r') as data_file:
|
||||||
data = loads(data_file.read())
|
data = loads(data_file.read())
|
||||||
questions = generate_questions(data)
|
questions = generate_questions(dataset=data, randomise=False)
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'time_limit': end_time,
|
'time_limit': end_time,
|
||||||
'questions': questions,
|
'questions': questions,
|
||||||
|
@@ -41,7 +41,7 @@ class User(UserMixin, db.Model):
|
|||||||
def set_password(self): raise AttributeError('set_password is not a readable attribute.')
|
def set_password(self): raise AttributeError('set_password is not a readable attribute.')
|
||||||
|
|
||||||
set_password.setter
|
set_password.setter
|
||||||
def set_password(self, password:str): self.password = generate_password_hash(password, method="sha256")
|
def set_password(self, password:str): self.password = generate_password_hash(password, method="scrypt")
|
||||||
|
|
||||||
def verify_password(self, password:str): return check_password_hash(self.password, password)
|
def verify_password(self, password:str): return check_password_hash(self.password, password)
|
||||||
|
|
||||||
|
@@ -30,6 +30,7 @@ def _instructions():
|
|||||||
@quiz.route('/start/', methods=['GET', 'POST'])
|
@quiz.route('/start/', methods=['GET', 'POST'])
|
||||||
def _start():
|
def _start():
|
||||||
clubs = [
|
clubs = [
|
||||||
|
'Barrowland Bears Korfball Club',
|
||||||
'Dundee Korfball Club',
|
'Dundee Korfball Club',
|
||||||
'Edinburgh City Korfball Club',
|
'Edinburgh City Korfball Club',
|
||||||
'Edinburgh Mavericks Korfball Club',
|
'Edinburgh Mavericks Korfball Club',
|
||||||
|
@@ -1,33 +1,33 @@
|
|||||||
blinker==1.5
|
blinker==1.9.0
|
||||||
cffi==1.15.1
|
cffi==2.0.0
|
||||||
click==8.1.3
|
click==8.3.0
|
||||||
cryptography==39.0.2
|
cryptography==46.0.2
|
||||||
dnspython==2.3.0
|
dnspython==2.8.0
|
||||||
dominate==2.7.0
|
dominate==2.9.1
|
||||||
email-validator==1.3.1
|
email-validator==2.3.0
|
||||||
Flask==2.2.3
|
Flask==3.1.2
|
||||||
Flask-Bootstrap==3.3.7.1
|
Flask-Bootstrap==3.3.7.1
|
||||||
Flask-Login==0.6.2
|
Flask-Login==0.6.3
|
||||||
Flask-Mail==0.9.1
|
Flask-Mail==0.10.0
|
||||||
Flask-SQLAlchemy==3.0.3
|
Flask-SQLAlchemy==3.1.1
|
||||||
Flask-WTF==1.1.1
|
Flask-WTF==1.2.2
|
||||||
greenlet==2.0.2
|
greenlet==3.2.4
|
||||||
gunicorn==20.1.0
|
gunicorn==23.0.0
|
||||||
idna==3.4
|
idna==3.10
|
||||||
itsdangerous==2.1.2
|
itsdangerous==2.2.0
|
||||||
Jinja2==3.1.2
|
Jinja2==3.1.6
|
||||||
MarkupSafe==2.1.2
|
MarkupSafe==3.0.3
|
||||||
pip==23.0.1
|
packaging==25.0
|
||||||
pycparser==2.21
|
pycparser==2.23
|
||||||
PyMySQL==1.0.2
|
PyMySQL==1.1.2
|
||||||
python-dotenv==1.0.0
|
python-dotenv==1.1.1
|
||||||
setuptools==67.4.0
|
setuptools==80.9.0
|
||||||
six==1.16.0
|
six==1.17.0
|
||||||
SQLAlchemy==2.0.4
|
SQLAlchemy==2.0.43
|
||||||
sqlalchemy-json==0.5.0
|
sqlalchemy-json==0.7.0
|
||||||
SQLAlchemy-Utils==0.40.0
|
SQLAlchemy-Utils==0.42.0
|
||||||
typing_extensions==4.5.0
|
typing_extensions==4.15.0
|
||||||
visitor==0.1.3
|
visitor==0.1.3
|
||||||
Werkzeug==2.2.3
|
Werkzeug==3.1.3
|
||||||
wheel==0.38.4
|
wheel==0.45.1
|
||||||
WTForms==3.0.1
|
WTForms==3.2.1
|
Reference in New Issue
Block a user