diff --git a/advlabdb/actions.py b/advlabdb/actions.py index 4ea7c1a..65cb593 100644 --- a/advlabdb/actions.py +++ b/advlabdb/actions.py @@ -1,7 +1,10 @@ +from datetime import datetime from flask import flash from flask_login import current_user from sqlalchemy import select +import subprocess +from . import data_dir 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") +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(): user_ids_to_deactivate = db.session.scalars( diff --git a/advlabdb/adminModelViews.py b/advlabdb/adminModelViews.py index e61d018..4dfc9a4 100644 --- a/advlabdb/adminModelViews.py +++ b/advlabdb/adminModelViews.py @@ -28,7 +28,7 @@ from wtforms.validators import URL, DataRequired, Email, NumberRange, Optional from wtforms.widgets import NumberInput 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 ( admin_formatter, appointment_date_formatter, @@ -1341,6 +1341,9 @@ class ImportView(SecureAdminBaseView): class ActionsView(SecureAdminBaseView): class ActionsForm(FlaskForm): + manual_backup = BooleanField( + label="Create a manual database backup", + ) update_final_experiment_and_part_marks = BooleanField( label="Manually update all final experiment and part marks in the active semester", ) @@ -1357,6 +1360,8 @@ class ActionsView(SecureAdminBaseView): form = ActionsView.ActionsForm() if form.validate_on_submit(): + if form.manual_backup.data: + manual_backup() if form.update_final_experiment_and_part_marks.data: update_final_experiment_and_part_marks() if form.deactivate_assistants.data: diff --git a/advlabdb/database_import.py b/advlabdb/database_import.py index fe3649c..17f336a 100644 --- a/advlabdb/database_import.py +++ b/advlabdb/database_import.py @@ -1,12 +1,12 @@ from datetime import datetime from pathlib import Path -from shutil import copy2 from flask import flash from sqlalchemy import select from . import data_dir from .exceptions import DatabaseImportException +from .actions import backup from .models import ( Appointment, Assistant, @@ -25,10 +25,6 @@ from .models import ( ) -def now(): - return datetime.now().strftime("%d_%m_%Y_%H_%M_%S") - - def is_null(entry): return entry == "NULL" or entry == "" @@ -48,11 +44,6 @@ def not_nullable(entry): 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": raise DatabaseImportException( "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) - # Backup - 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}") + backup(f"before_{dbSemester}_import") db.session.commit() except Exception as ex: @@ -342,10 +329,7 @@ def importFromFile(filePath: Path): raise ex - dest = db_bk_dir / f"after_{dbSemester}_import_{now()}.sqlite" - copy2(db_path, dest) - - flash(f"Made a backup of the database after the import at {dest}") + backup(f"after_{dbSemester}_import") flash("\nImport done!", "success") flash("Please check that everything worked as expected. Restore the database backup otherwise!", "warning")