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

Organize docs and scripts

This commit is contained in:
Mo 2022-05-08 17:29:45 +02:00
parent daf0b6287f
commit 1f89e4aaed
27 changed files with 520 additions and 454 deletions

View file

@ -0,0 +1,16 @@
from advlabdb.scripts.maintain.shared import show_update_datetime
from advlabdb.scripts.terminal_utils import box, run
def main():
show_update_datetime()
box("Update system packages")
run("sudo apt update")
run("sudo apt upgrade -y")
run("sudo reboot")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,11 @@
from datetime import datetime
from advlabdb.scripts.terminal_utils import box, spaced_hl
def show_update_datetime():
spaced_hl()
dt = datetime.now().strftime("%d.%m.%Y %H:%M:%S")
box(dt, "Update on")

View file

@ -0,0 +1,20 @@
# TODO: Port to Python
# Needed packages: asciidoctor
# List of documentation files
# DOC_FILE_NAMES=$(fd -d 1 -t f --extension adoc --exclude "README.adoc")
#
# for doc_file_name in "${DOC_FILE_NAMES[@]}"; do
# DOC_FILE_NAME_WITHOUT_EXTENSION=${doc_file_name::-5}
# OUTPUT_PATH=../advlabdb/templates/docs/$DOC_FILE_NAME_WITHOUT_EXTENSION.html
#
# # Convert to html with asciidoctor
# asciidoctor -v --backend html5 "$doc_file_name" -o "$OUTPUT_PATH"
#
# # Add the Jinja raw tag
# sed -i "1i {% raw %}" "$OUTPUT_PATH"
# echo -e "\n{% endraw %}" >>"$OUTPUT_PATH"
#
# # Done
# echo "Generated $OUTPUT_PATH"
# done

View file

@ -0,0 +1,23 @@
from advlabdb.scripts.maintain.shared import show_update_datetime
from advlabdb.scripts.setup.shared import LOCAL_BIN, install_latest_pipx, poetry_update
from advlabdb.scripts.terminal_utils import box, run
def main():
show_update_datetime()
box("Upgrade pipx")
install_latest_pipx()
box("Upgrade pipx packages")
pipx_bin = LOCAL_BIN / "pipx"
run(f"{pipx_bin} upgrade-all --include-injected")
# TODO: Backup
box("Install latest Poetry packages")
poetry_update()
if __name__ == "__main__":
main()

View file

@ -1,77 +1,16 @@
import sys
from pathlib import Path
from email_validator import validate_email
from flask_security import hash_password
from advlabdb import app, db, user_datastore
from advlabdb.independent_funs import randomPassword
from advlabdb.models import MAX_YEAR, MIN_YEAR, Admin, Semester
scripts_dir = Path(__file__).parent.absolute() / "scripts"
sys.path.insert(0, str(scripts_dir))
from shared import box
def validating_input(
prompt,
options=None,
format_function=lambda ans: ans,
check_constraints_function=lambda ans: True,
):
if options is not None:
prompt += " ["
for opt in options[:-1]:
prompt += opt + "/"
prompt += options[-1] + "]: "
lowered_options = [opt.lower() for opt in options]
def adj_check_constraints_function(ans):
return ans.lower() in lowered_options and check_constraints_function(ans)
else:
prompt += ": "
adj_check_constraints_function = check_constraints_function
ans = None
first_run = True
while ans is None or not adj_check_constraints_function(ans):
if not first_run:
print("Invalid input!")
else:
first_run = False
ans = input(prompt)
try:
ans = format_function(ans)
except Exception as ex:
ans = None
return ans
def confirm(prompt):
ans = validating_input(
prompt,
options=("y", "n"),
format_function=lambda ans: ans.lower(),
)
if ans == "y":
return True
else:
return False
from advlabdb.scripts.terminal_utils import box, confirm, validating_input
def main():
print("This script should only be used to initialize the database after setting up a server")
print("The old database will be DELETED and a new database will be created.")
if not confirm("Are you sure that you want to continue?"):
print("Aborted!")
return
@ -137,7 +76,7 @@ def main():
box(admin_password, "Admin password")
print("Done! Successfull database initialization.")
print("Done database initialization!")
if __name__ == "__main__":

View file

