mirror of https://github.com/gmbrax/Pilgrim.git
Merge pull request #19 from gmbrax/feat/photo-copying-system-dir
Feat/photo copying system dir
This commit is contained in:
commit
20c56e2c1b
|
|
@ -1,47 +1,18 @@
|
||||||
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 pathlib import Path
|
from pilgrim.utils import DirectoryManager
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
class Application:
|
class Application:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.config_dir = self._ensure_config_directory()
|
self.config_dir = DirectoryManager.get_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,7 +17,9 @@ 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,14 +1,26 @@
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from sqlalchemy import Column, String, Integer
|
from sqlalchemy import Column, Integer, String, UniqueConstraint
|
||||||
|
from sqlalchemy.orm import relationship
|
||||||
|
|
||||||
from ..database import Base
|
from .. import database
|
||||||
|
|
||||||
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)
|
name = Column(String, nullable=False)
|
||||||
|
directory_name = Column(String, nullable=False, unique=True)
|
||||||
|
entries = relationship("Entry", back_populates="travel_diary", cascade="all, delete-orphan")
|
||||||
|
|
||||||
def __init__(self, name: str, **kw: Any):
|
__table_args__ = (
|
||||||
|
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,26 +1,76 @@
|
||||||
|
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):
|
||||||
|
|
@ -28,20 +78,24 @@ class PhotoService:
|
||||||
addition_date = datetime.strptime(addition_date, "%Y-%m-%d %H:%M:%S")
|
addition_date = datetime.strptime(addition_date, "%Y-%m-%d %H:%M:%S")
|
||||||
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=filepath,
|
filepath=str(copied_path), # Store the path to the copied file
|
||||||
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=self._hash_file(filepath)
|
photo_hash=photo_hash
|
||||||
)
|
)
|
||||||
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)
|
||||||
|
|
||||||
|
|
@ -51,15 +105,30 @@ 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:
|
||||||
original.filepath = photo_dst.filepath
|
# If filepath changed, need to copy new file
|
||||||
|
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
|
||||||
|
|
@ -78,8 +147,12 @@ 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,30 +1,132 @@
|
||||||
|
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)
|
|
||||||
|
|
||||||
return new_travel_diary
|
def _sanitize_directory_name(self, name: str) -> str:
|
||||||
|
"""
|
||||||
|
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()
|
||||||
|
|
||||||
def read_by_id(self, travel_id:int):
|
# Ensure we have a valid name
|
||||||
return self.session.query(TravelDiary).get(travel_id)
|
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.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):
|
||||||
return self.session.query(TravelDiary).all()
|
diaries = 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:
|
||||||
original.name = name
|
try:
|
||||||
self.session.commit()
|
# Generate new directory name
|
||||||
self.session.refresh(original)
|
new_directory_name = self._sanitize_directory_name(name)
|
||||||
return original
|
old_directory = self._get_diary_directory(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)
|
||||||
|
|
@ -32,7 +134,14 @@ 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:
|
||||||
self.session.delete(travel_diary_id)
|
try:
|
||||||
self.session.commit()
|
# First delete the directory
|
||||||
return excluded
|
self._cleanup_diary_directory(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
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,20 @@
|
||||||
from typing import Optional, List
|
import re
|
||||||
import asyncio
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import hashlib
|
from typing import Optional, List
|
||||||
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.travel_diary import TravelDiary
|
|
||||||
from pilgrim.models.photo import Photo
|
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.add_photo_modal import AddPhotoModal
|
||||||
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.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.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.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):
|
||||||
|
|
@ -333,9 +328,11 @@ 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):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
from .directory_manager import DirectoryManager
|
||||||
|
|
||||||
|
__all__ = ['DirectoryManager']
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
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