mirror of
https://github.com/gmbrax/Pilgrim.git
synced 2026-11-10 03:40:16 +00:00
Implemented directory management and improved TravelDiary and Photo handling logic.
- Added `DirectoryManager` utility for consistent directory operations. - Introduced directory name sanitization and uniqueness enforcement for `TravelDiary`. - Updated `TravelDiaryService` with enhanced creation, update, and deletion workflows, including filesystem management. - Improved `PhotoService` to hash files, copy photos to specific diary directories, and manage updates safely. - Refined error handling and cascaded changes across related entities (e.g., `Entry` relationships).
This commit is contained in:
parent
b1e83aabbb
commit
090bbeda1a
@@ -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
|
||||
import asyncio
|
||||
|
||||
|
||||
class TravelDiaryService:
|
||||
def __init__(self,session):
|
||||
def __init__(self, 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):
|
||||
return self.session.query(TravelDiary).get(travel_id)
|
||||
# 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.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):
|
||||
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):
|
||||
original = self.read_by_id(travel_diary_id)
|
||||
if original is not None:
|
||||
original.name = name
|
||||
self.session.commit()
|
||||
self.session.refresh(original)
|
||||
return original
|
||||
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.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):
|
||||
return self.update(travel_diary_id, name)
|
||||
@@ -32,7 +134,14 @@ class TravelDiaryService:
|
||||
def delete(self, travel_diary_id: TravelDiary):
|
||||
excluded = self.read_by_id(travel_diary_id.id)
|
||||
if excluded is not None:
|
||||
self.session.delete(travel_diary_id)
|
||||
self.session.commit()
|
||||
return excluded
|
||||
try:
|
||||
# First delete the directory
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user