mirror of
https://codeberg.org/Mo8it/AdvLabDB.git
synced 2024-12-04 22:40:30 +00:00
Compare commits
9 commits
f41918e08c
...
726d73211b
Author | SHA1 | Date | |
---|---|---|---|
726d73211b | |||
e244275043 | |||
b9f868bc21 | |||
a1361d6e22 | |||
5e6c809b53 | |||
145a62f0aa | |||
5fa7cc351f | |||
ef38ebe7e8 | |||
664d17cb31 |
12 changed files with 1374 additions and 1267 deletions
|
@ -1,4 +1,4 @@
|
||||||
/.*
|
.*
|
||||||
/dev_data/
|
/dev_data/
|
||||||
/docs/
|
/docs/
|
||||||
/migrations/
|
/migrations/
|
||||||
|
@ -6,3 +6,5 @@
|
||||||
*.pyc
|
*.pyc
|
||||||
__pycache__/
|
__pycache__/
|
||||||
/pyproject.toml
|
/pyproject.toml
|
||||||
|
.containerignore
|
||||||
|
Containerfile
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
AdvLabDB is a database with a web interface for labs.
|
AdvLabDB is a database with a web interface for labs.
|
||||||
|
|
||||||
<!-- TODO: Screenshots -->
|
|
||||||
|
|
||||||
Admins have an interface to manage students, groups, experiments, appointments, marks and analyse data.
|
Admins have an interface to manage students, groups, experiments, appointments, marks and analyse data.
|
||||||
|
|
||||||
Assistants have a separate interface to set marks and schedule appointments with students.
|
Assistants have a separate interface to set marks and schedule appointments with students.
|
||||||
|
@ -34,7 +32,7 @@ After the database initialization, run `sudo docker compose up -d` to start the
|
||||||
If you don't want to use your own reverse proxy, you can use [`compose.yaml`](compose.yaml) directly with Traefik as a reverse proxy:
|
If you don't want to use your own reverse proxy, you can use [`compose.yaml`](compose.yaml) directly with Traefik as a reverse proxy:
|
||||||
|
|
||||||
- Create a directory `~/advlabdb` (You use another path, but then change it also in the following steps).
|
- Create a directory `~/advlabdb` (You use another path, but then change it also in the following steps).
|
||||||
- Move into the directory `~/advlabdb` and clone the repository into it as `repo`: `git clone https://codeberg.org/Mo8it/AdvLabDB.git ~/advlabdb/repo`
|
- Move into the directory `~/advlabdb` and clone the repository into it as `repo`: `git clone https://codeberg.org/mo8it/AdvLabDB.git ~/advlabdb/repo`
|
||||||
- Copy the directory [`deploy/traefik`](deploy/traefik/) into the parent directory `~/advlabdb`: `cp ~/advlabdb/repo/deploy/traefik ~/advlabdb`
|
- Copy the directory [`deploy/traefik`](deploy/traefik/) into the parent directory `~/advlabdb`: `cp ~/advlabdb/repo/deploy/traefik ~/advlabdb`
|
||||||
- Open the file `~/advlabdb/traefik/etc/traefik.yaml`, uncomment `email: EMAIL` and replace `EMAIL` it with your email address. You will receive a notification to this email address if any problem with your SSL certificate (for https) occur, for example certificate expiration.
|
- Open the file `~/advlabdb/traefik/etc/traefik.yaml`, uncomment `email: EMAIL` and replace `EMAIL` it with your email address. You will receive a notification to this email address if any problem with your SSL certificate (for https) occur, for example certificate expiration.
|
||||||
- Open the file `~/advlabdb/traefik/etc/dynamic`, uncomment ``rule: Host(`SERVER_NAME`)`` and replace `SERVER_NAME` with your domain, `advlabdb.mo8it.com` for example.
|
- Open the file `~/advlabdb/traefik/etc/dynamic`, uncomment ``rule: Host(`SERVER_NAME`)`` and replace `SERVER_NAME` with your domain, `advlabdb.mo8it.com` for example.
|
||||||
|
@ -42,7 +40,7 @@ If you don't want to use your own reverse proxy, you can use [`compose.yaml`](co
|
||||||
- Now start Traefik by running the command `sudo docker compose up -d` in the directory `~/advlabdb/traefik`.
|
- Now start Traefik by running the command `sudo docker compose up -d` in the directory `~/advlabdb/traefik`.
|
||||||
- Create directories for the data and logs volumes: `mkdir ~/advlabdb/{data,logs}`
|
- Create directories for the data and logs volumes: `mkdir ~/advlabdb/{data,logs}`
|
||||||
- Change the time zone `TZ` in `~/advlabdb/repo/compose.yaml` if it is not `Europe/Berlin`.
|
- Change the time zone `TZ` in `~/advlabdb/repo/compose.yaml` if it is not `Europe/Berlin`.
|
||||||
- Initilialize the database by running `sudo docker compose run advlabdb manage.py setup init-db` in the directory `~/advlabdb/repo` and following the guide.
|
- Initialize the database by running `sudo docker compose run advlabdb manage.py setup init-db` in the directory `~/advlabdb/repo` and following the guide.
|
||||||
- Start AdvLabDB by running the command `sudo docker compose up -d` in the directory `~/advlabdb/repo`.
|
- Start AdvLabDB by running the command `sudo docker compose up -d` in the directory `~/advlabdb/repo`.
|
||||||
- Visit the domain that you specified in the Traefik configuration as `SERVER_NAME` to test if AdvLabDB is running. If this is not the case, open an issue, maybe I could help :)
|
- Visit the domain that you specified in the Traefik configuration as `SERVER_NAME` to test if AdvLabDB is running. If this is not the case, open an issue, maybe I could help :)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
from datetime import datetime
|
||||||
from flask import flash
|
from flask import flash
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from . import data_dir
|
||||||
from .models import Assistant, Semester, User, db
|
from .models import Assistant, Semester, User, db
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,6 +14,23 @@ def update_final_experiment_and_part_marks():
|
||||||
|
|
||||||
flash("Manually updated all final experiment and part marks.", "success")
|
flash("Manually updated all final experiment and part marks.", "success")
|
||||||
|
|
||||||
|
def backup(backup_prefix):
|
||||||
|
db_path = data_dir / "db/advlabdb.sqlite"
|
||||||
|
|
||||||
|
db_bk_dir = db_path.parent / "backups"
|
||||||
|
db_bk_dir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
now = datetime.now().strftime("%d_%m_%Y_%H_%M_%S")
|
||||||
|
dest = db_bk_dir / f"{backup_prefix}_{now}.sqlite"
|
||||||
|
|
||||||
|
status = subprocess.run(["sqlite3", db_path, f".backup {dest}"]).returncode
|
||||||
|
if status == 0:
|
||||||
|
flash(f"Created a database backup at the path {dest}", "success")
|
||||||
|
else:
|
||||||
|
flash("Failed to create a database backup! Make sure that `sqlite3` is installed on your system!", "danger")
|
||||||
|
|
||||||
|
def manual_backup():
|
||||||
|
backup("manual")
|
||||||
|
|
||||||
def deactivate_assistants():
|
def deactivate_assistants():
|
||||||
user_ids_to_deactivate = db.session.scalars(
|
user_ids_to_deactivate = db.session.scalars(
|
||||||
|
|
|
@ -28,7 +28,7 @@ from wtforms.validators import URL, DataRequired, Email, NumberRange, Optional
|
||||||
from wtforms.widgets import NumberInput
|
from wtforms.widgets import NumberInput
|
||||||
|
|
||||||
from . import data_dir, user_datastore
|
from . import data_dir, user_datastore
|
||||||
from .actions import deactivate_assistants, update_final_experiment_and_part_marks
|
from .actions import deactivate_assistants, update_final_experiment_and_part_marks, manual_backup
|
||||||
from .admin_link_formatters import (
|
from .admin_link_formatters import (
|
||||||
admin_formatter,
|
admin_formatter,
|
||||||
appointment_date_formatter,
|
appointment_date_formatter,
|
||||||
|
@ -1341,6 +1341,9 @@ class ImportView(SecureAdminBaseView):
|
||||||
|
|
||||||
class ActionsView(SecureAdminBaseView):
|
class ActionsView(SecureAdminBaseView):
|
||||||
class ActionsForm(FlaskForm):
|
class ActionsForm(FlaskForm):
|
||||||
|
manual_backup = BooleanField(
|
||||||
|
label="Create a manual database backup",
|
||||||
|
)
|
||||||
update_final_experiment_and_part_marks = BooleanField(
|
update_final_experiment_and_part_marks = BooleanField(
|
||||||
label="Manually update all final experiment and part marks in the active semester",
|
label="Manually update all final experiment and part marks in the active semester",
|
||||||
)
|
)
|
||||||
|
@ -1357,6 +1360,8 @@ class ActionsView(SecureAdminBaseView):
|
||||||
form = ActionsView.ActionsForm()
|
form = ActionsView.ActionsForm()
|
||||||
|
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
|
if form.manual_backup.data:
|
||||||
|
manual_backup()
|
||||||
if form.update_final_experiment_and_part_marks.data:
|
if form.update_final_experiment_and_part_marks.data:
|
||||||
update_final_experiment_and_part_marks()
|
update_final_experiment_and_part_marks()
|
||||||
if form.deactivate_assistants.data:
|
if form.deactivate_assistants.data:
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from shutil import copy2
|
|
||||||
|
|
||||||
from flask import flash
|
from flask import flash
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
|
||||||
from . import data_dir
|
from . import data_dir
|
||||||
from .exceptions import DatabaseImportException
|
from .exceptions import DatabaseImportException
|
||||||
|
from .actions import backup
|
||||||
from .models import (
|
from .models import (
|
||||||
Appointment,
|
Appointment,
|
||||||
Assistant,
|
Assistant,
|
||||||
|
@ -25,10 +25,6 @@ from .models import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def now():
|
|
||||||
return datetime.now().strftime("%d_%m_%Y_%H_%M_%S")
|
|
||||||
|
|
||||||
|
|
||||||
def is_null(entry):
|
def is_null(entry):
|
||||||
return entry == "NULL" or entry == ""
|
return entry == "NULL" or entry == ""
|
||||||
|
|
||||||
|
@ -48,11 +44,6 @@ def not_nullable(entry):
|
||||||
|
|
||||||
|
|
||||||
def importFromFile(filePath: Path):
|
def importFromFile(filePath: Path):
|
||||||
db_path = data_dir / "db/advlabdb.sqlite"
|
|
||||||
|
|
||||||
db_bk_dir = db_path.parent / "backups"
|
|
||||||
db_bk_dir.mkdir(exist_ok=True)
|
|
||||||
|
|
||||||
if filePath.name[-4:] != ".txt":
|
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)!"
|
"The import file has to be a text file with txt extension (.txt at the end of the filename)!"
|
||||||
|
@ -330,11 +321,7 @@ def importFromFile(filePath: Path):
|
||||||
)
|
)
|
||||||
db.session.add(dbAppointment)
|
db.session.add(dbAppointment)
|
||||||
|
|
||||||
# Backup
|
backup(f"before_{dbSemester}_import")
|
||||||
dest = db_bk_dir / f"before_{dbSemester}_import_{now()}.sqlite"
|
|
||||||
copy2(db_path, dest)
|
|
||||||
|
|
||||||
flash(f"Made a backup of the database before committing the import at {dest}")
|
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
|
@ -342,10 +329,7 @@ def importFromFile(filePath: Path):
|
||||||
|
|
||||||
raise ex
|
raise ex
|
||||||
|
|
||||||
dest = db_bk_dir / f"after_{dbSemester}_import_{now()}.sqlite"
|
backup(f"after_{dbSemester}_import")
|
||||||
copy2(db_path, dest)
|
|
||||||
|
|
||||||
flash(f"Made a backup of the database after the import at {dest}")
|
|
||||||
|
|
||||||
flash("\nImport done!", "success")
|
flash("\nImport done!", "success")
|
||||||
flash("Please check that everything worked as expected. Restore the database backup otherwise!", "warning")
|
flash("Please check that everything worked as expected. Restore the database backup otherwise!", "warning")
|
||||||
|
|
|
@ -16,4 +16,4 @@ def randomPassword() -> str:
|
||||||
|
|
||||||
|
|
||||||
def reportBadAttempt(message: str) -> None:
|
def reportBadAttempt(message: str) -> None:
|
||||||
print("BAD ATTEMPT:", message) # TODO: Log
|
print("BAD ATTEMPT:", message)
|
||||||
|
|
|
@ -10,10 +10,9 @@ bp = Blueprint("main", __name__, root_path="/", template_folder="templates")
|
||||||
|
|
||||||
@bp.app_context_processor
|
@bp.app_context_processor
|
||||||
def util_processor():
|
def util_processor():
|
||||||
author_email = "mobitar@students.uni-mainz.de"
|
|
||||||
repo_url = settings["repo_url"]
|
repo_url = settings["repo_url"]
|
||||||
|
|
||||||
footer = f"This website is still under development (beta release)! If you have any questions, find any bugs or want some feature, please write a formless email (german/english) to Mo Bitar: <a href='mailto:{author_email}'>{author_email}</a><br><br>AdvLabDB - <a href={repo_url} target='_blank' rel='noopener noreferrer'>Source code</a>"
|
footer = f"AdvLabDB - <a href={repo_url} target='_blank' rel='noopener noreferrer'>Source code</a>"
|
||||||
|
|
||||||
return dict(active_semester_str=active_semester_str, current_user=current_user, footer=footer)
|
return dict(active_semester_str=active_semester_str, current_user=current_user, footer=footer)
|
||||||
|
|
||||||
|
|
|
@ -2,22 +2,30 @@
|
||||||
<p>Welcome to the administration part of AdvLabDB.</p>
|
<p>Welcome to the administration part of AdvLabDB.</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Disclaimer: The database design behind AdvLabDB with its tables is not trivial. But it should be very efficient after understanding it because it follows database design patterns to avoids duplications.
|
Disclaimer: The database design behind AdvLabDB with its tables is not trivial.
|
||||||
|
But it should be very efficient after understanding it because it follows database design patterns to avoids duplications.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Some tables like <a href="#part_student">Part Student</a> and <a href="#group_experiment">Group Experiment</a> are not intuitive at the first moment. The table <a href="#part_student">Part Student</a> for example can be confused with the table <a href="#student">Student</a>. Therefore, please <strong>read this documentation</strong> to understand all terms and relationships and familiarize yourself with this web interface.
|
Some tables like <a href="#part_student">Part Student</a> and <a href="#group_experiment">Group Experiment</a> are not intuitive at the first moment.
|
||||||
|
The table <a href="#part_student">Part Student</a> for example can be confused with the table <a href="#student">Student</a>.
|
||||||
|
Therefore, please <strong>read this documentation</strong> to understand all terms and relationships and familiarize yourself with this web interface.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
<p>In the first section, menu items are explained. You can find some "How-To"s in the second chapter.</p>
|
<p>
|
||||||
|
In the first section, menu items are explained.
|
||||||
|
You can find some "How-To"s in the second chapter.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h2>Menu items</h2>
|
<h2>Menu items</h2>
|
||||||
<p>In this section, you will find explanations to each menu item on the upper navigation bar.</p>
|
<p>In this section, you will find explanations to each menu item on the upper navigation bar.</p>
|
||||||
|
|
||||||
<h3 id="home">Home</h3>
|
<h3 id="home">Home</h3>
|
||||||
<p>
|
<p>
|
||||||
This is your home page. Here, you find a table with the number of <strong>missing</strong> experiment marks for every assistant in your <a href="#active_semester">active semester</a>. Assistants without missing experiment marks are not showed in the table.
|
This is your home page.
|
||||||
|
Here, you find a table with the number of <strong>missing</strong> experiment marks for every assistant in your <a href="#active_semester">active semester</a>.
|
||||||
|
Assistants without missing experiment marks are not showed in the table.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>Tables</h3>
|
<h3>Tables</h3>
|
||||||
|
@ -41,17 +49,14 @@
|
||||||
<li>The student number is the number assigned to a student from the university (Martikelnummer in German).</li>
|
<li>The student number is the number assigned to a student from the university (Martikelnummer in German).</li>
|
||||||
<li>The uni email is the email address from the university.</li>
|
<li>The uni email is the email address from the university.</li>
|
||||||
<li>
|
<li>
|
||||||
The contact email is an optional email address that is preferred by the student. If the uni and contact email are equal, only the uni email is stored.
|
The contact email is an optional email address that is preferred by the student.
|
||||||
|
If the uni and contact email are equal, only the uni email is stored.
|
||||||
</li>
|
</li>
|
||||||
<li>You can optionally enter the topic and work group of the student's bachelor thesis</li>
|
<li>You can optionally enter the topic and work group of the student's bachelor thesis</li>
|
||||||
<li>
|
<li>The note field is free for you to enter any notes related to this student.</li>
|
||||||
The note field is free for you to enter any notes related to this student.
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h4 id="part_student">
|
<h4 id="part_student">Part Student</h4>
|
||||||
Part Student
|
|
||||||
</h4>
|
|
||||||
<p>
|
<p>
|
||||||
This table shows all part student in your <a href="#active_semester">active semester</a>.
|
This table shows all part student in your <a href="#active_semester">active semester</a>.
|
||||||
</p>
|
</p>
|
||||||
|
@ -61,24 +66,27 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Lets say the student Max is doing his second lab practicum in his bachelor of science program (part 2 in BS). Max would have only one record in the table <a href="#student">Student</a> anyway. Lets call the part of the first practicum "BS1" and that of the second practicum "BS2". He should have a Part Student record with the part "BS1", but he needs a second record for the part "BS2" now. Therefore, you would create a new Part Student record that refers to the Student record and part "BS2".
|
Lets say the student Max is doing his second lab practicum in his bachelor of science program (part 2 in BS).
|
||||||
|
Max would have only one record in the table <a href="#student">Student</a> anyway.
|
||||||
|
Lets call the part of the first practicum "BS1" and that of the second practicum "BS2".
|
||||||
|
He should have a Part Student record with the part "BS1", but he needs a second record for the part "BS2" now.
|
||||||
|
Therefore, you would create a new Part Student record that refers to the Student record and part "BS2".
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
You can put Max in an existing <a href="#group">group</a> or create one containing him later. The group has to be compatible with the part!
|
You can put Max in an existing <a href="#group">group</a> or create one containing him later.
|
||||||
|
The group has to be compatible with the part!
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>The table also shows the experiment marks of the student in this part.</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The table also shows the experiment marks of the student in this part.
|
Every part student has a final part mark shown in this table.
|
||||||
|
The final part mark is calculated as explained in <a href="#experiment">Experiment</a>.
|
||||||
|
Normally, this final part mark is the one relevant for the transcript of records of students.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<h4 id="group">Group</h4>
|
||||||
Every part student has a final part mark shown in this table. The final part mark is calculated as explained in <a href="#experiment">Experiment</a>. Normally, this final part mark is the one relevant for the transcript of records of students.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h4 id="group">
|
|
||||||
Group
|
|
||||||
</h4>
|
|
||||||
<p>
|
<p>
|
||||||
This table shows all groups in your <a href="#active_semester">active semester</a>.
|
This table shows all groups in your <a href="#active_semester">active semester</a>.
|
||||||
</p>
|
</p>
|
||||||
|
@ -87,27 +95,23 @@
|
||||||
A group contains multiple <a href="#part_student">part students</a> that do <a href="#group_experiment">group experiments</a> together.
|
A group contains multiple <a href="#part_student">part students</a> that do <a href="#group_experiment">group experiments</a> together.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h5>
|
<h5>Conditions</h5>
|
||||||
Conditions
|
|
||||||
</h5>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>A group can not be empty.</li>
|
||||||
A group can not be empty.
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
The <a href="#part">part</a> of all <a href="#part_student">part students</a> in a group must have the same <a href="#program">program</a> which will be the program of the group.
|
The <a href="#part">part</a> of all <a href="#part_student">part students</a> in a group must have the same <a href="#program">program</a> which will be the program of the group.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Every group has a unique set of <a href="#program">program</a> and number (BS and 1 for example) in a semester. Both are assigned automatically on creation.
|
Every group has a unique set of <a href="#program">program</a> and number (BS and 1 for example) in a semester.
|
||||||
|
Both are assigned automatically on creation.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
A <a href="#part_student">part student</a> has to be in a group to be able to do experiments. But a student can be alone in a group.
|
A <a href="#part_student">part student</a> has to be in a group to be able to do experiments.
|
||||||
|
But a student can be alone in a group.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h4 id="group_experiment">
|
<h4 id="group_experiment">Group Experiment</h4>
|
||||||
Group Experiment
|
|
||||||
</h4>
|
|
||||||
<p>
|
<p>
|
||||||
This table shows all group experiments in your <a href="#active_semester">active semester</a>.
|
This table shows all group experiments in your <a href="#active_semester">active semester</a>.
|
||||||
</p>
|
</p>
|
||||||
|
@ -125,235 +129,200 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
After creating a group experiment, an <a href="#experiment_mark">experiment mark</a> is created for every <a href="#part_student">part student</a> in the related <a href="#group">group</a>. But an <a href="#experiment_mark">experiment mark</a> can be created manually if needed.
|
After creating a group experiment, an <a href="#experiment_mark">experiment mark</a> is created for every <a href="#part_student">part student</a> in the related <a href="#group">group</a>.
|
||||||
|
But an <a href="#experiment_mark">experiment mark</a> can be created manually if needed.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h5>
|
<h5>Create</h5>
|
||||||
Create
|
|
||||||
</h5>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
Choose a group and a semester experiment from your <a href="#active_semester">active semester</a>.
|
Choose a group and a semester experiment from your <a href="#active_semester">active semester</a>.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
You can additionally directly add two <a href="#appointment">appointments</a> for this group experiment. But you can also create <a href="#appointment">appointments</a> manually later in the <a href="#appointment">Appointment</a> table.
|
You can additionally directly add two <a href="#appointment">appointments</a> for this group experiment.
|
||||||
|
But you can also create <a href="#appointment">appointments</a> manually later in the <a href="#appointment">Appointment</a> table.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
You can add an optional note to the group experiment, but this note is primarily meant for assistants that are responsible for the related <a href="#semester_experiment">semester experiment</a> to add their notes to this group experiment ("Protocol was submitted" for example).
|
You can add an optional note to the group experiment, but this note is primarily meant for assistants that are responsible for the related <a href="#semester_experiment">semester experiment</a> to add their notes to this group experiment ("Protocol was submitted" for example).
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h4 id="appointment">
|
<h4 id="appointment">Appointment</h4>
|
||||||
Appointment
|
|
||||||
</h4>
|
|
||||||
<p>
|
<p>
|
||||||
This table shows all appointments in your <a href="#active_semester">active semester</a>.
|
This table shows all appointments in your <a href="#active_semester">active semester</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
You can create an appointment for a <a href="#group_experiment">group experiment</a> by specifying a date which can be changed by the assistant(s) of the related <a href="#semester_experiment">semester experiment</a> after communicating with the students. The date is only a first proposal.
|
You can create an appointment for a <a href="#group_experiment">group experiment</a> by specifying a date which can be changed by the assistant(s) of the related <a href="#semester_experiment">semester experiment</a> after communicating with the students.
|
||||||
|
The date is only a first proposal.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
A "special" appointment means that the students prefer the appointment to be in the non-lecture period of the semester. This is not enforced internally.
|
A "special" appointment means that the students prefer the appointment to be in the non-lecture period of the semester.
|
||||||
|
This is not enforced internally.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
If the related <a href="#semester_experiment">semester experiment</a> only has one assistant, then this assistant is assigned automatically during creation. Otherwise, you have to assign one responsible assistant.
|
If the related <a href="#semester_experiment">semester experiment</a> only has one assistant, then this assistant is assigned automatically during creation.
|
||||||
|
Otherwise, you have to assign one responsible assistant.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h4 id="experiment_mark">
|
<h4 id="experiment_mark">Experiment Mark</h4>
|
||||||
Experiment Mark
|
|
||||||
</h4>
|
|
||||||
<p>
|
<p>
|
||||||
This table shows all experiment marks in your <a href="#active_semester">active semester</a>.
|
This table shows all experiment marks in your <a href="#active_semester">active semester</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
An experiment mark is assigned to a <a href="#part_student">part student</a> for one <a href="#group_experiment">group experiment</a>. Although students work in groups, they get individual marks, not group marks.
|
An experiment mark is assigned to a <a href="#part_student">part student</a> for one <a href="#group_experiment">group experiment</a>.
|
||||||
|
Although students work in groups, they get individual marks, not group marks.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
After creating a <a href="#group_experiment">group experiment</a>, an experiment mark is created for every <a href="#part_student">part student</a> in the related <a href="#group">group</a>. But an experiment mark can be created manually if needed.
|
After creating a <a href="#group_experiment">group experiment</a>, an experiment mark is created for every <a href="#part_student">part student</a> in the related <a href="#group">group</a>.
|
||||||
|
But an experiment mark can be created manually if needed.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Experiment marks consist of oral and protocol marks and are assigned by the responsible assistant(s). But admins can change marks, too. The columns "Assistant" and "Admin" keep track of the last assistant or admin that changed the experiment mark.
|
Experiment marks consist of oral and protocol marks and are assigned by the responsible assistant(s).
|
||||||
|
But admins can change marks, too.
|
||||||
|
The columns "Assistant" and "Admin" keep track of the last assistant or admin that changed the experiment mark.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The column "Final Experiment Mark" shows the automatically calculated student's final mark for this experiment. This mark is calculated with respect to the weights of the related <a href="#semester_experiment">semester experiment</a>.
|
The column "Final Experiment Mark" shows the automatically calculated student's final mark for this experiment.
|
||||||
|
This mark is calculated with respect to the weights of the related <a href="#semester_experiment">semester experiment</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h4 id="experiment">
|
<!-- TODO
|
||||||
Experiment
|
<h4 id="experiment">Experiment</h4>
|
||||||
</h4>
|
<p></p>
|
||||||
|
|
||||||
|
<h4 id="semester_experiment">Semester Experiment</h4>
|
||||||
|
<p></p>
|
||||||
|
|
||||||
|
<h4 id="semester">Semester</h4>
|
||||||
|
<p></p>
|
||||||
|
|
||||||
|
<h4 id="part">Part</h4>
|
||||||
|
<p></p>
|
||||||
|
|
||||||
|
<h4 id="assistant">Assistant</h4>
|
||||||
|
<p></p>
|
||||||
|
|
||||||
|
<h4 id="admin">Admin</h4>
|
||||||
|
<p></p>
|
||||||
|
|
||||||
|
<h4 id="user">User</h4>
|
||||||
|
<p></p>
|
||||||
|
|
||||||
|
<h4 id="program">Program</h4>
|
||||||
|
<p></p>
|
||||||
|
|
||||||
|
<h3 id="import">Import</h3>
|
||||||
|
<p></p>
|
||||||
|
|
||||||
|
<h3 id="actions">Actions</h3>
|
||||||
|
<p></p>
|
||||||
|
|
||||||
|
<h3 id="analysis">Analysis</h3>
|
||||||
|
<p></p>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<h3 id="docs">Docs</h3>
|
||||||
|
<p>This is a link which leads you to this page.</p>
|
||||||
|
|
||||||
|
<h3 id="user_settings">User settings</h3>
|
||||||
<p>
|
<p>
|
||||||
|
In the user settings, You can change your active semester, user information and password.
|
||||||
|
Don't forget to click on <em>Save</em> after changing any option.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h4 id="semester_experiment">
|
<h4 id="active_semester">Active semester</h4>
|
||||||
Semester Experiment
|
|
||||||
</h4>
|
|
||||||
<p>
|
<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>
|
</p>
|
||||||
|
|
||||||
<h4 id="semester">
|
<h4>User information</h4>
|
||||||
Semester
|
|
||||||
</h4>
|
|
||||||
<p>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h4 id="part">
|
|
||||||
Part
|
|
||||||
</h4>
|
|
||||||
<p>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h4 id="assistant">
|
|
||||||
Assistant
|
|
||||||
</h4>
|
|
||||||
<p>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h4 id="admin">
|
|
||||||
Admin
|
|
||||||
</h4>
|
|
||||||
<p>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h4 id="user">
|
|
||||||
User
|
|
||||||
</h4>
|
|
||||||
<p>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h4 id="program">
|
|
||||||
Program
|
|
||||||
</h4>
|
|
||||||
<p>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3 id="import">
|
|
||||||
Import
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3 id="actions">
|
|
||||||
Actions
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3 id="analysis">
|
|
||||||
Analysis
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3 id="docs">
|
|
||||||
Docs
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
This is a link which leads you to this page.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3 id="user_settings">
|
|
||||||
User settings
|
|
||||||
</h3>
|
|
||||||
<p>
|
|
||||||
In the user settings, You can change your active semester, user information and password. Don't forget to click on <em>Save</em> after changing any option.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h4 id="active_semester">
|
|
||||||
Active semester
|
|
||||||
</h4>
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<h4>
|
|
||||||
User information
|
|
||||||
</h4>
|
|
||||||
<p>
|
<p>
|
||||||
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.
|
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.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h4>
|
<h4>Password</h4>
|
||||||
Password
|
|
||||||
</h4>
|
|
||||||
<p>
|
<p>
|
||||||
You can generate a new random password by 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.
|
You can generate a new random password by 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>
|
</p>
|
||||||
|
<p>If you forget your password, you can reset it using the CLI on the server.</p>
|
||||||
|
|
||||||
|
<!-- TODO
|
||||||
|
<h2>How-Tos</h2>
|
||||||
|
<h3>Start a new semester</h3>
|
||||||
|
<p></p>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<h3>Tables</h3>
|
||||||
|
|
||||||
|
<h4>Create a record</h4>
|
||||||
<p>
|
<p>
|
||||||
If you forget your password, you can reset it using the CLI on the server.
|
You can create a record in almost all tables.
|
||||||
|
To do so, click on the "Create" button at the top of a table.
|
||||||
|
Then, fill the form and click on the "Save" button.
|
||||||
|
Required fields are marks with a red star near their label.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>
|
<h4>Edit a record</h4>
|
||||||
How-Tos
|
|
||||||
</h2>
|
|
||||||
<h3>
|
|
||||||
Start a new semester
|
|
||||||
</h3>
|
|
||||||
<p>
|
<p>
|
||||||
|
You can edit records of almost all tables.
|
||||||
|
To do so, click on the pen symbol on the left of a record.
|
||||||
|
Then, fill the form and click on the "Save" button.
|
||||||
|
Required fields are marks with a red star near their label.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>
|
<h4>Show record details</h4>
|
||||||
Table related
|
|
||||||
</h3>
|
|
||||||
<h4>
|
|
||||||
Create a record
|
|
||||||
</h4>
|
|
||||||
<p>
|
<p>
|
||||||
You can create a record in almost all tables. To do so, click on the "Create" button at the top of a table. Then, fill the form and click on the "Save" button. Required fields are marks with a red star near their label.
|
Almost all tables have an eye symbol near a record which shows details for that record.
|
||||||
|
Some fields in this view are not shown in the table itself for horizontal space reasons.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h4>
|
<h4>Delete records</h4>
|
||||||
Edit a record
|
|
||||||
</h4>
|
|
||||||
<p>
|
<p>
|
||||||
You can edit records of almost all tables. To do so, click on the pen symbol on the left of a record. Then, fill the form and click on the "Save" button. Required fields are marks with a red star near their label.
|
Almost all tables have a trash can symbol near records to delete them.
|
||||||
|
Deleting a record is not recommended and often not possible because of database dependencies.
|
||||||
|
If you really want to delete a record, then try to delete it by clicking on the trash can symbol.
|
||||||
|
If you get any errors related to some dependency, then delete dependent records when you are sure about it and then try to delete the record again.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h4>
|
<h4>Go to record</h4>
|
||||||
Show record details
|
|
||||||
</h4>
|
|
||||||
<p>
|
<p>
|
||||||
Almost all tables have an eye symbol near a record which shows details for that record. Some fields in this view are not shown in the table itself for horizontal space reasons.
|
If a record in a table contains records of other tables, then these records are highlighted as a link.
|
||||||
|
Clicking on a record link takes you to its details.
|
||||||
|
You can edit this record then by clicking on the edit button at the top if needed.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h4>
|
<h4>Sort by column</h4>
|
||||||
Delete records
|
|
||||||
</h4>
|
|
||||||
<p>
|
<p>
|
||||||
Almost all tables have a trash can symbol near records to delete them. Deleting a record is not recommended and often not possible because of database dependencies. If you really want to delete a record, then try to delete it by clicking on the trash can symbol. If you get any errors related to some dependency, then delete dependent records when you are sure about it and then try to delete the record again.
|
You can sort a table by a column by clicking on the column label if it is highlighted as a link.
|
||||||
|
Clicking on the column label again sorts in descending order.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h4>
|
<h4>Apply table filter</h4>
|
||||||
Go to record
|
|
||||||
</h4>
|
|
||||||
<p>
|
<p>
|
||||||
If a record in a table contains records of other tables, then these records are highlighted as a link. Clicking on a record link takes you to its details. You can edit this record then by clicking on the edit button at the top if needed.
|
Some tables have filters.
|
||||||
|
Those table have a "Add Filter" button at the top which allows you to choose a filter to apply.
|
||||||
</p>
|
</p>
|
||||||
|
<p>You can apply multiple filters at once!</p>
|
||||||
|
|
||||||
<h4>
|
<h4>Export table</h4>
|
||||||
Sort by column
|
|
||||||
</h4>
|
|
||||||
<p>
|
|
||||||
You can sort a table by a column by clicking on the column label if it is highlighted as a link. Clicking on the column label again sorts in descending order.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h4>
|
|
||||||
Apply table filter
|
|
||||||
</h4>
|
|
||||||
<p>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h4>
|
|
||||||
Export table
|
|
||||||
</h4>
|
|
||||||
<p>
|
<p>
|
||||||
|
Most tables can be exported. Those tables have an "Export button" at the top which exports the table as a CSV file.
|
||||||
</p>
|
</p>
|
||||||
|
<p>Exporting a table respects the applied filters.</p>
|
||||||
|
|
|
@ -5,8 +5,6 @@ http:
|
||||||
# Uncomment following line and replace SERVER_NAME!
|
# Uncomment following line and replace SERVER_NAME!
|
||||||
# rule: Host(`SERVER_NAME`)
|
# rule: Host(`SERVER_NAME`)
|
||||||
service: advlabdb
|
service: advlabdb
|
||||||
tls:
|
|
||||||
certResolver: le
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
advlabdb:
|
advlabdb:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
global:
|
global:
|
||||||
checkNewVersion: false
|
checkNewVersion: false
|
||||||
sendAnonymousUsage: true
|
sendAnonymousUsage: false
|
||||||
|
|
||||||
log:
|
log:
|
||||||
filePath: /volumes/logs/traefik.log
|
filePath: /volumes/logs/traefik.log
|
||||||
|
@ -18,6 +18,9 @@ entryPoints:
|
||||||
|
|
||||||
websecure:
|
websecure:
|
||||||
address: :443
|
address: :443
|
||||||
|
http:
|
||||||
|
tls:
|
||||||
|
certResolver: le
|
||||||
|
|
||||||
certificatesResolvers:
|
certificatesResolvers:
|
||||||
le:
|
le:
|
||||||
|
@ -29,7 +32,7 @@ certificatesResolvers:
|
||||||
|
|
||||||
accessLog:
|
accessLog:
|
||||||
filePath: /volumes/logs/access.log
|
filePath: /volumes/logs/access.log
|
||||||
bufferingSize: 100
|
bufferingSize: 128
|
||||||
|
|
||||||
providers:
|
providers:
|
||||||
file:
|
file:
|
||||||
|
|
1238
poetry.lock
generated
1238
poetry.lock
generated
File diff suppressed because it is too large
Load diff
987
requirements.txt
987
requirements.txt
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue