mirror of https://github.com/gmbrax/Pilgrim.git
				
				
				
			Compare commits
	
		
			No commits in common. "6724a66fed14c894e42352708a41aba9d972dc27" and "b1e83aabbb2f69d8339a4012d5fa79934148d632" have entirely different histories.
		
	
	
		
			6724a66fed
			...
			b1e83aabbb
		
	
		| 
						 | 
					@ -1,18 +1,47 @@
 | 
				
			||||||
from pilgrim.database import Database
 | 
					from pilgrim.database import Database
 | 
				
			||||||
from pilgrim.service.servicemanager import ServiceManager
 | 
					from pilgrim.service.servicemanager import ServiceManager
 | 
				
			||||||
from pilgrim.ui.ui import UIApp
 | 
					from pilgrim.ui.ui import UIApp
 | 
				
			||||||
from pilgrim.utils import DirectoryManager
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Application:
 | 
					class Application:
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
        self.config_dir = DirectoryManager.get_config_directory()
 | 
					        self.config_dir = self._ensure_config_directory()
 | 
				
			||||||
        self.database = Database()
 | 
					        self.database = Database()
 | 
				
			||||||
        session = self.database.session()
 | 
					        session = self.database.session()
 | 
				
			||||||
        session_manager = ServiceManager()
 | 
					        session_manager = ServiceManager()
 | 
				
			||||||
        session_manager.set_session(session)
 | 
					        session_manager.set_session(session)
 | 
				
			||||||
        self.ui = UIApp(session_manager)
 | 
					        self.ui = UIApp(session_manager)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _ensure_config_directory(self) -> Path:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Ensures the ~/.pilgrim directory exists and has the correct permissions.
 | 
				
			||||||
 | 
					        Creates it if it doesn't exist.
 | 
				
			||||||
 | 
					        Returns the Path object for the config directory.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        home = Path.home()
 | 
				
			||||||
 | 
					        config_dir = home / ".pilgrim"
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            # Create directory if it doesn't exist
 | 
				
			||||||
 | 
					            config_dir.mkdir(exist_ok=True)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            # Ensure correct permissions (rwx for user only)
 | 
				
			||||||
 | 
					            os.chmod(config_dir, 0o700)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            # Create an empty .gitignore if it doesn't exist
 | 
				
			||||||
 | 
					            gitignore = config_dir / ".gitignore"
 | 
				
			||||||
 | 
					            if not gitignore.exists():
 | 
				
			||||||
 | 
					                gitignore.write_text("*\n")
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return config_dir
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            print(f"Error setting up Pilgrim configuration directory: {str(e)}", file=sys.stderr)
 | 
				
			||||||
 | 
					            sys.exit(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def run(self):
 | 
					    def run(self):
 | 
				
			||||||
        self.database.create()
 | 
					        self.database.create()
 | 
				
			||||||
        self.ui.run()
 | 
					        self.ui.run()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,9 @@
 | 
				
			||||||
from typing import Any
 | 
					from typing import Any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from pilgrim.models.photo_in_entry import photo_entry_association
 | 
					 | 
				
			||||||
from sqlalchemy import Column, Integer, String, ForeignKey, DateTime
 | 
					from sqlalchemy import Column, Integer, String, ForeignKey, DateTime
 | 
				
			||||||
from sqlalchemy.orm import relationship
 | 
					from sqlalchemy.orm import relationship
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from pilgrim.models.photo_in_entry import photo_entry_association
 | 
				
			||||||
from ..database import Base
 | 
					from ..database import Base
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,9 +17,7 @@ class Entry(Base):
 | 
				
			||||||
        "Photo",
 | 
					        "Photo",
 | 
				
			||||||
        secondary=photo_entry_association,
 | 
					        secondary=photo_entry_association,
 | 
				
			||||||
        back_populates="entries")
 | 
					        back_populates="entries")
 | 
				
			||||||
    fk_travel_diary_id = Column(Integer, ForeignKey("travel_diaries.id"), nullable=False)
 | 
					    fk_travel_diary_id = Column(Integer, ForeignKey("travel_diaries.id"),nullable=False)
 | 
				
			||||||
    travel_diary = relationship("TravelDiary", back_populates="entries")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, title: str, text: str, date: str, travel_diary_id: int, **kw: Any):
 | 
					    def __init__(self, title: str, text: str, date: str, travel_diary_id: int, **kw: Any):
 | 
				
			||||||
        super().__init__(**kw)
 | 
					        super().__init__(**kw)
 | 
				
			||||||
        self.title = title
 | 
					        self.title = title
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,26 +1,14 @@
 | 
				
			||||||
from typing import Any
 | 
					from typing import Any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from sqlalchemy import Column, Integer, String, UniqueConstraint
 | 
					from sqlalchemy import Column, String, Integer
 | 
				
			||||||
from sqlalchemy.orm import relationship
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .. import database
 | 
					from ..database import Base
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TravelDiary(Base):
 | 
				
			||||||
class TravelDiary(database.Base):
 | 
					 | 
				
			||||||
    __tablename__ = "travel_diaries"
 | 
					    __tablename__ = "travel_diaries"
 | 
				
			||||||
    id = Column(Integer, primary_key=True)
 | 
					    id = Column(Integer, primary_key=True)
 | 
				
			||||||
    name = Column(String, nullable=False)
 | 
					    name = Column(String)
 | 
				
			||||||
    directory_name = Column(String, nullable=False, unique=True)
 | 
					 | 
				
			||||||
    entries = relationship("Entry", back_populates="travel_diary", cascade="all, delete-orphan")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    __table_args__ = (
 | 
					    def __init__(self, name: str, **kw: Any):
 | 
				
			||||||
        UniqueConstraint('directory_name', name='uq_travel_diary_directory_name'),
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, name: str, directory_name: str = None, **kw: Any):
 | 
					 | 
				
			||||||
        super().__init__(**kw)
 | 
					        super().__init__(**kw)
 | 
				
			||||||
        self.name = name
 | 
					        self.name = name
 | 
				
			||||||
        self.directory_name = directory_name  # Será definido pelo service
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __repr__(self):
 | 
					 | 
				
			||||||
        return f"<TravelDiary(id={self.id}, name='{self.name}', directory_name='{self.directory_name}')>"
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,77 +1,27 @@
 | 
				
			||||||
import hashlib
 | 
					 | 
				
			||||||
import os
 | 
					 | 
				
			||||||
import shutil
 | 
					 | 
				
			||||||
from datetime import datetime
 | 
					 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
from typing import List
 | 
					from typing import List
 | 
				
			||||||
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					import hashlib
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from pilgrim.models.photo import Photo
 | 
					from pilgrim.models.photo import Photo
 | 
				
			||||||
from pilgrim.models.travel_diary import TravelDiary
 | 
					from pilgrim.models.travel_diary import TravelDiary
 | 
				
			||||||
from pilgrim.utils import DirectoryManager
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PhotoService:
 | 
					class PhotoService:
 | 
				
			||||||
    def __init__(self, session):
 | 
					    def __init__(self, session):
 | 
				
			||||||
        self.session = session
 | 
					        self.session = session
 | 
				
			||||||
 | 
					    def _hash_file(self,filepath):
 | 
				
			||||||
    def _hash_file(self, filepath: Path) -> str:
 | 
					 | 
				
			||||||
        """Calculate hash of a file using SHA3-384."""
 | 
					 | 
				
			||||||
        hash_func = hashlib.new('sha3_384')
 | 
					        hash_func = hashlib.new('sha3_384')
 | 
				
			||||||
        with open(filepath, 'rb') as f:
 | 
					        with open(filepath, 'rb') as f:
 | 
				
			||||||
            while chunk := f.read(8192):
 | 
					            while chunk := f.read(8192):
 | 
				
			||||||
                hash_func.update(chunk)
 | 
					                hash_func.update(chunk)
 | 
				
			||||||
        return hash_func.hexdigest()
 | 
					        return hash_func.hexdigest()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _ensure_images_directory(self, travel_diary: TravelDiary) -> Path:
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Ensures the images directory exists for the given diary.
 | 
					 | 
				
			||||||
        Returns the path to the images directory.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        images_dir = DirectoryManager.get_diary_images_directory(travel_diary.directory_name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if not images_dir.exists():
 | 
					 | 
				
			||||||
            images_dir.mkdir(parents=True)
 | 
					 | 
				
			||||||
            os.chmod(images_dir, 0o700)  # Ensure correct permissions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return images_dir
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _copy_photo_to_diary(self, source_path: Path, travel_diary: TravelDiary) -> Path:
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Copies a photo to the diary's images directory.
 | 
					 | 
				
			||||||
        Returns the path to the copied file.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        images_dir = self._ensure_images_directory(travel_diary)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Get original filename and extension
 | 
					 | 
				
			||||||
        original_name = Path(source_path).name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Create destination path
 | 
					 | 
				
			||||||
        dest_path = images_dir / original_name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # If file with same name exists, add a number
 | 
					 | 
				
			||||||
        counter = 1
 | 
					 | 
				
			||||||
        while dest_path.exists():
 | 
					 | 
				
			||||||
            name_parts = original_name.rsplit('.', 1)
 | 
					 | 
				
			||||||
            if len(name_parts) > 1:
 | 
					 | 
				
			||||||
                dest_path = images_dir / f"{name_parts[0]}_{counter}.{name_parts[1]}"
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                dest_path = images_dir / f"{original_name}_{counter}"
 | 
					 | 
				
			||||||
            counter += 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Copy the file
 | 
					 | 
				
			||||||
        shutil.copy2(source_path, dest_path)
 | 
					 | 
				
			||||||
        os.chmod(dest_path, 0o600)  # Read/write for owner only
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return dest_path
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def create(self, filepath: Path, name: str, travel_diary_id: int, caption=None, addition_date=None) -> Photo | None:
 | 
					    def create(self, filepath: Path, name: str, travel_diary_id: int, caption=None, addition_date=None) -> Photo | None:
 | 
				
			||||||
        travel_diary = self.session.query(TravelDiary).filter(TravelDiary.id == travel_diary_id).first()
 | 
					        travel_diary = self.session.query(TravelDiary).filter(TravelDiary.id == travel_diary_id).first()
 | 
				
			||||||
        if not travel_diary:
 | 
					        if not travel_diary:
 | 
				
			||||||
            return None
 | 
					            return None
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        # Copy photo to diary's images directory
 | 
					 | 
				
			||||||
        copied_path = self._copy_photo_to_diary(filepath, travel_diary)
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        # Convert addition_date string to datetime if needed
 | 
					        # Convert addition_date string to datetime if needed
 | 
				
			||||||
        if isinstance(addition_date, str):
 | 
					        if isinstance(addition_date, str):
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
| 
						 | 
					@ -79,23 +29,19 @@ class PhotoService:
 | 
				
			||||||
            except ValueError:
 | 
					            except ValueError:
 | 
				
			||||||
                addition_date = None
 | 
					                addition_date = None
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        # Calculate hash from the copied file
 | 
					 | 
				
			||||||
        photo_hash = self._hash_file(copied_path)
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        new_photo = Photo(
 | 
					        new_photo = Photo(
 | 
				
			||||||
            filepath=str(copied_path),  # Store the path to the copied file
 | 
					            filepath=filepath, 
 | 
				
			||||||
            name=name, 
 | 
					            name=name, 
 | 
				
			||||||
            caption=caption, 
 | 
					            caption=caption, 
 | 
				
			||||||
            fk_travel_diary_id=travel_diary_id,
 | 
					            fk_travel_diary_id=travel_diary_id,
 | 
				
			||||||
            addition_date=addition_date,
 | 
					            addition_date=addition_date,
 | 
				
			||||||
            photo_hash=photo_hash
 | 
					            photo_hash=self._hash_file(filepath)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.session.add(new_photo)
 | 
					        self.session.add(new_photo)
 | 
				
			||||||
        self.session.commit()
 | 
					        self.session.commit()
 | 
				
			||||||
        self.session.refresh(new_photo)
 | 
					        self.session.refresh(new_photo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return new_photo
 | 
					        return new_photo
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def read_by_id(self, photo_id:int) -> Photo:
 | 
					    def read_by_id(self, photo_id:int) -> Photo:
 | 
				
			||||||
        return self.session.query(Photo).get(photo_id)
 | 
					        return self.session.query(Photo).get(photo_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -105,30 +51,15 @@ class PhotoService:
 | 
				
			||||||
    def update(self, photo_src: Photo, photo_dst: Photo) -> Photo | None:
 | 
					    def update(self, photo_src: Photo, photo_dst: Photo) -> Photo | None:
 | 
				
			||||||
        original: Photo = self.read_by_id(photo_src.id)
 | 
					        original: Photo = self.read_by_id(photo_src.id)
 | 
				
			||||||
        if original:
 | 
					        if original:
 | 
				
			||||||
            # If filepath changed, need to copy new file
 | 
					            original.filepath = photo_dst.filepath
 | 
				
			||||||
            if str(photo_dst.filepath) != str(original.filepath):
 | 
					 | 
				
			||||||
                travel_diary = self.session.query(TravelDiary).filter(
 | 
					 | 
				
			||||||
                    TravelDiary.id == original.fk_travel_diary_id).first()
 | 
					 | 
				
			||||||
                if travel_diary:
 | 
					 | 
				
			||||||
                    # Copy new photo
 | 
					 | 
				
			||||||
                    new_path = self._copy_photo_to_diary(Path(photo_dst.filepath), travel_diary)
 | 
					 | 
				
			||||||
                    # Delete old photo if it exists in our images directory
 | 
					 | 
				
			||||||
                    old_path = Path(original.filepath)
 | 
					 | 
				
			||||||
                    if old_path.exists() and str(DirectoryManager.get_diaries_root()) in str(old_path):
 | 
					 | 
				
			||||||
                        old_path.unlink()
 | 
					 | 
				
			||||||
                    original.filepath = str(new_path)
 | 
					 | 
				
			||||||
                    # Update hash based on the new copied file
 | 
					 | 
				
			||||||
                    original.photo_hash = self._hash_file(new_path)
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            original.name = photo_dst.name
 | 
					            original.name = photo_dst.name
 | 
				
			||||||
            original.addition_date = photo_dst.addition_date
 | 
					            original.addition_date = photo_dst.addition_date
 | 
				
			||||||
            original.caption = photo_dst.caption
 | 
					            original.caption = photo_dst.caption
 | 
				
			||||||
 | 
					            original.photo_hash = original.photo_hash
 | 
				
			||||||
            if photo_dst.entries and len(photo_dst.entries) > 0:
 | 
					            if photo_dst.entries and len(photo_dst.entries) > 0:
 | 
				
			||||||
                if original.entries is None:
 | 
					                if original.entries is None:
 | 
				
			||||||
                    original.entries = []
 | 
					                    original.entries = []
 | 
				
			||||||
                original.entries = photo_dst.entries  # Replace instead of extend
 | 
					                original.entries = photo_dst.entries  # Replace instead of extend
 | 
				
			||||||
 | 
					 | 
				
			||||||
            self.session.commit()
 | 
					            self.session.commit()
 | 
				
			||||||
            self.session.refresh(original)
 | 
					            self.session.refresh(original)
 | 
				
			||||||
            return original
 | 
					            return original
 | 
				
			||||||
| 
						 | 
					@ -147,12 +78,8 @@ class PhotoService:
 | 
				
			||||||
                id=excluded.id,
 | 
					                id=excluded.id,
 | 
				
			||||||
                photo_hash=excluded.photo_hash,
 | 
					                photo_hash=excluded.photo_hash,
 | 
				
			||||||
                entries=excluded.entries,
 | 
					                entries=excluded.entries,
 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Delete the physical file if it exists in our images directory
 | 
					            )
 | 
				
			||||||
            file_path = Path(excluded.filepath)
 | 
					 | 
				
			||||||
            if file_path.exists() and str(DirectoryManager.get_diaries_root()) in str(file_path):
 | 
					 | 
				
			||||||
                file_path.unlink()
 | 
					 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            self.session.delete(excluded)
 | 
					            self.session.delete(excluded)
 | 
				
			||||||
            self.session.commit()
 | 
					            self.session.commit()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,132 +1,30 @@
 | 
				
			||||||
import os
 | 
					 | 
				
			||||||
import re
 | 
					 | 
				
			||||||
import shutil
 | 
					 | 
				
			||||||
from pathlib import Path
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from pilgrim.utils import DirectoryManager
 | 
					 | 
				
			||||||
from sqlalchemy.exc import IntegrityError
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from ..models.travel_diary import TravelDiary
 | 
					from ..models.travel_diary import TravelDiary
 | 
				
			||||||
 | 
					import asyncio
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TravelDiaryService:
 | 
					class TravelDiaryService:
 | 
				
			||||||
    def __init__(self, session):
 | 
					    def __init__(self,session):
 | 
				
			||||||
        self.session = session
 | 
					        self.session = session
 | 
				
			||||||
 | 
					    async def async_create(self, name:str):
 | 
				
			||||||
    def _sanitize_directory_name(self, name: str) -> str:
 | 
					        new_travel_diary = TravelDiary(name)
 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Sanitizes a diary name for use as a directory name.
 | 
					 | 
				
			||||||
        - Removes special characters
 | 
					 | 
				
			||||||
        - Replaces spaces with underscores
 | 
					 | 
				
			||||||
        - Ensures name is unique by adding a suffix if needed
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        # Remove special characters and replace spaces
 | 
					 | 
				
			||||||
        safe_name = re.sub(r'[^\w\s-]', '', name)
 | 
					 | 
				
			||||||
        safe_name = safe_name.strip().replace(' ', '_').lower()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Ensure we have a valid name
 | 
					 | 
				
			||||||
        if not safe_name:
 | 
					 | 
				
			||||||
            safe_name = "unnamed_diary"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Check if name is already used in database
 | 
					 | 
				
			||||||
        base_name = safe_name
 | 
					 | 
				
			||||||
        counter = 1
 | 
					 | 
				
			||||||
        while self.session.query(TravelDiary).filter_by(directory_name=safe_name).first() is not None:
 | 
					 | 
				
			||||||
            safe_name = f"{base_name}_{counter}"
 | 
					 | 
				
			||||||
            counter += 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return safe_name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _get_diary_directory(self, diary: TravelDiary) -> Path:
 | 
					 | 
				
			||||||
        """Returns the directory path for a diary."""
 | 
					 | 
				
			||||||
        return DirectoryManager.get_diary_directory(diary.directory_name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _get_diary_data_directory(self, diary: TravelDiary) -> Path:
 | 
					 | 
				
			||||||
        """Returns the data directory path for a diary."""
 | 
					 | 
				
			||||||
        return DirectoryManager.get_diary_data_directory(diary.directory_name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _ensure_diary_directory(self, diary: TravelDiary) -> Path:
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Creates and returns the directory structure for a diary:
 | 
					 | 
				
			||||||
        ~/.pilgrim/diaries/{directory_name}/data/
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        # Create diary directory
 | 
					 | 
				
			||||||
        diary_dir = self._get_diary_directory(diary)
 | 
					 | 
				
			||||||
        diary_dir.mkdir(exist_ok=True)
 | 
					 | 
				
			||||||
        os.chmod(diary_dir, 0o700)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Create data subdirectory
 | 
					 | 
				
			||||||
        data_dir = self._get_diary_data_directory(diary)
 | 
					 | 
				
			||||||
        data_dir.mkdir(exist_ok=True)
 | 
					 | 
				
			||||||
        os.chmod(data_dir, 0o700)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return data_dir
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _cleanup_diary_directory(self, diary: TravelDiary):
 | 
					 | 
				
			||||||
        """Removes the diary directory and all its contents."""
 | 
					 | 
				
			||||||
        diary_dir = self._get_diary_directory(diary)
 | 
					 | 
				
			||||||
        if diary_dir.exists():
 | 
					 | 
				
			||||||
            shutil.rmtree(diary_dir)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async def async_create(self, name: str):
 | 
					 | 
				
			||||||
        # Generate safe directory name
 | 
					 | 
				
			||||||
        directory_name = self._sanitize_directory_name(name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Create diary with directory name
 | 
					 | 
				
			||||||
        new_travel_diary = TravelDiary(name=name, directory_name=directory_name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
        self.session.add(new_travel_diary)
 | 
					        self.session.add(new_travel_diary)
 | 
				
			||||||
        self.session.commit()
 | 
					        self.session.commit()
 | 
				
			||||||
        self.session.refresh(new_travel_diary)
 | 
					        self.session.refresh(new_travel_diary)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Create directory structure for the new diary
 | 
					 | 
				
			||||||
            self._ensure_diary_directory(new_travel_diary)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return new_travel_diary
 | 
					        return new_travel_diary
 | 
				
			||||||
        except IntegrityError:
 | 
					 | 
				
			||||||
            self.session.rollback()
 | 
					 | 
				
			||||||
            raise ValueError(f"Could not create diary: directory name '{directory_name}' already exists")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def read_by_id(self, travel_id: int):
 | 
					    def read_by_id(self, travel_id:int):
 | 
				
			||||||
        diary = self.session.query(TravelDiary).get(travel_id)
 | 
					        return self.session.query(TravelDiary).get(travel_id)
 | 
				
			||||||
        if diary:
 | 
					 | 
				
			||||||
            # Ensure directory exists when reading
 | 
					 | 
				
			||||||
            self._ensure_diary_directory(diary)
 | 
					 | 
				
			||||||
        return diary
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def read_all(self):
 | 
					    def read_all(self):
 | 
				
			||||||
        diaries = self.session.query(TravelDiary).all()
 | 
					        return self.session.query(TravelDiary).all()
 | 
				
			||||||
        # Ensure directories exist for all diaries
 | 
					 | 
				
			||||||
        for diary in diaries:
 | 
					 | 
				
			||||||
            self._ensure_diary_directory(diary)
 | 
					 | 
				
			||||||
        return diaries
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def update(self, travel_diary_id: int, name: str):
 | 
					    def update(self, travel_diary_id: int, name: str):
 | 
				
			||||||
        original = self.read_by_id(travel_diary_id)
 | 
					        original = self.read_by_id(travel_diary_id)
 | 
				
			||||||
        if original is not None:
 | 
					        if original is not None:
 | 
				
			||||||
            try:
 | 
					 | 
				
			||||||
                # Generate new directory name
 | 
					 | 
				
			||||||
                new_directory_name = self._sanitize_directory_name(name)
 | 
					 | 
				
			||||||
                old_directory = self._get_diary_directory(original)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                # Update diary
 | 
					 | 
				
			||||||
            original.name = name
 | 
					            original.name = name
 | 
				
			||||||
                original.directory_name = new_directory_name
 | 
					 | 
				
			||||||
            self.session.commit()
 | 
					            self.session.commit()
 | 
				
			||||||
            self.session.refresh(original)
 | 
					            self.session.refresh(original)
 | 
				
			||||||
 | 
					 | 
				
			||||||
                # Rename directory if it exists
 | 
					 | 
				
			||||||
                new_directory = self._get_diary_directory(original)
 | 
					 | 
				
			||||||
                if old_directory.exists() and old_directory != new_directory:
 | 
					 | 
				
			||||||
                    old_directory.rename(new_directory)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return original
 | 
					        return original
 | 
				
			||||||
            except IntegrityError:
 | 
					 | 
				
			||||||
                self.session.rollback()
 | 
					 | 
				
			||||||
                raise ValueError(f"Could not update diary: directory name '{new_directory_name}' already exists")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return None
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def async_update(self, travel_diary_id: int, name: str):
 | 
					    async def async_update(self, travel_diary_id: int, name: str):
 | 
				
			||||||
        return self.update(travel_diary_id, name)
 | 
					        return self.update(travel_diary_id, name)
 | 
				
			||||||
| 
						 | 
					@ -134,14 +32,7 @@ class TravelDiaryService:
 | 
				
			||||||
    def delete(self, travel_diary_id: TravelDiary):
 | 
					    def delete(self, travel_diary_id: TravelDiary):
 | 
				
			||||||
        excluded = self.read_by_id(travel_diary_id.id)
 | 
					        excluded = self.read_by_id(travel_diary_id.id)
 | 
				
			||||||
        if excluded is not None:
 | 
					        if excluded is not None:
 | 
				
			||||||
            try:
 | 
					 | 
				
			||||||
                # First delete the directory
 | 
					 | 
				
			||||||
                self._cleanup_diary_directory(excluded)
 | 
					 | 
				
			||||||
                # Then delete from database
 | 
					 | 
				
			||||||
            self.session.delete(travel_diary_id)
 | 
					            self.session.delete(travel_diary_id)
 | 
				
			||||||
            self.session.commit()
 | 
					            self.session.commit()
 | 
				
			||||||
            return excluded
 | 
					            return excluded
 | 
				
			||||||
            except Exception as e:
 | 
					 | 
				
			||||||
                self.session.rollback()
 | 
					 | 
				
			||||||
                raise ValueError(f"Could not delete diary: {str(e)}")
 | 
					 | 
				
			||||||
        return None
 | 
					        return None
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,4 @@
 | 
				
			||||||
from textual.app import ComposeResult
 | 
					from textual.app import ComposeResult
 | 
				
			||||||
from textual.binding import Binding
 | 
					 | 
				
			||||||
from textual.containers import Vertical, Horizontal
 | 
					from textual.containers import Vertical, Horizontal
 | 
				
			||||||
from textual.screen import ModalScreen
 | 
					from textual.screen import ModalScreen
 | 
				
			||||||
from textual.widgets import Label, Input, Button
 | 
					from textual.widgets import Label, Input, Button
 | 
				
			||||||
| 
						 | 
					@ -7,7 +6,7 @@ from textual.widgets import Label, Input, Button
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class EditDiaryModal(ModalScreen[tuple[int,str]]):
 | 
					class EditDiaryModal(ModalScreen[tuple[int,str]]):
 | 
				
			||||||
    BINDINGS = [
 | 
					    BINDINGS = [
 | 
				
			||||||
        Binding("escape", "cancel", "Cancel"),
 | 
					        ("escape", "cancel", "Cancel"),
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, diary_id: int):
 | 
					    def __init__(self, diary_id: int):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,36 +1,40 @@
 | 
				
			||||||
import re
 | 
					from typing import Optional, List
 | 
				
			||||||
 | 
					import asyncio
 | 
				
			||||||
from datetime import datetime
 | 
					from datetime import datetime
 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
from typing import Optional, List
 | 
					import hashlib
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from textual.app import ComposeResult
 | 
				
			||||||
 | 
					from textual.screen import Screen
 | 
				
			||||||
 | 
					from textual.widgets import Header, Footer, Static, TextArea, OptionList, Input, Button
 | 
				
			||||||
 | 
					from textual.binding import Binding
 | 
				
			||||||
 | 
					from textual.containers import Container, Horizontal, Vertical, ScrollableContainer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from pilgrim.models.entry import Entry
 | 
					from pilgrim.models.entry import Entry
 | 
				
			||||||
from pilgrim.models.photo import Photo
 | 
					 | 
				
			||||||
from pilgrim.models.travel_diary import TravelDiary
 | 
					from pilgrim.models.travel_diary import TravelDiary
 | 
				
			||||||
 | 
					from pilgrim.models.photo import Photo
 | 
				
			||||||
from pilgrim.ui.screens.modals.add_photo_modal import AddPhotoModal
 | 
					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.edit_photo_modal import EditPhotoModal
 | 
				
			||||||
 | 
					from pilgrim.ui.screens.modals.confirm_delete_modal import ConfirmDeleteModal
 | 
				
			||||||
from pilgrim.ui.screens.modals.file_picker_modal import FilePickerModal
 | 
					from pilgrim.ui.screens.modals.file_picker_modal import FilePickerModal
 | 
				
			||||||
from pilgrim.ui.screens.rename_entry_modal import RenameEntryModal
 | 
					from pilgrim.ui.screens.rename_entry_modal import RenameEntryModal
 | 
				
			||||||
from textual.app import ComposeResult
 | 
					 | 
				
			||||||
from textual.binding import Binding
 | 
					 | 
				
			||||||
from textual.containers import Container, Horizontal, Vertical
 | 
					 | 
				
			||||||
from textual.screen import Screen
 | 
					 | 
				
			||||||
from textual.widgets import Header, Footer, Static, TextArea, OptionList
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class EditEntryScreen(Screen):
 | 
					class EditEntryScreen(Screen):
 | 
				
			||||||
    TITLE = "Pilgrim - Edit"
 | 
					    TITLE = "Pilgrim - Edit"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    BINDINGS = [
 | 
					    BINDINGS = [
 | 
				
			||||||
        Binding("ctrl+q", "quit", "Quit"),
 | 
					        ("ctrl+q", "quit", "Quit"),
 | 
				
			||||||
        Binding("ctrl+s", "save", "Save"),
 | 
					        ("ctrl+s", "save", "Save"),
 | 
				
			||||||
        Binding("ctrl+n", "new_entry", "New Entry"),
 | 
					        ("ctrl+n", "new_entry", "New Entry"),
 | 
				
			||||||
        Binding("ctrl+shift+n", "next_entry", "Next Entry"),
 | 
					        ("ctrl+shift+n", "next_entry", "Next Entry"),
 | 
				
			||||||
        Binding("ctrl+shift+p", "prev_entry", "Previous Entry"),
 | 
					        ("ctrl+shift+p", "prev_entry", "Previous Entry"),
 | 
				
			||||||
        Binding("ctrl+r", "rename_entry", "Rename Entry"),
 | 
					        ("ctrl+r", "rename_entry", "Rename Entry"),
 | 
				
			||||||
        Binding("f8", "toggle_sidebar", "Toggle Photos"),
 | 
					        ("f8", "toggle_sidebar", "Toggle Photos"),
 | 
				
			||||||
        Binding("f9", "toggle_focus", "Toggle Focus"),
 | 
					        ("f9", "toggle_focus", "Toggle Focus"),
 | 
				
			||||||
        Binding("escape", "back_to_list", "Back to List"),
 | 
					        ("escape", "back_to_list", "Back to List"),
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, diary_id: int = 1):
 | 
					    def __init__(self, diary_id: int = 1):
 | 
				
			||||||
| 
						 | 
					@ -329,11 +333,9 @@ class EditEntryScreen(Screen):
 | 
				
			||||||
            all_photos = photo_service.read_all()
 | 
					            all_photos = photo_service.read_all()
 | 
				
			||||||
            self.cached_photos = [photo for photo in all_photos if photo.fk_travel_diary_id == diary_id]
 | 
					            self.cached_photos = [photo for photo in all_photos if photo.fk_travel_diary_id == diary_id]
 | 
				
			||||||
            self.cached_photos.sort(key=lambda x: x.id)
 | 
					            self.cached_photos.sort(key=lambda x: x.id)
 | 
				
			||||||
            return self.cached_photos
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        except Exception as e:
 | 
					        except Exception as e:
 | 
				
			||||||
            self.notify(f"Error loading photos: {str(e)}")
 | 
					            self.notify(f"Error loading photos: {str(e)}")
 | 
				
			||||||
            return []
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def action_toggle_sidebar(self):
 | 
					    def action_toggle_sidebar(self):
 | 
				
			||||||
| 
						 | 
					@ -911,11 +913,13 @@ class EditEntryScreen(Screen):
 | 
				
			||||||
            self.call_later(self._async_update_entry, content, photos_to_link)
 | 
					            self.call_later(self._async_update_entry, content, photos_to_link)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def _async_create_entry(self, content: str, photos_to_link: List[Photo]):
 | 
					    async def _async_create_entry(self, content: str, photos_to_link: List[Photo]):
 | 
				
			||||||
        """Creates a new entry and links the referenced photos."""
 | 
					        """Cria uma nova entrada e associa as fotos referenciadas."""
 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
        service_manager = self.app.service_manager
 | 
					        service_manager = self.app.service_manager
 | 
				
			||||||
 | 
					        db_session = service_manager.get_db_session()
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
            entry_service = service_manager.get_entry_service()
 | 
					            entry_service = service_manager.get_entry_service()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # O service.create deve criar o objeto em memória, mas NÃO fazer o commit ainda.
 | 
				
			||||||
            new_entry = entry_service.create(
 | 
					            new_entry = entry_service.create(
 | 
				
			||||||
                travel_diary_id=self.diary_id,
 | 
					                travel_diary_id=self.diary_id,
 | 
				
			||||||
                title=self.new_entry_title,
 | 
					                title=self.new_entry_title,
 | 
				
			||||||
| 
						 | 
					@ -925,6 +929,7 @@ class EditEntryScreen(Screen):
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if new_entry:
 | 
					            if new_entry:
 | 
				
			||||||
 | 
					               # A partir daqui, é só atualizar a UI como você já fazia
 | 
				
			||||||
                self.entries.append(new_entry)
 | 
					                self.entries.append(new_entry)
 | 
				
			||||||
                self.entries.sort(key=lambda x: x.id)
 | 
					                self.entries.sort(key=lambda x: x.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -935,51 +940,47 @@ class EditEntryScreen(Screen):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                self.is_new_entry = False
 | 
					                self.is_new_entry = False
 | 
				
			||||||
                self.has_unsaved_changes = False
 | 
					                self.has_unsaved_changes = False
 | 
				
			||||||
                self._original_content = new_entry.text
 | 
					                self._original_content = new_entry.text  # Pode ser o texto com hashes curtos
 | 
				
			||||||
                self.new_entry_title = "New Entry"
 | 
					                self.new_entry_title = "New Entry"
 | 
				
			||||||
                self.next_entry_id = max(entry.id for entry in self.entries) + 1
 | 
					                self.next_entry_id = max(entry.id for entry in self.entries) + 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                self._update_entry_display()
 | 
					                self._update_entry_display()
 | 
				
			||||||
                self.notify(f"Entry '{new_entry.title}' saved successfully!")
 | 
					                self.notify(f"✅ New Entry: '{new_entry.title}' Successfully saved")
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                self.notify("Error creating entry")
 | 
					                self.notify("❌ Error creating the Entry")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        except Exception as e:
 | 
					        except Exception as e:
 | 
				
			||||||
            self.notify(f"Error creating entry: {str(e)}")
 | 
					            self.notify(f"❌ Error creating the entry: {str(e)}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def _async_update_entry(self, updated_content: str, photos_to_link: List[Photo]):
 | 
					    async def _async_update_entry(self, updated_content: str, photos_to_link: List[Photo]):
 | 
				
			||||||
        """Updates an existing entry and its photo links."""
 | 
					        """Atualiza uma entrada existente e sua associação de fotos."""
 | 
				
			||||||
 | 
					        service_manager = self.app.service_manager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            if not self.entries:
 | 
					            if not self.entries:
 | 
				
			||||||
                self.notify("No entry to update")
 | 
					                self.notify("No Entry to update")
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
 | 
					 | 
				
			||||||
            service_manager = self.app.service_manager
 | 
					 | 
				
			||||||
            entry_service = service_manager.get_entry_service()
 | 
					            entry_service = service_manager.get_entry_service()
 | 
				
			||||||
            current_entry = self.entries[self.current_entry_index]
 | 
					            current_entry = self.entries[self.current_entry_index]
 | 
				
			||||||
 | 
					            entry_result : Entry = Entry(
 | 
				
			||||||
            entry_result = Entry(
 | 
					 | 
				
			||||||
                id=current_entry.id,
 | 
					 | 
				
			||||||
                title=current_entry.title,
 | 
					                title=current_entry.title,
 | 
				
			||||||
                text=updated_content,
 | 
					                text=updated_content,
 | 
				
			||||||
                photos=photos_to_link,
 | 
					                photos=photos_to_link,
 | 
				
			||||||
                date=current_entry.date,
 | 
					                date=current_entry.date,
 | 
				
			||||||
                travel_diary_id=self.diary_id,
 | 
					                travel_diary_id=self.diary_id
 | 
				
			||||||
                fk_travel_diary_id=self.diary_id
 | 
					
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					            entry_service.update(current_entry, entry_result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            result = entry_service.update(current_entry, entry_result)
 | 
					            # A partir daqui, é só atualizar a UI
 | 
				
			||||||
 | 
					 | 
				
			||||||
            if result:
 | 
					 | 
				
			||||||
            self.has_unsaved_changes = False
 | 
					            self.has_unsaved_changes = False
 | 
				
			||||||
                self._original_content = updated_content
 | 
					            self._original_content = updated_content  # Pode ser o texto com hashes curtos
 | 
				
			||||||
            self._update_sub_header()
 | 
					            self._update_sub_header()
 | 
				
			||||||
                self.notify(f"Entry '{current_entry.title}' saved successfully!")
 | 
					            self.notify(f"✅ Entry: '{current_entry.title}' sucesfully saved")
 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                self.notify("Error updating entry")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        except Exception as e:
 | 
					        except Exception as e:
 | 
				
			||||||
            self.notify(f"Error updating entry: {str(e)}")
 | 
					           # Desfaz as mudanças em caso de erro
 | 
				
			||||||
 | 
					            self.notify(f"❌ Error on updating the entry:: {str(e)}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def on_key(self, event):
 | 
					    def on_key(self, event):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,11 @@
 | 
				
			||||||
from textual.app import ComposeResult
 | 
					from textual.app import ComposeResult
 | 
				
			||||||
from textual.binding import Binding
 | 
					 | 
				
			||||||
from textual.containers import Vertical, Horizontal
 | 
					from textual.containers import Vertical, Horizontal
 | 
				
			||||||
from textual.screen import ModalScreen
 | 
					from textual.screen import ModalScreen
 | 
				
			||||||
from textual.widgets import Label, Input, Button
 | 
					from textual.widgets import Label, Input, Button
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
class NewDiaryModal(ModalScreen[str]):
 | 
					class NewDiaryModal(ModalScreen[str]):
 | 
				
			||||||
    BINDINGS = [
 | 
					    BINDINGS = [
 | 
				
			||||||
        Binding("escape", "cancel", "Cancel"),
 | 
					        ("escape", "cancel", "Cancel"),
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
        super().__init__()
 | 
					        super().__init__()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,4 @@
 | 
				
			||||||
from textual.app import ComposeResult
 | 
					from textual.app import ComposeResult
 | 
				
			||||||
from textual.binding import Binding
 | 
					 | 
				
			||||||
from textual.containers import Vertical, Horizontal
 | 
					from textual.containers import Vertical, Horizontal
 | 
				
			||||||
from textual.screen import ModalScreen
 | 
					from textual.screen import ModalScreen
 | 
				
			||||||
from textual.widgets import Label, Input, Button
 | 
					from textual.widgets import Label, Input, Button
 | 
				
			||||||
| 
						 | 
					@ -9,7 +8,7 @@ class RenameEntryModal(ModalScreen[str]):
 | 
				
			||||||
    """A modal screen to rename a diary entry."""
 | 
					    """A modal screen to rename a diary entry."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    BINDINGS = [
 | 
					    BINDINGS = [
 | 
				
			||||||
        Binding("escape", "cancel", "Cancel"),
 | 
					        ("escape", "cancel", "Cancel"),
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, current_name: str):
 | 
					    def __init__(self, current_name: str):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +0,0 @@
 | 
				
			||||||
from .directory_manager import DirectoryManager
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
__all__ = ['DirectoryManager']
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,39 +0,0 @@
 | 
				
			||||||
import os
 | 
					 | 
				
			||||||
from pathlib import Path
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class DirectoryManager:
 | 
					 | 
				
			||||||
    @staticmethod
 | 
					 | 
				
			||||||
    def get_config_directory() -> Path:
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Get the ~/.pilgrim directory path.
 | 
					 | 
				
			||||||
        Creates it if it doesn't exist.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        home = Path.home()
 | 
					 | 
				
			||||||
        config_dir = home / ".pilgrim"
 | 
					 | 
				
			||||||
        config_dir.mkdir(exist_ok=True)
 | 
					 | 
				
			||||||
        os.chmod(config_dir, 0o700)
 | 
					 | 
				
			||||||
        return config_dir
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @staticmethod
 | 
					 | 
				
			||||||
    def get_diaries_root() -> Path:
 | 
					 | 
				
			||||||
        """Returns the path to the diaries directory."""
 | 
					 | 
				
			||||||
        diaries_dir = DirectoryManager.get_config_directory() / "diaries"
 | 
					 | 
				
			||||||
        diaries_dir.mkdir(exist_ok=True)
 | 
					 | 
				
			||||||
        os.chmod(diaries_dir, 0o700)
 | 
					 | 
				
			||||||
        return diaries_dir
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @staticmethod
 | 
					 | 
				
			||||||
    def get_diary_directory(directory_name: str) -> Path:
 | 
					 | 
				
			||||||
        """Returns the directory path for a specific diary."""
 | 
					 | 
				
			||||||
        return DirectoryManager.get_diaries_root() / directory_name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @staticmethod
 | 
					 | 
				
			||||||
    def get_diary_data_directory(directory_name: str) -> Path:
 | 
					 | 
				
			||||||
        """Returns the data directory path for a specific diary."""
 | 
					 | 
				
			||||||
        return DirectoryManager.get_diary_directory(directory_name) / "data"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @staticmethod
 | 
					 | 
				
			||||||
    def get_diary_images_directory(directory_name: str) -> Path:
 | 
					 | 
				
			||||||
        """Returns the images directory path for a specific diary."""
 | 
					 | 
				
			||||||
        return DirectoryManager.get_diary_data_directory(directory_name) / "images"
 | 
					 | 
				
			||||||
		Loading…
	
		Reference in New Issue