mirror of https://github.com/gmbrax/Pilgrim.git
169 lines
6.3 KiB
Python
169 lines
6.3 KiB
Python
import hashlib
|
|
import os
|
|
import shutil
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import List
|
|
|
|
from pilgrim.models.photo import Photo
|
|
from pilgrim.models.travel_diary import TravelDiary
|
|
from pilgrim.utils import DirectoryManager
|
|
|
|
|
|
class PhotoService:
|
|
def __init__(self, session):
|
|
self.session = session
|
|
|
|
@staticmethod
|
|
def hash_file(filepath: Path) -> str:
|
|
"""Calculate the hash of a file using SHA3-384."""
|
|
hash_func = hashlib.new('sha3_384')
|
|
with open(filepath, 'rb') as f:
|
|
while chunk := f.read(8192):
|
|
hash_func.update(chunk)
|
|
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 check_photo_by_hash(self, photohash:str, traveldiaryid:int):
|
|
photo = (self.session.query(Photo).filter(Photo.photo_hash == photohash,Photo.fk_travel_diary_id == traveldiaryid)
|
|
.first())
|
|
return photo
|
|
|
|
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()
|
|
if not travel_diary:
|
|
return None
|
|
photo_hash = self.hash_file(filepath)
|
|
if self.check_photo_by_hash(photo_hash, travel_diary_id):
|
|
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
|
|
if isinstance(addition_date, str):
|
|
try:
|
|
addition_date = datetime.strptime(addition_date, "%Y-%m-%d %H:%M:%S")
|
|
except ValueError:
|
|
addition_date = None
|
|
|
|
|
|
new_photo = Photo(
|
|
filepath=str(copied_path), # Store the path to the copied file
|
|
name=name,
|
|
caption=caption,
|
|
fk_travel_diary_id=travel_diary_id,
|
|
addition_date=addition_date,
|
|
photo_hash=photo_hash
|
|
)
|
|
self.session.add(new_photo)
|
|
self.session.commit()
|
|
self.session.refresh(new_photo)
|
|
|
|
return new_photo
|
|
|
|
def read_by_id(self, photo_id:int) -> Photo:
|
|
return self.session.query(Photo).get(photo_id)
|
|
|
|
def read_all(self) -> List[Photo]:
|
|
return self.session.query(Photo).all()
|
|
|
|
def update(self, photo_src: Photo, photo_dst: Photo) -> Photo | None:
|
|
original: Photo = self.read_by_id(photo_src.id)
|
|
if original:
|
|
# 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.addition_date = photo_dst.addition_date
|
|
original.caption = photo_dst.caption
|
|
|
|
if photo_dst.entries and len(photo_dst.entries) > 0:
|
|
if original.entries is None:
|
|
original.entries = []
|
|
original.entries = photo_dst.entries # Replace instead of extend
|
|
|
|
self.session.commit()
|
|
self.session.refresh(original)
|
|
return original
|
|
return None
|
|
|
|
def delete(self, photo_src: Photo) -> Photo | None:
|
|
excluded = self.read_by_id(photo_src.id)
|
|
if excluded:
|
|
# Store photo data before deletion
|
|
deleted_photo = Photo(
|
|
filepath=excluded.filepath,
|
|
name=excluded.name,
|
|
addition_date=excluded.addition_date,
|
|
caption=excluded.caption,
|
|
fk_travel_diary_id=excluded.fk_travel_diary_id,
|
|
id=excluded.id,
|
|
photo_hash=excluded.photo_hash,
|
|
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.commit()
|
|
|
|
return deleted_photo
|
|
return None
|