Compare commits

..

13 Commits

Author SHA1 Message Date
Gustavo Henrique Miranda e7a7d0cd87
Merge 447dffd7df into f825236c45 2025-07-24 23:26:20 +00:00
Gustavo Henrique Miranda 447dffd7df
Merge pull request #81 from gmbrax/refactor/clean-the-ruff-errors
Fix all the Ruff errors
2025-07-24 20:26:17 -03:00
Gustavo Henrique Santos Souza de Miranda 2b79b08df0 Fix all the Ruff errors 2025-07-24 20:19:09 -03:00
Gustavo Henrique Miranda 48b3ec5fd1
Merge pull request #80 from gmbrax/feat/backup-database
add the backup feature
2025-07-24 19:59:15 -03:00
Gustavo Henrique Santos Souza de Miranda c3397ab982 Remove the wrong error message and made the program exit if no session is found 2025-07-24 19:36:12 -03:00
Gustavo Henrique Miranda bb9457978c
Update src/pilgrim/service/backup_service.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-07-24 19:31:13 -03:00
Gustavo Henrique Santos Souza de Miranda c09425cd48 Merge remote-tracking branch 'origin/feat/backup-database' into feat/backup-database 2025-07-24 19:23:59 -03:00
Gustavo Henrique Miranda 7c950b93ab
Update src/pilgrim/ui/screens/diary_list_screen.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-07-24 19:23:40 -03:00
Gustavo Henrique Santos Souza de Miranda 17371088da Changed the way it uses the database connection to be more robust 2025-07-24 19:21:47 -03:00
Gustavo Henrique Santos Souza de Miranda 209099cc5e Add a error handling on backup operation 2025-07-24 19:05:55 -03:00
Gustavo Henrique Santos Souza de Miranda 890fc470bb Change the filename variable to conform to the proper pathlike use 2025-07-24 19:00:04 -03:00
Gustavo Henrique Santos Souza de Miranda 8cf5f7e465 Fix moved the backup service initialization inside the check for the session 2025-07-24 18:39:08 -03:00
Gustavo Henrique Santos Souza de Miranda f62ecc18f7 add the backup feature 2025-07-24 18:15:26 -03:00
13 changed files with 65 additions and 29 deletions

View File

@ -1,9 +1,7 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import sessionmaker
from pathlib import Path
import os
import shutil
from pilgrim.utils import ConfigManager

View File

@ -0,0 +1,34 @@
import zipfile
from pilgrim.utils.directory_manager import DirectoryManager
class BackupService:
def __init__(self, session):
self.session = session
def create_backup(self):
db_path = DirectoryManager.get_database_path()
if not db_path.exists():
raise FileNotFoundError("No Database Found")
with self.session.connection() as conn:
raw_conn = conn.connection
dump = "\n".join(line for line in raw_conn.iterdump())
filename = DirectoryManager.get_config_directory() / "backup.zip"
diaries_root_path = DirectoryManager.get_diaries_root()
try:
with zipfile.ZipFile(filename, "w",zipfile.ZIP_DEFLATED) as zipf:
zipf.writestr("database.sql", dump)
if diaries_root_path.exists():
for file_path in diaries_root_path.rglob('*'):
if file_path.is_file():
arcname = file_path.relative_to(diaries_root_path.parent)
zipf.write(file_path, arcname=arcname)
return True, filename
except Exception as e:
return False, str(e)

View File

@ -1,7 +1,6 @@
from pilgrim.service.mocks.entry_service_mock import EntryServiceMock
from pilgrim.service.mocks.photo_service_mock import PhotoServiceMock
from pilgrim.service.mocks.travel_diary_service_mock import TravelDiaryServiceMock
from pilgrim.service.photo_service import PhotoService
from pilgrim.service.servicemanager import ServiceManager

View File

@ -3,7 +3,6 @@ import re
import shutil
from pathlib import Path
from pilgrim.models.entry import Entry
from pilgrim.utils import DirectoryManager
from sqlalchemy.exc import IntegrityError

View File

@ -1,19 +1,19 @@
from typing import Optional, Tuple
import asyncio
from textual.app import ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Label, Static, OptionList, Button
from textual.widgets import Header, Footer, Static, OptionList, Button
from textual.binding import Binding
from textual.containers import Vertical, Container, Horizontal
from textual.containers import Container, Horizontal
from pilgrim.models.travel_diary import TravelDiary
from pilgrim.ui.screens.about_screen import AboutScreen
from pilgrim.ui.screens.diary_settings_screen import SettingsScreen
from pilgrim.ui.screens.edit_diary_modal import EditDiaryModal
from pilgrim.ui.screens.new_diary_modal import NewDiaryModal
from pilgrim.ui.screens.edit_entry_screen import EditEntryScreen
from pilgrim.service.backup_service import BackupService
class DiaryListScreen(Screen):
TITLE = "Pilgrim - Main"
@ -211,11 +211,11 @@ class DiaryListScreen(Screen):
def _on_new_diary_submitted(self, result):
"""Callback after diary creation"""
if result: # Se result não é string vazia, o diário foi criado
self.notify(f"Returning to diary list...")
self.notify("Returning to diary list...")
# Atualiza a lista de diários
self.refresh_diaries()
else:
self.notify(f"Creation canceled...")
self.notify("Creation canceled...")
def _on_screen_resume(self) -> None:
super()._on_screen_resume()
@ -298,3 +298,18 @@ class DiaryListScreen(Screen):
self.notify("Invalid diary ID")
else:
self.notify("Select a diary to open the settings")
def action_backup(self):
session = self.app.service_manager.get_session()
if session:
backup_service = BackupService(session)
result_operation, result_data = backup_service.create_backup()
if result_operation:
self.notify(f"Backup result: {result_data}")
else:
self.notify(f"Error performing backup: {result_data}")
else:
self.notify("Error: Session not found",severity="error")
self.app.exit()

View File

@ -1,13 +1,12 @@
from textual.widgets import Static
from textual.containers import Container
from textual.widgets import Header, Footer, Label, Button,Checkbox,Input
from textual.widgets import Header, Footer, Label, Button,Checkbox
from textual.screen import Screen
from textual.reactive import reactive
from textual.binding import Binding
from textual import on
from pilgrim.models.travel_diary import TravelDiary
from pilgrim.ui.screens.modals.delete_all_entries_from_diary_modal import DeleteAllEntriesModal
from pilgrim.ui.screens.modals.delete_all_photos_from_diary_modal import DeleteAllPhotosModal
from pilgrim.ui.screens.modals.delete_diary_modal import DeleteDiaryModal

View File

@ -5,11 +5,9 @@ from typing import Optional, List
from pilgrim.models.entry import Entry
from pilgrim.models.photo import Photo
from pilgrim.models.travel_diary import TravelDiary
from pilgrim.ui.screens.modals.add_photo_modal import AddPhotoModal
from pilgrim.ui.screens.modals.confirm_delete_modal import ConfirmDeleteModal
from pilgrim.ui.screens.modals.edit_photo_modal import EditPhotoModal
from pilgrim.ui.screens.modals.file_picker_modal import FilePickerModal
from pilgrim.ui.screens.rename_entry_modal import RenameEntryModal
from textual.app import ComposeResult
from textual.binding import Binding
@ -189,7 +187,7 @@ class EditEntryScreen(Screen):
"""Ensures the diary info widget is always updated with current diary name"""
try:
self.diary_info.update(f"Diary: {self.diary_name}")
except Exception as e:
except Exception:
self.diary_info.update(f"Diary: {self.diary_id}")
def refresh_entries(self):
@ -427,7 +425,7 @@ class EditEntryScreen(Screen):
photo_details += f"🔗 {photo_hash}\n"
photo_details += f"📅 {selected_photo.addition_date}\n"
photo_details += f"💬 {selected_photo.caption or 'No caption'}\n"
photo_details += f"[b]Reference formats:[/b]\n"
photo_details += "[b]Reference formats:[/b]\n"
photo_details += f"\\[\\[photo::{photo_hash}\\]\\]"
self.photo_info.update(photo_details)
@ -745,8 +743,8 @@ class EditEntryScreen(Screen):
if selected_photo.caption:
photo_details += f"Caption: {selected_photo.caption}\n"
else:
photo_details += f"Caption: No Caption\n"
photo_details += f"[b]Reference formats:[/b]\n"
photo_details += "Caption: No Caption\n"
photo_details += "[b]Reference formats:[/b]\n"
photo_details += f"\\[\\[photo::{photo_hash}]]"
self.photo_info.update(photo_details)

View File

@ -1,11 +1,9 @@
import os
from pathlib import Path
from textual.app import ComposeResult
from textual.screen import Screen
from textual.widgets import Static, Input, Button
from textual.containers import Horizontal, Container
from .file_picker_modal import FilePickerModal
import hashlib
class AddPhotoModal(Screen):
"""Modal for adding a new photo"""

View File

@ -37,6 +37,11 @@ class UIApp(App):
"Open About Pilgrim",
screen.action_about_cmd
)
yield SystemCommand(
"Backup Database",
"Backup the Database",
screen.action_backup
)
elif isinstance(screen, AboutScreen):
yield SystemCommand(

View File

@ -1,5 +1,4 @@
import os.path
from os import PathLike
from threading import Lock
import tomli

View File

@ -1,14 +1,8 @@
from re import search
import pytest
from datetime import datetime
from unittest.mock import Mock
from sqlalchemy import create_engine
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import sessionmaker
from pilgrim.database import Base
from pilgrim.models.travel_diary import TravelDiary
from pilgrim.models.entry import Entry
from pilgrim.models.photo import Photo

View File

@ -1,4 +1,3 @@
import pytest
from pathlib import Path
from pilgrim import TravelDiary

View File

@ -4,7 +4,6 @@ from pathlib import Path
from unittest.mock import patch
from pilgrim.utils.config_manager import ConfigManager, SingletonMeta
from pilgrim.utils.directory_manager import DirectoryManager
@pytest.fixture
def clean_singleton():