@ -0,0 +1,97 @@
from pathlib import Path
from advlabdb.scripts.setup.shared import (
LOCAL_BIN,
LOGS_DIR,
install_latest_pipx,
poetry_update,
)
from advlabdb.scripts.terminal_utils import run, step
def main():
file_dir = Path(__file__).parent.absolute()
step("Update system packages")
run("sudo apt update")
run("sudo apt dist-upgrade")
step("Remove unused packages")
run("sudo apt autoremove")
step("Install needed system packages")
run("sudo apt install python3 python3-pip python3-venv ufw nginx systemd asciidoctor -y")
step("Install optional system packages")
run("sudo apt install htop")
step("Setup firewall")
run("sudo ufw default allow outgoing")
run("sudo ufw default deny incoming")
run("sudo ufw allow ssh")
run("sudo ufw allow http/tcp")
run("sudo ufw allow https/tcp")
run("sudo ufw enable")
run("sudo ufw status")
step("Enable Gunicorn")
gunicorn_service_file = file_dir / "gunicorn.service"
run(f"sudo cp -v {gunicorn_service_file} /etc/systemd/system/")
run("sudo systemctl enable gunicorn")
step("Setup Nginx")
for dir_appendix in ("available", "enabled"):
run(f"sudo rm -v /etc/nginx/sites-{dir_appendix}/default")
nginx_conf_file = file_dir / "advlabdb.conf"
run(f"sudo cp -v {nginx_conf_file} /etc/nginx/sites-available/")
run("sudo ln -v -s /etc/nginx/sites-available/advlabdb.conf /etc/nginx/sites-enabled/")
run("sudo systemctl enable nginx")
step("Install pipx")
install_latest_pipx()
pipx_bin = LOCAL_BIN / "pipx"
step("Install Poetry")
run(f"{pipx_bin} install poetry")
# Place virtual environments in the root directory of the project
# The virtual environment will then be found in /home/admin/advlabdb/.venv
poetry_bin = LOCAL_BIN / "poetry"
run(f"{poetry_bin} config virtualenvs.in-project true")
step("Install Certbot")
run(f"{pipx_bin} install certbot")
run(f"{pipx_bin} inject certbot certbot-nginx")
step("Setup Certbot")
certbot_bin = LOCAL_BIN / "certbot"
run(f"sudo {certbot_bin} --nginx")
run(
f"echo \"0 0,12 * * * root python3 -c 'import random; import time; time.sleep(random.random() * 3600)' && sudo {certbot_bin} renew -q\" | sudo tee -a /etc/crontab"
)
step("Setup update cron jobs")
user_update_script = file_dir / "user_update.py"
user_update_log = LOGS_DIR / "user_update.log"
# Every Sunday at 04:00
run(f'echo "0 4 * * 0 admin python3 -u {user_update_script} &>> {user_update_log}" | sudo tee -a /etc/crontab')
root_update_script = file_dir / "root_update.py"
root_update_log = LOGS_DIR / "root_update.log"
# Every Sunday at 04:15
run(f'echo "15 4 * * 0 root python3 -u {root_update_script} &>> {root_update_log}" | sudo tee -a /etc/crontab')
step("Install latest Poetry packages")
poetry_update()
step("Deactivate the 'root' user")
run("sudo passwd -l root")
step("Reboot")
run("sudo reboot")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,20 @@
from advlabdb.scripts.setup.logged_server_setup import (
__file__ as logged_server_setup_script,
)
from advlabdb.scripts.setup.shared import LOGS_DIR
from advlabdb.scripts.terminal_utils import run
def main():
# Create logs directory
run(f"sudo mkdir -v -p {LOGS_DIR}")
run(f"sudo chown -R admin:admin {LOGS_DIR}")
log_file = LOGS_DIR / "server_setup.log"
# Start actual server setup script with logging
run(f"python3 -u {logged_server_setup_script} | tee {log_file}")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,17 @@
from pathlib import Path
from advlabdb import __file__ as advlabdb_init_path # Points to __init__.py
from advlabdb.scripts.terminal_utils import run
LOCAL_BIN = Path("/home/admin/.local/bin/")
LOGS_DIR = Path("/var/log/advlabdb")
def install_latest_pipx():
run("pip install --user --upgrade pipx")
def poetry_update():
poetry_bin = LOCAL_BIN / "poetry"
advlabdb_root_dir = Path(advlabdb_init_path).parents[1]
run(f"{poetry_bin} update", cwd=advlabdb_root_dir)

View file

