1
0
Fork 0
mirror of https://codeberg.org/Mo8it/AdvLabDB.git synced 2024-12-04 22:40:30 +00:00

Compare commits

...

13 commits

Author SHA1 Message Date
b02a6683f7 Update pre-commit hooks 2022-09-13 00:24:12 +02:00
a16e0bf33d flake8 2022-09-13 00:22:12 +02:00
0f7700b11a Add assistant missing marks table to admin home page 2022-09-13 00:20:39 +02:00
b6e54ba0c0 Fix typo 2022-09-12 23:03:42 +02:00
b1195fff7f Only lower if not None 2022-09-12 19:20:01 +02:00
19819d7e99 Lower email 2022-09-12 19:08:02 +02:00
a66d95ffb5 Add active semester to user settings in assistants space 2022-09-12 17:59:54 +02:00
0b4316a713 Sort semester starting with newest 2022-09-12 17:57:29 +02:00
1d8c5db16c Remove addMenuLink 2022-09-12 17:57:13 +02:00
f212ddf181 Add Tables category 2022-09-11 20:05:20 +02:00
8717f42bac Remove active semester drop down menu 2022-09-11 20:05:09 +02:00
8846a867f5 Format templates 2022-09-11 16:29:26 +02:00
6edb47fed9 Use jinja.html 2022-09-11 14:55:53 +02:00
33 changed files with 326 additions and 296 deletions

View file

@ -16,12 +16,12 @@ repos:
args: [--py39-plus]
- repo: https://github.com/crate-ci/typos
rev: v1.11.0
rev: v1.12.4
hooks:
- id: typos
- repo: https://github.com/psf/black
rev: 22.6.0
rev: 22.8.0
hooks:
- id: black
@ -41,7 +41,7 @@ repos:
- id: flake8
- repo: https://github.com/python-poetry/poetry
rev: 1.2.0b3
rev: 1.2.0rc1
hooks:
- id: poetry-check
- id: poetry-export

View file

@ -59,7 +59,6 @@ from .advlabdb_independent_funs import (
flashRandomPassword,
str_without_semester_formatter,
)
from .assistantModelViews import assistantSpace
from .custom_classes import (
SecureAdminBaseView,
SecureAdminIndexView,
@ -69,7 +68,6 @@ from .database_import import importFromFile
from .exceptions import ModelViewException
from .model_dependent_funs import (
generate_new_password_field,
initActiveSemesterMenuLinks,
mark_field,
user_info_fields,
)
@ -130,12 +128,20 @@ class UserView(SecureAdminModelView):
@staticmethod
def semesterQueryFactory():
return Semester.query
return Semester.query.order_by(Semester.id.desc())
@staticmethod
def default_roles():
return [user_datastore.find_role("assistant")]
active_semester = QuerySelectField(
"Active Semester",
query_factory=semesterQueryFactory,
validators=[DataRequired()],
default=Semester.lastSemester,
description="Not fixed and users (including assistants) can change it.",
)
email = StringField(
"Email",
validators=[DataRequired(), Email()],
@ -170,13 +176,6 @@ class UserView(SecureAdminModelView):
"Active",
default=True,
)
active_semester = QuerySelectField(
"Active Semester",
query_factory=semesterQueryFactory,
validators=[DataRequired()],
default=Semester.lastSemester,
description="Not fixed and users (including assistants) can change it.",
)
class EditForm(CreateForm):
semester_experiments = None
@ -280,6 +279,9 @@ class UserView(SecureAdminModelView):
admin = Admin(user=model)
self.session.add(admin)
# Lower email
model.email = model.email.lower()
def after_model_change(self, form, model, is_created):
if is_created:
flash(
@ -368,26 +370,9 @@ class SemesterView(SecureAdminModelView):
transferAssistants=form.transfer_assistants.data,
)
@staticmethod
def addMenuLink(space, newSemester):
categoryText = "Active semester"
link = MenuLink(
name=str(newSemester),
url=url_for("main.set_semester") + "?semester_id=" + str(newSemester.id),
category=categoryText,
)
category = space._menu_categories.get(categoryText)
link.parent = category
category._children.insert(0, link)
def after_model_change(self, form, model, is_created):
current_user.setActiveSemester(model)
SemesterView.addMenuLink(adminSpace, model)
SemesterView.addMenuLink(assistantSpace, model)
def programQueryFactory():
return Program.query
@ -483,10 +468,17 @@ class StudentView(SecureAdminModelView):
}
def on_model_change(self, form, model, is_created):
# Don't save contact_email if it is similar to uni_email
# No need to strip strings because of email form validation
if model.contact_email is not None and model.contact_email == model.uni_email:
model.contact_email = None
# Lower uni email
model.uni_email = model.uni_email.lower()
if model.contact_email is not None:
# Lower contact email
model.contact_email = model.contact_email.lower()
# Don't save contact_email if it is similar to uni_email
# No need to strip strings because of email form validation
if model.contact_email == model.uni_email:
model.contact_email = None
def partQueryFactory():
@ -1298,7 +1290,7 @@ class ImportView(SecureAdminBaseView):
except Exception as ex:
flash(str(ex), "error")
return self.render("import.html", form=form)
return self.render("import.jinja.html", form=form)
class ActionsView(SecureAdminBaseView):
@ -1320,7 +1312,7 @@ class ActionsView(SecureAdminBaseView):
return redirect(url_for("main.index"))
return self.render("actions.html", form=form)
return self.render("actions.jinja.html", form=form)
class AnalysisView(SecureAdminBaseView):
@ -1420,7 +1412,7 @@ class AnalysisView(SecureAdminBaseView):
protocolMarkHists = AnalysisView.markHists("Protocol", activeAssistants)
return self.render(
"analysis/assistant_marks.html",
"analysis/assistant_marks.jinja.html",
histIndices=range(len(oralMarkHists)),
oralMarkHists=oralMarkHists,
protocolMarkHists=protocolMarkHists,
@ -1469,18 +1461,18 @@ class AnalysisView(SecureAdminBaseView):
meanFinalPartMarksPlot = AnalysisView.htmlFig(fig)
return self.render(
"analysis/final_part_marks.html",
"analysis/final_part_marks.jinja.html",
activeSemesterFinalPartMarksHists=activeSemesterFinalPartMarksHists,
meanFinalPartMarksPlot=meanFinalPartMarksPlot,
)
return self.render("analysis/analysis.html", form=form)
return self.render("analysis/analysis.jinja.html", form=form)
class DocsView(SecureAdminBaseView):
@expose("/")
def index(self):
return self.render("docs/docs.html", role="admin")
return self.render("docs/docs.jinja.html", role="admin")
def init_admin_model_views(app):
@ -1505,4 +1497,4 @@ def init_admin_model_views(app):
adminSpace.add_view(AnalysisView(name="Analysis", url="analysis"))
adminSpace.add_view(DocsView(name="Docs", url="docs"))
initActiveSemesterMenuLinks(adminSpace, app)
adminSpace.add_link(MenuLink(name="Logout", url="/logout"))

View file

@ -1,10 +1,13 @@
from flask import flash, redirect, request, url_for
from flask_admin import Admin as FlaskAdmin
from flask_admin import expose
from flask_admin.contrib.sqla.fields import QuerySelectField
from flask_admin.menu import MenuLink
from flask_admin.model.template import EndpointLinkRowAction
from flask_login import current_user
from flask_security.changeable import admin_change_password
from flask_wtf import FlaskForm
from wtforms.validators import DataRequired
from .advlabdb_independent_funs import (
deep_getattr,
@ -21,12 +24,11 @@ from .exceptions import ModelViewException
from .forms import assistant_group_experiment_form_factory
from .model_dependent_funs import (
generate_new_password_field,
initActiveSemesterMenuLinks,
parse_selection_mark_field,
user_info_fields,
)
from .model_independent_funs import randomPassword, reportBadAttempt
from .models import Assistant, GroupExperiment, SemesterExperiment, User, db
from .models import Assistant, GroupExperiment, Semester, SemesterExperiment, User, db
assistantSpace = FlaskAdmin(
name="Assistant@AdvLabDB",
@ -168,7 +170,7 @@ class AssistantGroupExperimentView(SecureAssistantModelView):
final_experiment_marks = [experiment_mark.final_experiment_mark for experiment_mark in experiment_marks]
return self.render(
"assistant_group_experiment_form.html",
"assistant_group_experiment_form.jinja.html",
form=form,
experiment_label=group_experiment.semester_experiment.experiment.str(),
group_number=group_experiment.group.number,
@ -184,6 +186,19 @@ class AssistantGroupExperimentView(SecureAssistantModelView):
class AssistantUserView(SecureAssistantModelView):
class EditForm(FlaskForm):
@staticmethod
def semesterQueryFactory():
# Show only last two semesters to assistants
return Semester.query.order_by(Semester.id.desc()).limit(2)
active_semester = QuerySelectField(
"Active Semester",
query_factory=semesterQueryFactory,
validators=[DataRequired()],
default=Semester.lastSemester,
description="You should change the active semester to the last semester. Do not forget to click save!",
)
phone_number, mobile_phone_number, building, room = user_info_fields()
generate_new_password = generate_new_password_field()
@ -220,7 +235,7 @@ class AssistantUserView(SecureAssistantModelView):
class AssistantDocsView(SecureAssistantBaseView):
@expose("/")
def index(self):
return self.render("docs/docs.html", role="assistant")
return self.render("docs/docs.jinja.html", role="assistant")
def init_assistant_model_views(app):
@ -230,4 +245,4 @@ def init_assistant_model_views(app):
assistantSpace.add_view(AssistantUserView(User, url="user"))
assistantSpace.add_view(AssistantDocsView(name="Docs", url="docs"))
initActiveSemesterMenuLinks(assistantSpace, app)
assistantSpace.add_link(MenuLink(name="Logout", url="/logout"))

View file

@ -4,7 +4,7 @@ from flask_admin.contrib.sqla import ModelView
from flask_admin.helpers import get_form_data
from flask_admin.model.helpers import get_mdict_item_or_list
from flask_login import current_user
from sqlalchemy import select
from sqlalchemy import func, select
from .exceptions import DatabaseException, ModelViewException
from .model_independent_funs import reportBadAttempt
@ -12,8 +12,6 @@ from .models import (
Assistant,
ExperimentMark,
GroupExperiment,
Part,
PartStudent,
SemesterExperiment,
db,
get_count,
@ -48,22 +46,21 @@ class SecureAdminIndexView(CustomIndexView):
@expose("/")
def index(self):
active_semester_experiment_mark_ids_stmt = (
select(ExperimentMark.final_experiment_mark)
.join(PartStudent)
.join(Part)
.where(Part.semester == current_user.active_semester)
)
number_of_all_experiment_marks = get_count(active_semester_experiment_mark_ids_stmt)
number_of_missing_final_experiment_marks = get_count(
active_semester_experiment_mark_ids_stmt.where(ExperimentMark.final_experiment_mark == None)
assistants_num_missing = db.session.execute(
select(Assistant, func.count())
.join(Assistant.semester_experiments)
.where(SemesterExperiment.semester == current_user.active_semester)
.join(SemesterExperiment.group_experiments)
.where(GroupExperiment.experiment_marks_missing == True)
.join(GroupExperiment.experiment_marks)
.where(ExperimentMark.final_experiment_mark == None)
.group_by(Assistant.id)
.order_by(func.count().desc())
)
return self.render(
"admin_index.html",
number_of_missing_final_experiment_marks=number_of_missing_final_experiment_marks,
number_of_all_experiment_marks=number_of_all_experiment_marks,
"admin_index.jinja.html",
assistants_num_missing=assistants_num_missing,
)
@ -89,7 +86,7 @@ class SecureAssistantIndexView(CustomIndexView):
)
return self.render(
"assistant_index.html",
"assistant_index.jinja.html",
number_of_missing_final_experiment_marks=number_of_missing_final_experiment_marks,
number_of_all_experiment_marks=number_of_all_experiment_marks,
)
@ -230,14 +227,14 @@ class SecureAdminModelView(CustomModelView):
column_display_actions = True
can_view_details = True
list_template = "admin_list.html"
create_template = "admin_create.html"
edit_template = "admin_edit.html"
details_template = "admin_details.html"
list_template = "admin_list.jinja.html"
create_template = "admin_create.jinja.html"
edit_template = "admin_edit.jinja.html"
details_template = "admin_details.jinja.html"
def __init__(self, model, **kwargs):
url = get_url(kwargs)
super().__init__(model, db.session, endpoint="admin_" + url, **kwargs)
super().__init__(model, db.session, endpoint="admin_" + url, category="Tables", **kwargs)
def is_accessible(self):
return adminViewIsAccessible()
@ -250,10 +247,10 @@ class SecureAssistantModelView(CustomModelView):
can_edit = False
column_display_actions = False
list_template = "assistant_list.html"
create_template = "assistant_create.html"
edit_template = "assistant_edit.html"
details_template = "assistant_details.html"
list_template = "assistant_list.jinja.html"
create_template = "assistant_create.jinja.html"
edit_template = "assistant_edit.jinja.html"
details_template = "assistant_details.jinja.html"
"""
SECURITY NOTES:

View file

@ -6,7 +6,7 @@ from flask import flash
from sqlalchemy import select
from . import data_dir
from .exceptions import DataBaseImportException
from .exceptions import DatabaseImportException
from .models import (
Appointment,
Assistant,
@ -37,14 +37,14 @@ def nullable(entry):
if is_null(entry):
return None
return entry
return entry.strip()
def not_nullable(entry):
if is_null(entry):
raise DataBaseImportException("Unnullable entry is NULL!")
raise DatabaseImportException("Unnullable entry is NULL!")
return entry
return entry.strip()
def importFromFile(filePath: Path):
@ -54,7 +54,7 @@ def importFromFile(filePath: Path):
db_bk_dir.mkdir(exist_ok=True)
if filePath.name[-4:] != ".txt":
raise DataBaseImportException(
raise DatabaseImportException(
"The import file has to be a text file with txt extension (.txt at the end of the filename)!"
)
@ -82,7 +82,7 @@ def importFromFile(filePath: Path):
if expectingTable:
if line[0] != "#":
raise DataBaseImportException(f"Expected a Table name starting with # but got this line: {line}")
raise DatabaseImportException(f"Expected a Table name starting with # but got this line: {line}")
expectingTable = False
tableName = line[1:]
@ -104,7 +104,7 @@ def importFromFile(filePath: Path):
elif tableName == "Appointment":
activeDict = appointments
else:
raise DataBaseImportException(f"{tableName} is not a valid table name!")
raise DatabaseImportException(f"{tableName} is not a valid table name!")
readHeader = True
continue
@ -122,7 +122,7 @@ def importFromFile(filePath: Path):
cellsLen = len(cells)
if cellsLen != len(activeDict["_header"]):
raise DataBaseImportException(
raise DatabaseImportException(
f"The number of header cells is not equal to the number of row cells in row {cells}!"
)
@ -134,14 +134,14 @@ def importFromFile(filePath: Path):
flash("Semester...")
if len(semesters["label"]) != 1:
raise DataBaseImportException("Only one semester is allowed in an import file!")
raise DatabaseImportException("Only one semester is allowed in an import file!")
semesterLabel = not_nullable(semesters["label"][0])
semesterYear = int(not_nullable(semesters["year"][0]))
dbSemester = get_first(select(Semester).where(Semester.label == semesterLabel, Semester.year == semesterYear))
if dbSemester is None:
raise DataBaseImportException(
raise DatabaseImportException(
f"{semesterLabel}{semesterYear} does not exist in the database! Please make sure that you create the semester from the web interface first."
)
@ -164,7 +164,7 @@ def importFromFile(filePath: Path):
)
if dbPart is None:
raise DataBaseImportException(
raise DatabaseImportException(
f"Part with number {partNumber} and program label {partProgramLabel} does not exist in the database! Please make sure that you create parts from the web interface first."
)
@ -178,8 +178,10 @@ def importFromFile(filePath: Path):
student_number = int(not_nullable(student_number))
first_name = not_nullable(students["first_name"][i])
last_name = not_nullable(students["last_name"][i])
uni_email = not_nullable(students["uni_email"][i])
uni_email = not_nullable(students["uni_email"][i]).lower()
contact_email = nullable(students["contact_email"][i])
if contact_email is not None:
contact_email = contact_email.lower()
bachelor_thesis = nullable(students["bachelor_thesis"][i])
bachelor_thesis_work_group = nullable(students["bachelor_thesis_work_group"][i])
note = nullable(students["note"][i])
@ -277,7 +279,7 @@ def importFromFile(filePath: Path):
if dbExperiment is None:
# TODO: Check if experimentProgram is None
raise DataBaseImportException(
raise DatabaseImportException(
f"Experiment with number {experimentNumber} and program {experimentProgram} does not exist in the database. Please make sure that experiments are created from the web interface."
)
@ -288,7 +290,7 @@ def importFromFile(filePath: Path):
)
if dbSemesterExperiment is None:
raise DataBaseImportException(
raise DatabaseImportException(
f"No semester experiment exists in the database to the experiment with number {experimentNumber} and program {experimentProgram}. Please make sure that semester experiments are created in the web interface. The problem might be that the experiment was not active while creating a new semester"
)
@ -312,11 +314,11 @@ def importFromFile(filePath: Path):
for i, date in enumerate(appointments["date"]):
date = not_nullable(date)
assistantEmail = not_nullable(appointments["assistant_email"][i])
assistantEmail = not_nullable(appointments["assistant_email"][i]).lower()
assistant = get_first(select(Assistant).join(User).where(User.email == assistantEmail))
if assistant is None:
raise DataBaseImportException(
raise DatabaseImportException(
f"Assistant with email {assistantEmail} does not exist in the database! Please make sure that you create assistants in the web interface."
)
@ -345,5 +347,5 @@ def importFromFile(filePath: Path):
flash(f"Made a backup of the database after the import at {dest}")
flash("\nImport done!")
flash("Please check that everything worked as expected. Restore the database backup otherwise!")
flash("\nImport done!", "success")
flash("Please check that everything worked as expected. Restore the database backup otherwise!", "warning")

View file

@ -6,5 +6,5 @@ class DatabaseException(Exception):
pass
class DataBaseImportException(Exception):
class DatabaseImportException(Exception):
pass

View file

@ -4,40 +4,34 @@ Functions dependent on advlabdb.models.
from functools import cache
from flask import flash
from flask_admin.menu import MenuLink
from flask import flash, url_for
from flask_login import current_user
from markupsafe import Markup
from wtforms.fields import BooleanField, IntegerField, SelectField, StringField
from wtforms.validators import DataRequired, NumberRange, Optional
from .models import MAX_MARK, MIN_MARK, Semester
def initActiveSemesterMenuLinks(space, app):
with app.app_context():
try:
semesters = Semester.sortedSemestersStartingWithNewest()
for semester in semesters:
space.add_link(
MenuLink(
name=str(semester),
url="/set_semester" + "?semester_id=" + str(semester.id),
category="Active semester",
)
)
except Exception:
print(
"ERROR: The Semester table does not exist yet! Therefore, menu links could not be generated. You can ignore this error if you are just initializing the database."
)
else:
space.add_link(MenuLink(name="Logout", url="/logout"))
def user_settings_url():
if current_user.has_role("admin"):
role = "admin"
else:
role = "assistant"
return url_for("main.index") + role + "/user/edit/?id=" + str(current_user.id)
def active_semester_str():
active_semester = current_user.active_semester
active_semester_str = str(active_semester)
if active_semester != Semester.lastSemester():
flash(f"You are in the old semester {active_semester_str}!", "warning")
flash(
Markup(
f"You are in the old semester {active_semester_str}! You should change your active semester in <a href='{ user_settings_url() }'>user settings</a>."
),
"warning",
)
return active_semester_str

View file

@ -61,9 +61,16 @@ class Student(db.Model):
part_students = db.relationship("PartStudent", back_populates="student", lazy=True)
def __init__(self, uni_email, contact_email=None, **kwargs):
# Don't save contact_email if it is similar to uni_email
if contact_email is not None and contact_email.strip() == uni_email.strip():
contact_email = None
# Lower and strip uni email
uni_email = uni_email.strip().lower()
if contact_email is not None:
# Lower and strip contact email
contact_email = contact_email.strip().lower()
# Don't save contact_email if it is similar to uni_email
if contact_email == uni_email:
contact_email = None
super().__init__(uni_email=uni_email, contact_email=contact_email, **kwargs)
@ -527,7 +534,7 @@ class Semester(db.Model):
if last_semester is not None:
if year < last_semester.year or (year == last_semester.year and label == "SS"):
raise DatabaseException(f"You can only create semesters older than the last semester {last_semester}!")
raise DatabaseException(f"You can only create semesters later than the last semester {last_semester}!")
super().__init__(label=label, year=year, **kwargs)

View file

@ -1,4 +1,4 @@
{% from "macros.html" import information %}
{% from "macros.jinja.html" import information %}
{% extends "admin/master.html" %}
{% block body %}

View file

@ -1,4 +1,4 @@
{% from "macros.html" import information %}
{% from "macros.jinja.html" import information %}
{% extends "admin/model/create.html" %}
{% block body %}

View file

@ -1,4 +1,4 @@
{% from "macros.html" import information %}
{% from "macros.jinja.html" import information %}
{% extends "admin/model/details.html" %}
{% block body %}

View file

@ -1,4 +1,4 @@
{% from "macros.html" import information %}
{% from "macros.jinja.html" import information %}
{% extends "admin/model/edit.html" %}
{% block body %}

View file

@ -1,14 +0,0 @@
{% from "macros.html" import information, missing_final_experiment_marks %}
{% extends "admin/index.html" %}
{% block body %}
{{ information(current_user, active_semester_str, role="admin") }}
<hr>
{{ missing_final_experiment_marks(number_of_missing_final_experiment_marks, number_of_all_experiment_marks) }}
{{ super() }}
{{ footer|safe }}
{% endblock %}

View file

@ -0,0 +1,42 @@
{% from "macros.jinja.html" import information %}
{% extends "admin/index.html" %}
{% block body %}
{{ information(current_user, active_semester_str, role="admin") }}
<hr>
<div class="d-inline-flex">
<div class="table">
<table class="table table-bordered">
<thead class="thead-dark">
<tr>
<th scope="col">
Assistant
</th>
<th scope="col">
Missing experiment marks
</th>
</tr>
</thead>
<tbody>
{% for assistant_num_missing in assistants_num_missing %}
<tr>
<td>
{{ assistant_num_missing[0] }}
</td>
<td>
{{ assistant_num_missing[1] }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{{ super() }}
{{ footer|safe }}
{% endblock %}

View file

@ -1,4 +1,4 @@
{% from "macros.html" import information %}
{% from "macros.jinja.html" import information %}
{% extends "admin/model/list.html" %}
{% block body %}

View file

@ -1,8 +1,8 @@
{% from "macros.html" import information %}
{% from "macros.jinja.html" import information %}
{% extends "admin/master.html" %}
{% block body %}
{{information(current_user, active_semester_str, role="admin")}}
{{ information(current_user, active_semester_str, role="admin")}}
<hr>

View file

@ -1,8 +1,8 @@
{% from "macros.html" import information %}
{% from "macros.jinja.html" import information %}
{% extends "admin/master.html" %}
{% block body %}
{{information(current_user, active_semester_str, role="admin")}}
{{ information(current_user, active_semester_str, role="admin")}}
<hr>
@ -15,8 +15,8 @@
<hr>
{% for histInd in histIndices %}
<img src="data:image/png;base64,{{oralMarkHists[histInd]}}">
<img src="data:image/png;base64,{{protocolMarkHists[histInd]}}">
<img src="data:image/png;base64,{{ oralMarkHists[histInd]}}">
<img src="data:image/png;base64,{{ protocolMarkHists[histInd]}}">
<hr>
{% endfor %}
{% endblock body %}

View file

@ -1,15 +0,0 @@
{% from "macros.html" import information %}
{% extends "admin/master.html" %}
{% block body %}
{{information(current_user, active_semester_str, role="admin")}}
<hr>
{% for activeSemesterFinalPartMarksHist in activeSemesterFinalPartMarksHists %}
<img src="data:image/png;base64,{{activeSemesterFinalPartMarksHist}}">
<hr>
{% endfor %}
<img src="data:image/png;base64,{{meanFinalPartMarksPlot}}">
{% endblock body %}

View file

@ -0,0 +1,15 @@
{% from "macros.jinja.html" import information %}
{% extends "admin/master.html" %}
{% block body %}
{{ information(current_user, active_semester_str, role="admin")}}
<hr>
{% for activeSemesterFinalPartMarksHist in activeSemesterFinalPartMarksHists %}
<img src="data:image/png;base64,{{ activeSemesterFinalPartMarksHist }}">
<hr>
{% endfor %}
<img src="data:image/png;base64,{{ meanFinalPartMarksPlot }}">
{% endblock body %}

View file

@ -1,4 +1,4 @@
{% from "macros.html" import information %}
{% from "macros.jinja.html" import information %}
{% extends "admin/model/create.html" %}
{% block body %}

View file

@ -1,4 +1,4 @@
{% from "macros.html" import information %}
{% from "macros.jinja.html" import information %}
{% extends "admin/model/details.html" %}
{% block body %}

View file

@ -1,4 +1,4 @@
{% from "macros.html" import information %}
{% from "macros.jinja.html" import information %}
{% extends "admin/model/edit.html" %}
{% block body %}

View file

@ -1,4 +1,4 @@
{% from "macros.html" import information %}
{% from "macros.jinja.html" import information %}
{% extends "admin/master.html" %}
{% block body %}

View file

@ -1,14 +0,0 @@
{% from "macros.html" import information, missing_final_experiment_marks %}
{% extends "admin/index.html" %}
{% block body %}
{{ information(current_user, active_semester_str, role="assistant") }}
<hr>
{{missing_final_experiment_marks(number_of_missing_final_experiment_marks, number_of_all_experiment_marks)}}
{{ super() }}
{{ footer|safe }}
{% endblock %}

View file

@ -0,0 +1,17 @@
{% from "macros.jinja.html" import information %}
{% extends "admin/index.html" %}
{% block body %}
{{ information(current_user, active_semester_str, role="assistant") }}
<hr>
<p>
Number of <strong>missing</strong> final experiment marks:
{{ number_of_missing_final_experiment_marks }} / {{ number_of_all_experiment_marks }}
</p>
{{ super() }}
{{ footer|safe }}
{% endblock %}

View file

@ -1,4 +1,4 @@
{% from "macros.html" import information %}
{% from "macros.jinja.html" import information %}
{% extends "admin/model/list.html" %}
{% block body %}

View file

@ -1,98 +0,0 @@
<div class="alert alert-warning" role="alert">
Changes have been done to the web interface. Read the section
<a href="#_group_experiment">Group Experiment</a> for more information.
</div>
<h2>Welcome</h2>
<p>
Welcome to this website, AdvLabDB, where you will be able to manage your lab
assistance. Please, <strong>read this documentation</strong> to familiarize
yourself with this website.
</p>
<h2>Menu items</h2>
<p>
In this section, you will find explanations to each menu item on the upper
navigation bar.
</p>
<h3>Home</h3>
<p>
This is your home page. Here, you currently only find the number of
<strong>missing</strong> final experiment marks for the experiments that you
are assigned to. At the latest at the end of the semester, this number should
be 0. A number higher than 0 means that some oral and/or protocol marks are
missing. The final experiment mark is calculated automatically after the oral
and protocol marks of an experiment are set.
</p>
<h3 id="_group_experiment">Group Experiment</h3>
<p>
This is the most important menu item. Here, you see all pairs of experiments
and groups that you are responsible for. If a value in the column
<em>Experiment Marks Missing</em> is <span style="color: red">Yes</span>, then
you did not set all the experiment marks for the pair in the related row. If
you click on the edit button on the left side of a row, then you will get a
form.
</p>
<p>
In the form, you can edit the <em>date</em> of an appointment by clicking on
it. You should do so after writing with the students doing the experiment
corresponding to the appointment. The initial date you find before editing it
is only a suggestion in the week where the appointment should take place.
Please take take into account if the appointment should be in the semester
break.
</p>
<p>
Below the appointments in the form, you can set the students' oral and
protocol marks. Students in a group have separate marks. The oral mark has to
be set after the oral exam. The protocol mark has to be set after correcting
the group's protocol.
</p>
<div class="alert alert-warning" role="alert">
Oral and protocol marks are between 0 and 15!
</div>
<p>
After editing appointment date(s) or experiment marks, click on the
<em>Save</em> button to save the changes.
</p>
<p>
The changes are lost if you don't click on <em>Save</em>. Therefore, if you
want to discard the changes, just click on the back button of your browser.
</p>
<h3>User</h3>
<p>
Here, you find a table with only one row which is you as a user. Make sure
that the fields <em>Phone Number</em>, <em>Mobile Phone Number</em>,
<em>Building</em> and <em>Room</em> are filled and up to date, especially if
you are a new assistant. To edit these fields, click on the pen icon at the
left of the single entry in the table. After editing, click on <em>Save</em>.
</p>
<p>
You can generate a new random password by clicking on the pen icon, checking
the corresponding checkbox and then clicking on
<em>Save</em>. You will be then logged out. Your new password is displayed
above the login fields. Make sure that you save the password in a safe place.
Using a free open source password manager like
<a href="https://bitwarden.com/" target="_blank" rel="noopener">Bitwarden</a>
or
<a href="https://keepassxc.org/" target="_blank" rel="noopener">KeepassXC</a>
is recommended.
</p>
<h3>Docs</h3>
<p>This is a link which leads you to this page.</p>
<h3>Active semester</h3>
<p>
An active semester is the semester you are working in. All shown experiment
marks and appointments are in your active semester. By default as a new
assistant, your active semester should be the latest semester.
</p>
<p>
If you see a warning that you are in an old semester, click on
<em>Active semester</em> and choose the latest semester. You should only work
in an old semester if there are still experiment marks to be set in the old
semester.
</p>

View file

@ -0,0 +1,92 @@
<h2>Welcome</h2>
<p>
Welcome to this website, AdvLabDB, where you will be able to manage your lab
assistance. Please, <strong>read this documentation</strong> to familiarize
yourself with this website.
</p>
<h2>Menu items</h2>
<p>
In this section, you will find explanations to each menu item on the upper
navigation bar.
</p>
<h3>Home</h3>
<p>
This is your home page. Here, you currently only find the number of
<strong>missing</strong> final experiment marks for the experiments that you
are assigned to. At the latest at the end of the semester, this number should
be 0. A number higher than 0 means that some oral and/or protocol marks are
missing. The final experiment mark is calculated automatically after the oral
and protocol marks of an experiment are set.
</p>
<h3 id="_group_experiment">Group Experiment</h3>
<p>
This is the most important menu item. Here, you see all pairs of experiments
and groups that you are responsible for. If a value in the column
<em>Experiment Marks Missing</em> is <span style="color: red">Yes</span>, then
you did not set all the experiment marks for the pair in the related row. If
you click on the edit button on the left side of a row, then you will get a
form.
</p>
<p>
In the form, you can edit the <em>date</em> of an appointment by clicking on
it. You should do so after writing with the students doing the experiment
corresponding to the appointment. The initial date you find before editing it
is only a suggestion in the week where the appointment should take place.
Please take take into account if the appointment should be in the semester
break.
</p>
<p>
Below the appointments in the form, you can set the students' oral and
protocol marks. Students in a group have separate marks. The oral mark has to
be set after the oral exam. The protocol mark has to be set after correcting
the group's protocol.
</p>
<div class="alert alert-warning" role="alert">
Oral and protocol marks are between 0 and 15!
</div>
<p>
After editing appointment date(s) or experiment marks, click on the
<em>Save</em> button to save the changes.
</p>
<p>
The changes are lost if you don't click on <em>Save</em>. Therefore, if you
want to discard the changes, just click on the back button of your browser.
</p>
<h3>User</h3>
<p>
Here, you find a table with only one row which is you as a user. Make sure
that the fields <em>Phone Number</em>, <em>Mobile Phone Number</em>,
<em>Building</em> and <em>Room</em> are filled and up to date, especially if
you are a new assistant. To edit these fields, click on the pen icon at the
left of the single entry in the table. After editing, click on <em>Save</em>.
</p>
<p>
You can generate a new random password by clicking on the pen icon, checking
the corresponding checkbox and then clicking on
<em>Save</em>. You will be then logged out. Your new password is displayed
above the login fields. Make sure that you save the password in a safe place.
Using a free open source password manager like
<a href="https://bitwarden.com/" target="_blank" rel="noopener">Bitwarden</a>
or
<a href="https://keepassxc.org/" target="_blank" rel="noopener">KeepassXC</a>
is recommended.
</p>
<h3>Docs</h3>
<p>This is a link which leads you to this page.</p>
<h3>Active semester</h3>
<p>
An active semester is the semester you are working in. All shown experiment
marks and appointments are in your active semester. By default as a new
assistant, your active semester should be the latest semester.
</p>
<p>
If you see a warning that you are in an old semester, then you should change
your active semester in the user settings. You should only work in an old
semester if there are still experiment marks to be set in the old semester.
</p>

View file

@ -1,4 +1,4 @@
{% from "macros.html" import information %}
{% from "macros.jinja.html" import information %}
{% extends "admin/master.html" %}
{% block body %}
@ -6,7 +6,7 @@
<hr>
{% include "docs/" + role + ".html" %}
{% include "docs/" + role + ".jinja.html" %}
{{ footer|safe }}
{% endblock body %}

View file

@ -1,4 +1,4 @@
{% from "macros.html" import information %}
{% from "macros.jinja.html" import information %}
{% extends "admin/master.html" %}
{% block body %}

View file

@ -1,20 +0,0 @@
{% macro information(current_user, active_semester_str, role) %} User:
<a
href="{{ url_for('main.index') }}{{ role }}/user/details/?id={{ current_user.id }}"
>{{ current_user }}</a
>
| Active semester: {{ active_semester_str() }} {% if (role == "admin") and
(current_user.has_role("assistant")) %} |
<a href="{{ url_for('main.index') }}assistant">Assistant space</a>. {% elif
(role == "assistant") and (current_user.has_role("admin")) %} |
<a href="{{ url_for('main.index') }}admin">Admin space</a>. {% endif %} {%
endmacro %} {% macro
missing_final_experiment_marks(number_of_missing_final_experiment_marks,
number_of_all_experiment_marks) %}
<p>
Number of <strong>missing</strong> final experiment marks: {{
number_of_missing_final_experiment_marks }} / {{
number_of_all_experiment_marks }}
</p>
{% endmacro %}

View file

@ -0,0 +1,18 @@
{% macro information(current_user, active_semester_str, role) %}
User:
<a
href="{{ url_for('main.index') }}{{ role }}/user/edit/?id={{ current_user.id }}"
>{{ current_user }}</a>
| Active semester: {{ active_semester_str() }}
{% if (role == "admin") and (current_user.has_role("assistant")) %}
|
<a
href="{{ url_for('main.index') }}assistant"
>Assistant space</a>.
{% elif (role == "assistant") and (current_user.has_role("admin")) %}
|
<a
href="{{ url_for('main.index') }}admin"
>Admin space</a>.
{% endif %}
{% endmacro %}