1
0
Fork 0
mirror of https://codeberg.org/Mo8it/AdvLabDB.git synced 2024-11-08 21:21:06 +00:00
AdvLabDB/advlabdb/database_import.py

335 lines
12 KiB
Python
Raw Normal View History

2021-09-11 18:47:48 +00:00
from datetime import datetime
2022-09-07 22:33:00 +00:00
from pathlib import Path
2021-09-11 18:47:48 +00:00
2022-08-09 13:50:59 +00:00
from flask import flash
2022-05-16 17:14:55 +00:00
from sqlalchemy import select
2022-02-13 18:58:05 +00:00
2023-11-01 22:18:47 +00:00
from .actions import backup
2023-11-02 17:47:52 +00:00
from .exceptions import DatabaseImportException
2022-05-08 19:26:25 +00:00
from .models import (
2022-02-13 18:58:05 +00:00
Appointment,
Assistant,
2021-09-11 18:47:48 +00:00
Experiment,
2022-02-13 18:58:05 +00:00
Group,
2021-09-11 18:47:48 +00:00
GroupExperiment,
2022-02-13 18:58:05 +00:00
Part,
PartStudent,
2021-09-11 18:47:48 +00:00
Program,
2022-02-13 18:58:05 +00:00
Semester,
2021-09-11 18:47:48 +00:00
SemesterExperiment,
2022-02-13 18:58:05 +00:00
Student,
2021-09-11 18:47:48 +00:00
User,
2022-08-09 12:46:48 +00:00
db,
get_first,
2021-09-11 18:47:48 +00:00
)
2022-05-07 14:48:28 +00:00
2022-05-17 23:08:07 +00:00
def is_null(entry):
2023-11-02 19:04:09 +00:00
return entry in {"NULL", ""}
2022-05-17 23:08:07 +00:00
def nullable(entry):
if is_null(entry):
return None
2022-09-12 17:08:02 +00:00
return entry.strip()
2022-05-17 23:08:07 +00:00
def not_nullable(entry):
if is_null(entry):
2022-09-12 17:20:01 +00:00
raise DatabaseImportException("Unnullable entry is NULL!")
2022-05-17 23:08:07 +00:00
2022-09-12 17:08:02 +00:00
return entry.strip()
2022-05-17 23:08:07 +00:00
2022-09-07 22:33:00 +00:00
def importFromFile(filePath: Path):
if filePath.name[-4:] != ".txt":
2022-09-12 17:20:01 +00:00
raise DatabaseImportException(
2022-03-19 21:22:23 +00:00
"The import file has to be a text file with txt extension (.txt at the end of the filename)!"
2021-09-11 18:47:48 +00:00
)
semesters = {}
parts = {}
students = {}
groups = {}
partStudents = {}
experiments = {}
groupExperiments = {}
appointments = {}
2023-11-02 18:44:24 +00:00
with filePath.open() as f:
2022-08-08 20:51:52 +00:00
flash("Reading file...")
2021-09-11 18:47:48 +00:00
expectingTable = True
readHeader = False
for line in f:
line = line[:-1]
2022-05-17 23:08:07 +00:00
if line == "":
2021-09-11 18:47:48 +00:00
expectingTable = True
continue
if expectingTable:
2022-05-17 23:08:07 +00:00
if line[0] != "#":
2022-09-12 17:20:01 +00:00
raise DatabaseImportException(f"Expected a Table name starting with # but got this line: {line}")
2021-09-11 18:47:48 +00:00
2022-05-17 23:08:07 +00:00
expectingTable = False
tableName = line[1:]
if tableName == "Semester":
activeDict = semesters
elif tableName == "Part":
activeDict = parts
elif tableName == "Student":
activeDict = students
elif tableName == "Group":
activeDict = groups
elif tableName == "PartStudent":
activeDict = partStudents
elif tableName == "Experiment":
activeDict = experiments
elif tableName == "GroupExperiment":
activeDict = groupExperiments
elif tableName == "Appointment":
activeDict = appointments
else:
2022-09-12 17:20:01 +00:00
raise DatabaseImportException(f"{tableName} is not a valid table name!")
2022-05-17 23:08:07 +00:00
readHeader = True
continue
2021-09-11 18:47:48 +00:00
cells = line.split("\t")
if readHeader:
readHeader = False
activeDict["_header"] = cells
for cell in cells:
activeDict[cell] = []
continue
cellsLen = len(cells)
2022-05-17 23:08:07 +00:00
if cellsLen != len(activeDict["_header"]):
2022-09-12 17:20:01 +00:00
raise DatabaseImportException(
2021-09-11 18:47:48 +00:00
f"The number of header cells is not equal to the number of row cells in row {cells}!"
)
2022-05-17 23:08:07 +00:00
for i in range(cellsLen):
activeDict[activeDict["_header"][i]].append(cells[i])
try:
2021-09-11 18:47:48 +00:00
# Semester
2022-08-08 20:51:52 +00:00
flash("Semester...")
2021-09-11 18:47:48 +00:00
2022-05-17 23:08:07 +00:00
if len(semesters["label"]) != 1:
2022-09-12 17:20:01 +00:00
raise DatabaseImportException("Only one semester is allowed in an import file!")
2021-09-11 18:47:48 +00:00
2022-05-17 23:08:07 +00:00
semesterLabel = not_nullable(semesters["label"][0])
semesterYear = int(not_nullable(semesters["year"][0]))
2022-05-16 20:28:09 +00:00
dbSemester = get_first(select(Semester).where(Semester.label == semesterLabel, Semester.year == semesterYear))
2021-09-11 18:47:48 +00:00
2022-05-16 17:14:55 +00:00
if dbSemester is None:
2022-09-12 17:20:01 +00:00
raise DatabaseImportException(
2021-09-11 18:47:48 +00:00
f"{semesterLabel}{semesterYear} does not exist in the database! Please make sure that you create the semester from the web interface first."
)
# Part
2022-08-08 20:51:52 +00:00
flash("Part...")
2021-09-11 18:47:48 +00:00
dbParts = {}
for i, id in enumerate(parts["id"]):
2022-05-17 23:08:07 +00:00
id = int(not_nullable(id))
partNumber = int(not_nullable(parts["number"][i]))
partProgramLabel = not_nullable(parts["program_label"][i])
2022-05-16 20:28:09 +00:00
dbPart = get_first(
select(Part)
.join(Program)
.where(
Part.number == partNumber,
Program.label == partProgramLabel,
Part.semester == dbSemester,
2022-05-16 17:14:55 +00:00
)
)
2021-09-11 18:47:48 +00:00
2022-05-16 17:14:55 +00:00
if dbPart is None:
2022-09-12 17:20:01 +00:00
raise DatabaseImportException(
2022-05-17 23:25:51 +00:00
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."
2021-09-11 18:47:48 +00:00
)
dbParts[id] = dbPart
# Student
2022-08-08 20:51:52 +00:00
flash("Student...")
2021-09-11 18:47:48 +00:00
dbStudents = {}
2022-09-07 23:37:06 +00:00
for i, student_number in enumerate(students["student_number"]):
student_number = int(not_nullable(student_number))
first_name = not_nullable(students["first_name"][i])
last_name = not_nullable(students["last_name"][i])
2022-09-12 17:08:02 +00:00
uni_email = not_nullable(students["uni_email"][i]).lower()
2022-09-12 17:20:01 +00:00
contact_email = nullable(students["contact_email"][i])
if contact_email is not None:
contact_email = contact_email.lower()
2022-09-07 23:37:06 +00:00
bachelor_thesis = nullable(students["bachelor_thesis"][i])
bachelor_thesis_work_group = nullable(students["bachelor_thesis_work_group"][i])
note = nullable(students["note"][i])
dbStudent = get_first(select(Student).where(Student.student_number == student_number))
2021-09-11 18:47:48 +00:00
2022-05-16 17:14:55 +00:00
if dbStudent is None:
2021-09-11 18:47:48 +00:00
dbStudent = Student(
2022-09-07 23:37:06 +00:00
student_number=student_number,
first_name=first_name,
last_name=last_name,
uni_email=uni_email,
contact_email=contact_email,
bachelor_thesis=bachelor_thesis,
bachelor_thesis_work_group=bachelor_thesis_work_group,
note=note,
2021-09-11 18:47:48 +00:00
)
db.session.add(dbStudent)
else:
2022-09-07 23:37:06 +00:00
# Check if columns that should not change match
if dbStudent.first_name != first_name:
2022-09-10 16:26:46 +00:00
flash(
f'First name "{dbStudent.first_name}" in the database does not match with the first name "{first_name}" provided in the import file for the student number {student_number}.',
"warning",
2022-09-07 23:37:06 +00:00
)
if dbStudent.last_name != last_name:
2022-09-10 16:26:46 +00:00
flash(
f'Last name "{dbStudent.last_name}" in the database does not match with the last name "{last_name}" provided in the import file for the student number {student_number}.',
"warning",
2022-09-07 23:37:06 +00:00
)
if dbStudent.uni_email != uni_email:
2022-09-10 16:26:46 +00:00
flash(
f'University email "{dbStudent.uni_email}" in the database does not match with the university email "{last_name}" provided in the import file for the student number {student_number}.',
"warning",
2022-09-07 23:37:06 +00:00
)
dbStudent.contact_email = contact_email
# Only overwrite if set
if bachelor_thesis is not None:
dbStudent.bachelor_thesis = bachelor_thesis
if bachelor_thesis_work_group is not None:
dbStudent.bachelor_thesis_work_group = bachelor_thesis_work_group
# Append to note instead of overwriting
if note is not None:
if dbStudent.note is None:
dbStudent.note = note
dbStudent.note += "\n" + note
dbStudents[student_number] = dbStudent
2021-09-11 18:47:48 +00:00
# Group
2022-08-08 20:51:52 +00:00
flash("Group...")
2021-09-11 18:47:48 +00:00
dbGroups = {}
for i, id in enumerate(groups["id"]):
2022-05-17 23:08:07 +00:00
id = int(not_nullable(id))
2022-05-16 20:28:09 +00:00
program = get_first(select(Program).where(Program.label == not_nullable(groups["program_label"][i])))
2021-09-11 18:47:48 +00:00
dbGroup = Group(
2022-05-17 23:08:07 +00:00
number=int(not_nullable(groups["number"][i])),
2022-05-16 17:14:55 +00:00
program=program,
2021-09-11 18:47:48 +00:00
semester=dbSemester,
)
db.session.add(dbGroup)
dbGroups[id] = dbGroup
# PartStudent
2022-08-08 20:51:52 +00:00
flash("PartStudent...")
2021-09-11 18:47:48 +00:00
2022-09-07 23:37:06 +00:00
for i, student_number in enumerate(partStudents["student_number"]):
student_number = int(not_nullable(student_number))
2022-05-15 18:05:00 +00:00
dbPartStudent = PartStudent(
2022-09-07 23:37:06 +00:00
student=dbStudents[student_number],
2022-05-17 23:08:07 +00:00
part=dbParts[int(not_nullable(partStudents["part_id"][i]))],
group=dbGroups[int(not_nullable(partStudents["group_id"][i]))],
2021-09-11 18:47:48 +00:00
)
db.session.add(dbPartStudent)
# Experiment
2022-08-08 20:51:52 +00:00
flash("Experiment...")
2021-09-11 18:47:48 +00:00
dbSemesterExperiments = {}
for i, id in enumerate(experiments["id"]):
2022-05-17 23:08:07 +00:00
id = int(not_nullable(id))
experimentNumber = int(not_nullable(experiments["number"][i]))
2022-05-16 17:14:55 +00:00
2022-05-16 20:28:09 +00:00
experimentProgram = get_first(
select(Program).where(Program.label == not_nullable(experiments["program_label"][i]))
2022-05-16 17:14:55 +00:00
)
2022-05-16 20:28:09 +00:00
dbExperiment = get_first(
select(Experiment).where(Experiment.number == experimentNumber, Experiment.program == experimentProgram)
2022-05-16 17:14:55 +00:00
)
if dbExperiment is None:
2022-05-17 23:08:07 +00:00
# TODO: Check if experimentProgram is None
2022-09-12 17:20:01 +00:00
raise DatabaseImportException(
2022-06-01 21:02:17 +00:00
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."
2021-09-11 18:47:48 +00:00
)
2022-05-16 20:28:09 +00:00
dbSemesterExperiment = get_first(
select(SemesterExperiment).where(
SemesterExperiment.experiment == dbExperiment, SemesterExperiment.semester == dbSemester
2022-05-16 17:14:55 +00:00
)
)
2021-09-11 18:47:48 +00:00
2022-05-16 17:14:55 +00:00
if dbSemesterExperiment is None:
2022-09-12 17:20:01 +00:00
raise DatabaseImportException(
2022-06-01 21:02:17 +00:00
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"
2021-09-11 18:47:48 +00:00
)
dbSemesterExperiments[id] = dbSemesterExperiment
# GroupExperiment
2022-08-08 20:51:52 +00:00
flash("GroupExperiment...")
2021-09-11 18:47:48 +00:00
dbGroupExperiments = {}
for i, id in enumerate(groupExperiments["id"]):
2022-05-17 23:08:07 +00:00
id = int(not_nullable(id))
2022-05-15 18:59:57 +00:00
dbGroupExperiment = GroupExperiment(
2022-05-17 23:08:07 +00:00
semester_experiment=dbSemesterExperiments[int(not_nullable(groupExperiments["experiment_id"][i]))],
group=dbGroups[int(not_nullable(groupExperiments["group_id"][i]))],
2021-09-11 18:47:48 +00:00
)
db.session.add(dbGroupExperiment)
dbGroupExperiments[id] = dbGroupExperiment
# Appointment
2022-08-08 20:51:52 +00:00
flash("Appointment...")
2021-09-11 18:47:48 +00:00
for i, date in enumerate(appointments["date"]):
2022-05-17 23:08:07 +00:00
date = not_nullable(date)
2022-09-12 17:08:02 +00:00
assistantEmail = not_nullable(appointments["assistant_email"][i]).lower()
2022-05-16 20:28:09 +00:00
assistant = get_first(select(Assistant).join(User).where(User.email == assistantEmail))
2021-09-11 18:47:48 +00:00
2022-05-15 20:24:49 +00:00
if assistant is None:
2022-09-12 17:20:01 +00:00
raise DatabaseImportException(
2022-05-17 23:08:07 +00:00
f"Assistant with email {assistantEmail} does not exist in the database! Please make sure that you create assistants in the web interface."
2021-09-11 18:47:48 +00:00
)
2022-05-15 20:24:49 +00:00
dbAppointment = Appointment(
2021-09-11 18:47:48 +00:00
date=datetime.strptime(date, "%d.%m.%Y").date(),
2022-05-17 23:08:07 +00:00
special=bool(int(not_nullable(appointments["special"][i]))),
group_experiment=dbGroupExperiments[int(not_nullable(appointments["group_experiment_id"][i]))],
2021-09-11 18:47:48 +00:00
assistant=assistant,
)
db.session.add(dbAppointment)
2023-11-01 22:18:47 +00:00
backup(f"before_{dbSemester}_import")
2022-05-17 23:08:07 +00:00
db.session.commit()
except Exception as ex:
db.session.rollback()
2021-09-11 18:47:48 +00:00
2022-05-17 23:08:07 +00:00
raise ex
2022-02-13 18:58:05 +00:00
2023-11-01 22:18:47 +00:00
backup(f"after_{dbSemester}_import")
2021-09-11 18:47:48 +00:00
2022-09-12 17:20:01 +00:00
flash("\nImport done!", "success")
flash("Please check that everything worked as expected. Restore the database backup otherwise!", "warning")