@ -0,0 +1,95 @@
import subprocess
from getpass import getpass
def run(command, **kwargs):
return subprocess.run(command, shell=True, **kwargs)
def box(message, context=None):
text_line = "| "
if context is not None:
text_line += context + ": "
text_line += message + " |"
separator = "=" * len(text_line)
print()
print(separator)
print(text_line)
print(separator)
print()
def step(message):
continue_message = "-> Press ENTER to continue or Ctrl+C to interrupt the script <-"
upper_separator = "_" * len(continue_message)
print()
print(upper_separator)
box(message, "Next step")
print(continue_message)
getpass("")
print()
def spaced_hl():
print("\n\n" + "_" * 20 + "\n\n")
def validating_input(
prompt,
options=None,
format_function=lambda ans: ans,
check_constraints_function=lambda ans: True,
):
if options is not None:
prompt += " ["
for opt in options[:-1]:
prompt += opt + "/"
prompt += options[-1] + "]: "
lowered_options = [opt.lower() for opt in options]
def adj_check_constraints_function(ans):
return ans.lower() in lowered_options and check_constraints_function(ans)
else:
prompt += ": "
adj_check_constraints_function = check_constraints_function
ans = None
first_run = True
while ans is None or not adj_check_constraints_function(ans):
if not first_run:
print("Invalid input!")
else:
first_run = False
ans = input(prompt)
try:
ans = format_function(ans)
except Exception as ex:
ans = None
return ans
def confirm(prompt):
ans = validating_input(
prompt,
options=("y", "n"),
format_function=lambda ans: ans.lower(),
)
if ans == "y":
return True
else:
return False

View file

@ -0,0 +1,150 @@
from datetime import date
from flask_security import hash_password
from advlabdb import app, db, user_datastore
from advlabdb.models import *
def main():
with app.app_context():
with db.session.begin():
db.drop_all()
db.create_all()
program1 = Program(label="BS")
program2 = Program(label="MS")
program3 = Program(label="BE")
db.session.add(program1)
db.session.add(program2)
db.session.add(program3)
sem1 = Semester.customInit(label="SS", year=22)
sem2 = Semester.customInit(label="WS", year=22)
db.session.add(sem1)
db.session.add(sem2)
partKwargs = [
{"program": program1, "number": 1},
{"program": program1, "number": 2},
{"program": program2, "number": 1},
{"program": program2, "number": 2},
{"program": program3, "number": 1},
]
for kwargs in partKwargs:
db.session.add(Part(semester=sem1, **kwargs))
sem2.transferPartsFrom(sem1)
part1 = sem2.parts[0]
part2 = sem2.parts[2]
student1 = Student(student_number=123, first_name="Mo", last_name="Bit", uni_email="m@test.com")
student2 = Student(student_number=1232, first_name="Mo2", last_name="Bit", uni_email="m2@test.com")
student3 = Student(student_number=1233, first_name="Mo3", last_name="Bit3", uni_email="m3@test.com")
db.session.add(student1)
db.session.add(student2)
db.session.add(student3)
ps1 = PartStudent.customInit(student=student1, part=part1)
ps2 = PartStudent.customInit(student=student2, part=part1)
ps3 = PartStudent.customInit(student=student3, part=part2)
db.session.add(ps1)
db.session.add(ps2)
db.session.add(ps3)
g1 = Group.customInit(part_students=[ps1, ps2])
g2 = Group.customInit(part_students=[ps3])
db.session.add(g1)
db.session.add(g2)
ex1 = Experiment(
number=1,
program=program1,
title="exp",
room="123",
building="phy",
responsibility="none",
duration_in_days=2,
)
ex2 = Experiment(
number=1,
program=program2,
title="exp2",
room="123",
building="phy",
responsibility="none",
duration_in_days=2,
)
db.session.add(ex1)
db.session.add(ex2)
sx1 = SemesterExperiment(experiment=ex1, semester=sem2)
sx2 = SemesterExperiment(experiment=ex2, semester=sem2)
db.session.add(sx1)
db.session.add(sx2)
gx1 = GroupExperiment.customInit(semester_experiment=sx1, group=g1)
gx2 = GroupExperiment.customInit(semester_experiment=sx2, group=g2)
db.session.add(gx1)
db.session.add(gx2)
adminRole = user_datastore.create_role(name="admin")
assistantRole = user_datastore.create_role(name="assistant")
admin_user = user_datastore.create_user(
email="admin@advlabdb.de",
password=hash_password("admin"),
roles=[adminRole],
first_name="Peter",
last_name="Blümler",
)
admin = Admin(user=admin_user)
db.session.add(admin)
us1 = user_datastore.create_user(
email="test@protonmail.com",
password=hash_password("h1"),
roles=[assistantRole],
first_name="As1",
last_name="l",
phone_number="012333212",
mobile_phone_number="012334123",
)
us2 = user_datastore.create_user(
email="test2@protonmail.com",
password=hash_password("h2"),
roles=[assistantRole],
first_name="As2",
last_name="l",
)
as1 = Assistant(user=us1)
as2 = Assistant(user=us2)
as1.semester_experiments.append(sx1)
as2.semester_experiments.append(sx2)
db.session.add(as1)
db.session.add(as2)
ap1 = Appointment.customInit(date=date(2021, 3, 21), special=True, group_experiment=gx1, assistant=as1)
ap2 = Appointment.customInit(date=date(2021, 3, 22), special=True, group_experiment=gx2, assistant=as2)
db.session.add(ap1)
db.session.add(ap2)
if __name__ == "__main__":
main()

View file

@ -1,23 +0,0 @@
#!/usr/bin/bash
# Needed packages: fd (find alternative), asciidoctor
# You have to cd into the docs directory where this script is located before running it!
# List of documentation files
DOC_FILE_NAMES=$(fd -d 1 -t f --extension adoc --exclude "README.adoc")
for doc_file_name in "${DOC_FILE_NAMES[@]}"; do
DOC_FILE_NAME_WITHOUT_EXTENSION=${doc_file_name::-5}
OUTPUT_PATH=../advlabdb/templates/docs/$DOC_FILE_NAME_WITHOUT_EXTENSION.html
# Convert to html with asciidoctor
asciidoctor -v --backend html5 "$doc_file_name" -o "$OUTPUT_PATH"
# Add the Jinja raw tag
sed -i "1i {% raw %}" "$OUTPUT_PATH"
echo -e "\n{% endraw %}" >>"$OUTPUT_PATH"
# Done
echo "Generated $OUTPUT_PATH"
done

View file

@ -0,0 +1,68 @@
= Server setup
== Setup scripts
`ssh` as `root`:
[source,bash]
----
ssh root@SERVER_NAME
----
Run the following (as root):
[source,bash]
----
# Install needed packages
apt update
apt install sudo python3 -y
# Add a sudo user with the name 'admin'
sudo useradd admin
sudo usermod -aG sudo admin
sudo mkhomedir_helper admin
# Enter a new password for 'admin'
sudo passwd admin
# Break the SSH connection
exit
----
Copy the repository to the server into `/home/admin/advlabdb`.
`ssh` again with the new user `admin` and password:
[source,bash]
----
ssh admin@SERVER_NAME
----
Run the following:
[source,bash]
----
# Run server setup script
python3 ~/advlabdb/scripts/server_setup.py
----
Change server_name in advlabdb.conf
== Stop ssh to root
// TODO: Add blocking password access
IMPORTANT: This step is important for security!
Change
----
PermitRootLogin yes
----
to
----
PermitRootLogin no
----
in the config file `/etc/ssh/sshd_config`

View file

@ -1,93 +0,0 @@
#!/usr/bin/env python3
import sys
from pathlib import Path
script_dir = Path(__file__).parent.absolute()
sys.path.insert(0, str(script_dir))
from shared import install_latest_pipx, local_bin, poetry_update, run, step
logs_dir = Path("/var/log/advlabdb/")
step("Update system packages")
run("sudo apt update")
run("sudo apt dist-upgrade")
step("Remove unused packages")
run("sudo apt autoremove")
step("Install needed system packages")
run("sudo apt install python3 python3-pip python3-venv ufw nginx systemd -y")
step("Install optional system packages")
run("sudo apt install htop")
step("Setup firewall")
run("sudo ufw default allow outgoing")
run("sudo ufw default deny incoming")
run("sudo ufw allow ssh")
run("sudo ufw allow http/tcp")
# TODO: Setup https
# run("sudo ufw allow https/tcp")
run("sudo ufw enable")
run("sudo ufw status")
step("Enable Gunicorn")
gunicorn_service_file = script_dir / "gunicorn.service"
run(f"sudo cp -v {gunicorn_service_file} /etc/systemd/system/")
run("sudo systemctl enable gunicorn")
step("Setup Nginx")
for dir_appendix in ("available", "enabled"):
run(f"sudo rm -v /etc/nginx/sites-{dir_appendix}/default")
nginx_conf_file = script_dir / "advlabdb.conf"
run(f"sudo cp -v {nginx_conf_file} /etc/nginx/sites-available/")
run("sudo ln -v -s /etc/nginx/sites-available/advlabdb.conf /etc/nginx/sites-enabled/")
run("sudo systemctl enable nginx")
step("Install pipx")
install_latest_pipx()
pipx_bin = local_bin / "pipx"
step("Install Poetry")
run(f"{pipx_bin} install poetry")
# Place virtual environments in the root directory of the project
# The virtual environment will then be found in /home/admin/advlabdb/.venv
poetry_bin = local_bin / "poetry"
run(f"{poetry_bin} config virtualenvs.in-project true")
step("Install Certbot")
run(f"{pipx_bin} install certbot")
run(f"{pipx_bin} inject certbot certbot-nginx")
step("Setup Certbot")
certbot_bin = local_bin / "certbot"
run(f"sudo {certbot_bin} --nginx")
run(
f"echo \"0 0,12 * * * root python3 -c 'import random; import time; time.sleep(random.random() * 3600)' && sudo {certbot_bin} renew -q\" | sudo tee -a /etc/crontab"
)
step("Setup update cron jobs")
user_update_script = script_dir / "user_update.py"
user_update_log = logs_dir / "user_update.log"
# Every Sunday at 04:00
run(f'echo "0 4 * * 0 admin python3 -u {user_update_script} &>> {user_update_log}" | sudo tee -a /etc/crontab')
root_update_script = script_dir / "root_update.py"
root_update_log = logs_dir / "root_update.log"
# Every Sunday at 04:15
run(f'echo "15 4 * * 0 root python3 -u {root_update_script} &>> {root_update_log}" | sudo tee -a /etc/crontab')
step("Install latest Poetry packages")
poetry_update(script_dir)
step("Deactivate the 'root' user")
run("sudo passwd -l root")
step("Reboot")
run("sudo reboot")

View file

@ -1,23 +0,0 @@
#!/usr/bin/env python3
import sys
from datetime import datetime
from pathlib import Path
script_dir = Path(__file__).parent.absolute()
sys.path.insert(0, str(script_dir))
from shared import box, run, spaced_hl
spaced_hl()
dt = datetime.now().strftime("%d.%m.%Y %H:%M:%S")
box(dt, "Update on")
box("Update system packages")
run("sudo apt update")
run("sudo apt upgrade -y")
run("sudo reboot")

View file

@ -1,22 +0,0 @@
#!/usr/bin/env python3
import sys
from pathlib import Path
script_dir = Path(__file__).parent.absolute()
sys.path.insert(0, str(script_dir))
from shared import run
logs_dir = Path("/var/log/advlabdb/")
# Create logs directory
run(f"sudo mkdir -v -p {logs_dir}")
run(f"sudo chown -R admin:admin {logs_dir}")
logged_server_setup_script = script_dir / "logged_server_setup.py"
log_file = logs_dir / "server_setup.log"
# Start actual server setup script with logging
run(f"python3 -u {logged_server_setup_script} | tee {log_file}")

View file

@ -1,55 +0,0 @@
#!/usr/bin/env python3
import subprocess
from getpass import getpass
from pathlib import Path
local_bin = Path("/home/admin/.local/bin/")
def run(command, **kwargs):
return subprocess.run(command, shell=True, **kwargs)
def box(message, context=None):
text_line = "| "
if context is not None:
text_line += context + ": "
text_line += message + " |"
separator = "=" * len(text_line)
print()
print(separator)
print(text_line)
print(separator)
print()
def step(message):
continue_message = "-> Press ENTER to continue or Ctrl+C to interrupt the script <-"
upper_separator = "_" * len(continue_message)
print()
print(upper_separator)
box(message, "Next step")
print(continue_message)
getpass("")
print()
def install_latest_pipx():
run("pip install --user --upgrade pipx")
def poetry_update(script_dir):
poetry_bin = local_bin / "poetry"
run(f"{poetry_bin} update", cwd=script_dir / "..")
def spaced_hl():
print("\n\n___________________\n\n")

View file

@ -1,29 +0,0 @@
#!/usr/bin/env python3
import sys
from datetime import datetime
from pathlib import Path
script_dir = Path(__file__).parent.absolute()
sys.path.insert(0, str(script_dir))
from shared import box, install_latest_pipx, local_bin, poetry_update, run, spaced_hl
spaced_hl()
dt = datetime.now().strftime("%d.%m.%Y %H:%M:%S")
box(dt, "Update on")
box("Upgrade pipx")
install_latest_pipx()
box("Upgrade pipx packages")
pipx_bin = local_bin / "pipx"
run(f"{pipx_bin} upgrade-all --include-injected")
# TODO: Backup
box("Install latest Poetry packages")
poetry_update(script_dir)

145
testDB.py
View file

@ -1,145 +0,0 @@
from datetime import date
from flask_security import hash_password
from advlabdb import app, db, user_datastore
from advlabdb.models import *
with app.app_context():
db.drop_all()
db.create_all()
program1 = Program(label="BS")
program2 = Program(label="MS")
program3 = Program(label="BE")
db.session.add(program1)
db.session.add(program2)
db.session.add(program3)
sem1 = Semester.customInit(label="SS", year=22)
sem2 = Semester.customInit(label="WS", year=22)
db.session.add(sem1)
db.session.add(sem2)
partKwargs = [
{"program": program1, "number": 1},
{"program": program1, "number": 2},
{"program": program2, "number": 1},
{"program": program2, "number": 2},
{"program": program3, "number": 1},
]
for kwargs in partKwargs:
db.session.add(Part(semester=sem1, **kwargs))
sem2.transferPartsFrom(sem1)
part1 = sem2.parts[0]
part2 = sem2.parts[2]
student1 = Student(student_number=123, first_name="Mo", last_name="Bit", uni_email="m@test.com")
student2 = Student(student_number=1232, first_name="Mo2", last_name="Bit", uni_email="m2@test.com")
student3 = Student(student_number=1233, first_name="Mo3", last_name="Bit3", uni_email="m3@test.com")
db.session.add(student1)
db.session.add(student2)
db.session.add(student3)
ps1 = PartStudent.customInit(student=student1, part=part1)
ps2 = PartStudent.customInit(student=student2, part=part1)
ps3 = PartStudent.customInit(student=student3, part=part2)
db.session.add(ps1)
db.session.add(ps2)
db.session.add(ps3)
g1 = Group.customInit(part_students=[ps1, ps2])
g2 = Group.customInit(part_students=[ps3])
db.session.add(g1)
db.session.add(g2)
ex1 = Experiment(
number=1,
program=program1,
title="exp",
room="123",
building="phy",
responsibility="none",
duration_in_days=2,
)
ex2 = Experiment(
number=1,
program=program2,
title="exp2",
room="123",
building="phy",
responsibility="none",
duration_in_days=2,
)
db.session.add(ex1)
db.session.add(ex2)
sx1 = SemesterExperiment(experiment=ex1, semester=sem2)
sx2 = SemesterExperiment(experiment=ex2, semester=sem2)
db.session.add(sx1)
db.session.add(sx2)
gx1 = GroupExperiment.customInit(semester_experiment=sx1, group=g1)
gx2 = GroupExperiment.customInit(semester_experiment=sx2, group=g2)
db.session.add(gx1)
db.session.add(gx2)
adminRole = user_datastore.create_role(name="admin")
assistantRole = user_datastore.create_role(name="assistant")
admin_user = user_datastore.create_user(
email="admin@advlabdb.de",
password=hash_password("admin"),
roles=[adminRole],
first_name="Peter",
last_name="Blümler",
)
admin = Admin(user=admin_user)
db.session.add(admin)
us1 = user_datastore.create_user(
email="test@protonmail.com",
password=hash_password("h1"),
roles=[assistantRole],
first_name="As1",
last_name="l",
phone_number="012333212",
mobile_phone_number="012334123",
)
us2 = user_datastore.create_user(
email="test2@protonmail.com",
password=hash_password("h2"),
roles=[assistantRole],
first_name="As2",
last_name="l",
)
as1 = Assistant(user=us1)
as2 = Assistant(user=us2)
as1.semester_experiments.append(sx1)
as2.semester_experiments.append(sx2)
db.session.add(as1)
db.session.add(as2)
ap1 = Appointment.customInit(date=date(2021, 3, 21), special=True, group_experiment=gx1, assistant=as1)
ap2 = Appointment.customInit(date=date(2021, 3, 22), special=True, group_experiment=gx2, assistant=as2)
db.session.add(ap1)
db.session.add(ap2)
db.session.commit()