This commit introduces a folder browsing button in the Dashboard GUI, allowing users to select a folder for photo scanning. It also implements path normalization and validation using new utility functions from the path_utils module, ensuring that folder paths are absolute and accessible before scanning. Additionally, the PhotoManager class has been updated to utilize these path utilities, enhancing the robustness of folder scanning operations. This improves user experience by preventing errors related to invalid paths and streamlining folder management across the application.
175 lines
4.4 KiB
Python
175 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Path utility functions for PunimTag
|
|
Ensures all paths are stored as absolute paths for consistency
|
|
"""
|
|
|
|
import os
|
|
from pathlib import Path
|
|
from typing import Union
|
|
|
|
|
|
def normalize_path(path: Union[str, Path]) -> str:
|
|
"""
|
|
Convert any path to an absolute path.
|
|
|
|
Args:
|
|
path: Path to normalize (can be relative or absolute)
|
|
|
|
Returns:
|
|
Absolute path as string
|
|
|
|
Examples:
|
|
normalize_path("demo_photos") -> "/home/user/punimtag/demo_photos"
|
|
normalize_path("./photos") -> "/home/user/punimtag/photos"
|
|
normalize_path("/absolute/path") -> "/absolute/path"
|
|
"""
|
|
if not path:
|
|
raise ValueError("Path cannot be empty or None")
|
|
|
|
# Convert to string if Path object
|
|
path_str = str(path)
|
|
|
|
# Use pathlib for robust path resolution
|
|
normalized = Path(path_str).resolve()
|
|
|
|
return str(normalized)
|
|
|
|
|
|
def is_absolute_path(path: Union[str, Path]) -> bool:
|
|
"""
|
|
Check if a path is absolute.
|
|
|
|
Args:
|
|
path: Path to check
|
|
|
|
Returns:
|
|
True if path is absolute, False if relative
|
|
"""
|
|
return Path(path).is_absolute()
|
|
|
|
|
|
def is_relative_path(path: Union[str, Path]) -> bool:
|
|
"""
|
|
Check if a path is relative.
|
|
|
|
Args:
|
|
path: Path to check
|
|
|
|
Returns:
|
|
True if path is relative, False if absolute
|
|
"""
|
|
return not Path(path).is_absolute()
|
|
|
|
|
|
def validate_path_exists(path: Union[str, Path]) -> bool:
|
|
"""
|
|
Check if a path exists and is accessible.
|
|
|
|
Args:
|
|
path: Path to validate
|
|
|
|
Returns:
|
|
True if path exists and is accessible, False otherwise
|
|
"""
|
|
try:
|
|
normalized = normalize_path(path)
|
|
return os.path.exists(normalized) and os.access(normalized, os.R_OK)
|
|
except (ValueError, OSError):
|
|
return False
|
|
|
|
|
|
def get_path_info(path: Union[str, Path]) -> dict:
|
|
"""
|
|
Get detailed information about a path.
|
|
|
|
Args:
|
|
path: Path to analyze
|
|
|
|
Returns:
|
|
Dictionary with path information
|
|
"""
|
|
try:
|
|
normalized = normalize_path(path)
|
|
path_obj = Path(normalized)
|
|
|
|
return {
|
|
'original': str(path),
|
|
'normalized': normalized,
|
|
'is_absolute': path_obj.is_absolute(),
|
|
'is_relative': not path_obj.is_absolute(),
|
|
'exists': path_obj.exists(),
|
|
'is_file': path_obj.is_file(),
|
|
'is_dir': path_obj.is_dir(),
|
|
'parent': str(path_obj.parent),
|
|
'name': path_obj.name,
|
|
'stem': path_obj.stem,
|
|
'suffix': path_obj.suffix
|
|
}
|
|
except Exception as e:
|
|
return {
|
|
'original': str(path),
|
|
'error': str(e)
|
|
}
|
|
|
|
|
|
def ensure_directory_exists(path: Union[str, Path]) -> str:
|
|
"""
|
|
Ensure a directory exists, creating it if necessary.
|
|
|
|
Args:
|
|
path: Directory path to ensure exists
|
|
|
|
Returns:
|
|
Absolute path to the directory
|
|
"""
|
|
normalized = normalize_path(path)
|
|
Path(normalized).mkdir(parents=True, exist_ok=True)
|
|
return normalized
|
|
|
|
|
|
def get_relative_path_from_base(absolute_path: Union[str, Path], base_path: Union[str, Path]) -> str:
|
|
"""
|
|
Get relative path from a base directory.
|
|
|
|
Args:
|
|
absolute_path: Absolute path to convert
|
|
base_path: Base directory to make relative to
|
|
|
|
Returns:
|
|
Relative path from base directory
|
|
"""
|
|
abs_path = normalize_path(absolute_path)
|
|
base = normalize_path(base_path)
|
|
|
|
try:
|
|
return str(Path(abs_path).relative_to(base))
|
|
except ValueError:
|
|
# If paths don't share a common base, return the absolute path
|
|
return abs_path
|
|
|
|
|
|
# Test the utility functions
|
|
if __name__ == "__main__":
|
|
print("=== Path Utility Functions Test ===")
|
|
|
|
test_paths = [
|
|
"demo_photos",
|
|
"./demo_photos",
|
|
"../demo_photos",
|
|
"/home/ladmin/Code/punimtag/demo_photos",
|
|
"C:/Users/Test/Photos",
|
|
"/tmp/test"
|
|
]
|
|
|
|
for path in test_paths:
|
|
print(f"\nTesting: {path}")
|
|
try:
|
|
normalized = normalize_path(path)
|
|
info = get_path_info(path)
|
|
print(f" Normalized: {normalized}")
|
|
print(f" Exists: {info['exists']}")
|
|
print(f" Is directory: {info['is_dir']}")
|
|
except Exception as e:
|
|
print(f" Error: {e}")
|