Compare commits

..

No commits in common. "6724a66fed14c894e42352708a41aba9d972dc27" and "b1e83aabbb2f69d8339a4012d5fa79934148d632" have entirely different histories.

11 changed files with 114 additions and 326 deletions

View File

@ -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()

View File

@ -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

View File

@ -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}')>"

View File

@ -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()

View File

@ -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):
new_travel_diary = TravelDiary(name)
self.session.add(new_travel_diary)
self.session.commit()
self.session.refresh(new_travel_diary)
def _sanitize_directory_name(self, name: str) -> str: return new_travel_diary
"""
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 def read_by_id(self, travel_id:int):
if not safe_name: return self.session.query(TravelDiary).get(travel_id)
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.commit()
self.session.refresh(new_travel_diary)
# Create directory structure for the new diary
self._ensure_diary_directory(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):
diary = 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: original.name = name
# Generate new directory name self.session.commit()
new_directory_name = self._sanitize_directory_name(name) self.session.refresh(original)
old_directory = self._get_diary_directory(original) return original
# Update diary
original.name = name
original.directory_name = new_directory_name
self.session.commit()
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
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: self.session.delete(travel_diary_id)
# First delete the directory self.session.commit()
self._cleanup_diary_directory(excluded) return excluded
# Then delete from database
self.session.delete(travel_diary_id)
self.session.commit()
return excluded
except Exception as e:
self.session.rollback()
raise ValueError(f"Could not delete diary: {str(e)}")
return None return None

View File

@ -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):

View File

@ -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."""
service_manager = self.app.service_manager
db_session = service_manager.get_db_session()
try: try:
service_manager = self.app.service_manager
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
self.has_unsaved_changes = False
if result: self._original_content = updated_content # Pode ser o texto com hashes curtos
self.has_unsaved_changes = False self._update_sub_header()
self._original_content = updated_content self.notify(f"✅ Entry: '{current_entry.title}' sucesfully saved")
self._update_sub_header()
self.notify(f"Entry '{current_entry.title}' saved successfully!")
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):

View File

@ -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__()

View File

@ -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):

View File

@ -1,3 +0,0 @@
from .directory_manager import DirectoryManager
__all__ = ['DirectoryManager']

View File

@ -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"