mirror of
https://github.com/gmbrax/Pilgrim.git
synced 2026-11-10 07:10:17 +00:00
Compare commits
14
Commits
@@ -4,6 +4,7 @@ from pathlib import Path
|
||||
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, DateTime
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql.schema import Index
|
||||
|
||||
from pilgrim.models.photo_in_entry import photo_entry_association
|
||||
from ..database import Base
|
||||
@@ -24,6 +25,9 @@ class Photo(Base):
|
||||
)
|
||||
|
||||
fk_travel_diary_id = Column(Integer, ForeignKey("travel_diaries.id"),nullable=False)
|
||||
__table_args__ = (
|
||||
Index('idx_photo_hash_diary', 'hash', 'fk_travel_diary_id'),
|
||||
)
|
||||
|
||||
def __init__(self, filepath, name, photo_hash, addition_date=None, caption=None, entries=None, fk_travel_diary_id=None, **kw: Any):
|
||||
super().__init__(**kw)
|
||||
|
||||
@@ -14,8 +14,9 @@ class PhotoService:
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
|
||||
def _hash_file(self, filepath: Path) -> str:
|
||||
"""Calculate hash of a file using SHA3-384."""
|
||||
@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):
|
||||
@@ -64,10 +65,18 @@ class PhotoService:
|
||||
|
||||
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)
|
||||
@@ -79,8 +88,6 @@ class PhotoService:
|
||||
except ValueError:
|
||||
addition_date = None
|
||||
|
||||
# Calculate hash from the copied file
|
||||
photo_hash = self._hash_file(copied_path)
|
||||
|
||||
new_photo = Photo(
|
||||
filepath=str(copied_path), # Store the path to the copied file
|
||||
@@ -118,7 +125,7 @@ class PhotoService:
|
||||
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.photo_hash = self.hash_file(new_path)
|
||||
|
||||
original.name = photo_dst.name
|
||||
original.addition_date = photo_dst.addition_date
|
||||
|
||||
@@ -611,7 +611,8 @@ class EditEntryScreen(Screen):
|
||||
addition_date=original_photo.addition_date,
|
||||
caption=photo_data["caption"],
|
||||
entries=original_photo.entries if original_photo.entries is not None else [],
|
||||
id=original_photo.id
|
||||
id=original_photo.id,
|
||||
photo_hash=original_photo.photo_hash,
|
||||
)
|
||||
|
||||
result = photo_service.update(original_photo, updated_photo)
|
||||
|
||||
@@ -15,12 +15,6 @@ class AddPhotoModal(Screen):
|
||||
self.result = None
|
||||
self.created_photo = None
|
||||
|
||||
def _generate_photo_hash(self, photo_data: dict) -> str:
|
||||
"""Generate a short, unique hash for a photo"""
|
||||
# Use temporary data for hash generation
|
||||
unique_string = f"{photo_data['name']}_{photo_data.get('photo_id', 0)}_new"
|
||||
hash_object = hashlib.md5(unique_string.encode())
|
||||
return hash_object.hexdigest()[:8]
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Container(
|
||||
@@ -69,10 +63,16 @@ class AddPhotoModal(Screen):
|
||||
|
||||
async def _async_create_photo(self, photo_data: dict):
|
||||
"""Creates a new photo asynchronously using PhotoService"""
|
||||
|
||||
|
||||
try:
|
||||
service_manager = self.app.service_manager
|
||||
photo_service = service_manager.get_photo_service()
|
||||
|
||||
if photo_service.check_photo_by_hash(photo_service.hash_file(photo_data["filepath"]),self.diary_id):
|
||||
self.notify("Photo already exists in database", severity="error")
|
||||
return
|
||||
|
||||
new_photo = photo_service.create(
|
||||
filepath=Path(photo_data["filepath"]),
|
||||
name=photo_data["name"],
|
||||
@@ -82,13 +82,9 @@ class AddPhotoModal(Screen):
|
||||
|
||||
if new_photo:
|
||||
self.created_photo = new_photo
|
||||
# Generate hash for the new photo
|
||||
photo_hash = self._generate_photo_hash({
|
||||
"name": new_photo.name,
|
||||
"photo_id": new_photo.id
|
||||
})
|
||||
|
||||
|
||||
self.notify(f"Photo '{new_photo.name}' added successfully!\nHash: {photo_hash}\nReference: \\[\\[photo:{new_photo.name}:{photo_hash}\\]\\]",
|
||||
self.notify(f"Photo '{new_photo.name}' added successfully!\nHash: {new_photo.photo_hash[:8]}\nReference: \\[\\[photo:{new_photo.name}:{new_photo.photo_hash[:8]}\\]\\]",
|
||||
severity="information", timeout=5)
|
||||
|
||||
# Return the created photo data to the calling screen
|
||||
@@ -97,7 +93,7 @@ class AddPhotoModal(Screen):
|
||||
"name": photo_data["name"],
|
||||
"caption": photo_data["caption"],
|
||||
"photo_id": new_photo.id,
|
||||
"hash": photo_hash
|
||||
"hash": new_photo.photo_hash
|
||||
}
|
||||
self.dismiss(self.result)
|
||||
else:
|
||||
|
||||
@@ -3,7 +3,7 @@ from textual.screen import Screen
|
||||
from textual.widgets import Static, Input, Button
|
||||
from textual.containers import Container, Horizontal
|
||||
from pilgrim.models.photo import Photo
|
||||
import hashlib
|
||||
|
||||
|
||||
class EditPhotoModal(Screen):
|
||||
"""Modal for editing an existing photo (name and caption only)"""
|
||||
@@ -12,15 +12,11 @@ class EditPhotoModal(Screen):
|
||||
self.photo = photo
|
||||
self.result = None
|
||||
|
||||
def _generate_photo_hash(self, photo: Photo) -> str:
|
||||
"""Generate a short, unique hash for a photo"""
|
||||
unique_string = f"{photo.name}_{photo.id}_{photo.addition_date}"
|
||||
hash_object = hashlib.md5(unique_string.encode())
|
||||
return hash_object.hexdigest()[:8]
|
||||
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
# Generate hash for this photo
|
||||
photo_hash = self._generate_photo_hash(self.photo)
|
||||
|
||||
|
||||
yield Container(
|
||||
Static("✏️ Edit Photo", classes="EditPhotoModal-Title"),
|
||||
@@ -45,10 +41,9 @@ class EditPhotoModal(Screen):
|
||||
id="caption-input",
|
||||
classes="EditPhotoModal-Input"
|
||||
),
|
||||
Static(f"🔗 Photo Hash: {photo_hash}", classes="EditPhotoModal-Hash"),
|
||||
Static(f"🔗 Photo Hash: {self.photo.photo_hash[:8]}", classes="EditPhotoModal-Hash"),
|
||||
Static("Reference formats:", classes="EditPhotoModal-Label"),
|
||||
Static(f"\\[\\[photo:{self.photo.name}:{photo_hash}\\]\\]", classes="EditPhotoModal-Reference"),
|
||||
Static(f"\\[\\[photo::{photo_hash}\\]\\]", classes="EditPhotoModal-Reference"),
|
||||
Static(f"\\[\\[photo::{self.photo.photo_hash[:8]}\\]\\]", classes="EditPhotoModal-Reference"),
|
||||
Horizontal(
|
||||
Button("Save Changes", id="save-button", classes="EditPhotoModal-Button"),
|
||||
Button("Cancel", id="cancel-button", classes="EditPhotoModal-Button"),
|
||||
|
||||
Reference in New Issue
Block a user