1
0
Fork 0
mirror of https://codeberg.org/Mo8it/AdvLabDB.git synced 2024-12-20 23:41:20 +00:00

Add analysis

This commit is contained in:
Mo 2022-04-12 01:52:01 +02:00
parent e891fe4050
commit b33046dbc3
3 changed files with 90 additions and 6 deletions

View file

@ -1,5 +1,8 @@
from base64 import b64encode
from io import BytesIO
from pathlib import Path
import numpy as np
from flask import flash, has_request_context, redirect, request, url_for
from flask_admin import expose
from flask_admin.contrib.sqla.fields import QuerySelectField, QuerySelectMultipleField
@ -10,6 +13,7 @@ from flask_admin.model.template import EndpointLinkRowAction
from flask_security import admin_change_password, current_user, hash_password
from flask_wtf import FlaskForm
from flask_wtf.file import FileAllowed, FileField, FileRequired
from matplotlib.figure import Figure
from sqlalchemy import and_, func, or_
from werkzeug.utils import secure_filename
from wtforms import Form
@ -36,6 +40,8 @@ from advlabdb.customClasses import (
from advlabdb.database_import import importFromFile
from advlabdb.exceptions import DataBaseException, ModelViewException
from advlabdb.models import (
MAX_MARK,
MIN_MARK,
Admin,
Appointment,
Assistant,
@ -1270,20 +1276,20 @@ class ExperimentMarkView(SecureAdminModelView):
class EditForm(Form):
oral_mark = IntegerField(
"Oral Mark",
validators=[NumberRange(min=0, max=15), Optional()],
description="Between 0 and 15",
validators=[NumberRange(min=MIN_MARK, max=MAX_MARK), Optional()],
description=f"Between {MIN_MARK} and {MAX_MARK}",
)
protocol_mark = IntegerField(
"Protocol Mark",
validators=[NumberRange(min=0, max=15), Optional()],
description="Between 0 and 15",
validators=[NumberRange(min=MIN_MARK, max=MAX_MARK), Optional()],
description=f"Between {MIN_MARK} and {MAX_MARK}",
)
form = EditForm
column_descriptions = {
"oral_mark": "Between 0 and 15",
"protocol_mark": "Between 0 and 15",
"oral_mark": f"Between {MIN_MARK} and {MAX_MARK}",
"protocol_mark": f"Between {MIN_MARK} and {MAX_MARK}",
"final_experiment_mark": "Calculated automatically with oral and protocol marks and weightings",
"assistant": "The last assistant who edited the mark",
"admin": "The last admin who edited the mark",
@ -1429,6 +1435,65 @@ class ActionsView(SecureAdminBaseView):
return self.render("actions.html", form=form)
class AnalysisView(SecureAdminBaseView):
class AnalysisForm(FlaskForm):
assistantMarksSubmit = SubmitField(
label="Assistant's marks",
)
def markHist(data, title):
fig = Figure()
ax = fig.subplots()
ax.set_title(title)
ax.set_xlim(MIN_MARK - 0.5, MAX_MARK + 0.5)
ax.set_xticks(np.arange(MAX_MARK + 1))
ax.set_xlabel("Mark")
if data:
hist = ax.hist(
data,
bins=np.arange(MAX_MARK) - 0.5,
)
ax.set_yticks(np.arange(len(data) + 1))
else:
ax.set_yticks(np.arange(2))
buf = BytesIO()
fig.savefig(buf, format="png")
return b64encode(buf.getbuffer()).decode("ascii")
def markHists(markType, activeAssistants):
attr = markType.lower() + "_mark"
return [
AnalysisView.markHist(
data=[getattr(experimentMark, attr) for experimentMark in assistant.experiment_marks],
title=f"{assistant.repr()} - {markType} Marks",
)
for assistant in activeAssistants
]
@expose(methods=("GET", "POST"))
def index(self):
form = AnalysisView.AnalysisForm()
if form.validate_on_submit():
if form.assistantMarksSubmit.data:
activeAssistants = Assistant.query.filter(Assistant.user.has(User.active == True))
oralMarkHists = AnalysisView.markHists("Oral", activeAssistants)
protocolMarkHists = AnalysisView.markHists("Protocol", activeAssistants)
return self.render(
"analysis/assistant_marks.html",
histIndices=range(len(oralMarkHists)),
oralMarkHists=oralMarkHists,
protocolMarkHists=protocolMarkHists,
)
return self.render("analysis/analysis.html", form=form)
class DocsView(SecureAdminBaseView):
@expose("/")
def index(self):
@ -1452,6 +1517,7 @@ adminSpace.add_view(RoleView(Role, db.session))
adminSpace.add_view(ProgramView(Program, db.session))
adminSpace.add_view(ImportView(name="Import"))
adminSpace.add_view(ActionsView(name="Actions"))
adminSpace.add_view(AnalysisView(name="Analysis"))
adminSpace.add_view(DocsView(name="Docs"))
initActiveSemesterMenuLinks(adminSpace)

View file

@ -0,0 +1,13 @@
{% from "macros.html" import information %}
{% extends "admin/master.html" %}
{% block body %}
{{information(current_user, userActiveSemester, role="admin")}}
<hr>
<form method="POST">
{{ form.csrf_token }}
{{ form.assistantMarksSubmit }}
</form>
{% endblock body %}

View file

@ -0,0 +1,5 @@
{% for histInd in histIndices %}
<img src="data:image/png;base64,{{oralMarkHists[histInd]}}"\>
<img src="data:image/png;base64,{{protocolMarkHists[histInd]}}"\>
<hr>
{% endfor %}