feat: implement batch composition core
This commit is contained in:
+138
-2
@@ -1,2 +1,138 @@
|
||||
def run_batch(*args, **kwargs):
|
||||
raise NotImplementedError("Batch composition is not implemented yet.")
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Callable, Iterable, List, Optional, Tuple, Union
|
||||
|
||||
from core.composer import compose
|
||||
from core.models import (
|
||||
BatchMode,
|
||||
BatchOptions,
|
||||
BatchResult,
|
||||
ComposeResult,
|
||||
ImageAsset,
|
||||
TransformState,
|
||||
)
|
||||
from services.file_service import get_output_dir, make_safe_output_path
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
PathLike = Union[str, Path]
|
||||
ProgressCallback = Callable[[int, int, ComposeResult], None]
|
||||
|
||||
|
||||
def run_batch(
|
||||
garments: Iterable[Union[ImageAsset, PathLike]],
|
||||
prints: Iterable[Union[ImageAsset, PathLike]],
|
||||
transform: TransformState,
|
||||
options: BatchOptions,
|
||||
progress_callback: Optional[ProgressCallback] = None,
|
||||
) -> BatchResult:
|
||||
"""Run batch composition tasks.
|
||||
|
||||
The function only orchestrates pair generation and delegates all image
|
||||
processing to core.composer.compose(). A failed item is recorded and does
|
||||
not stop later tasks.
|
||||
"""
|
||||
garment_paths = _selected_paths(garments)
|
||||
print_paths = _selected_paths(prints)
|
||||
pairs = _build_pairs(garment_paths, print_paths, options.mode)
|
||||
|
||||
results = []
|
||||
export_options = options.export_options
|
||||
output_dir = Path(export_options.output_dir) if export_options.output_dir else get_output_dir()
|
||||
|
||||
logger.info(
|
||||
"Batch started: mode=%s, garments=%d, prints=%d, tasks=%d",
|
||||
options.mode.value,
|
||||
len(garment_paths),
|
||||
len(print_paths),
|
||||
len(pairs),
|
||||
)
|
||||
|
||||
total = len(pairs)
|
||||
for index, (garment_path, print_path) in enumerate(pairs, start=1):
|
||||
try:
|
||||
output_path = make_safe_output_path(
|
||||
output_dir,
|
||||
garment_path,
|
||||
print_path,
|
||||
export_options.output_format,
|
||||
)
|
||||
result = compose(
|
||||
garment_path=garment_path,
|
||||
print_path=print_path,
|
||||
transform=transform,
|
||||
options=export_options,
|
||||
output_path=output_path,
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.exception("Batch item failed unexpectedly: %s + %s", garment_path, print_path)
|
||||
result = ComposeResult(
|
||||
success=False,
|
||||
garment_path=garment_path,
|
||||
print_path=print_path,
|
||||
error=str(exc),
|
||||
)
|
||||
|
||||
results.append(result)
|
||||
if progress_callback is not None:
|
||||
progress_callback(index, total, result)
|
||||
|
||||
batch_result = _summarize(results)
|
||||
logger.info(
|
||||
"Batch finished: total=%d, success=%d, failure=%d",
|
||||
batch_result.total,
|
||||
batch_result.success_count,
|
||||
batch_result.failure_count,
|
||||
)
|
||||
return batch_result
|
||||
|
||||
|
||||
def _selected_paths(items: Iterable[Union[ImageAsset, PathLike]]) -> List[Path]:
|
||||
paths = []
|
||||
for item in items:
|
||||
if isinstance(item, ImageAsset):
|
||||
if not item.selected:
|
||||
continue
|
||||
paths.append(Path(item.path))
|
||||
else:
|
||||
paths.append(Path(item))
|
||||
return paths
|
||||
|
||||
|
||||
def _build_pairs(
|
||||
garment_paths: List[Path],
|
||||
print_paths: List[Path],
|
||||
mode: BatchMode,
|
||||
) -> List[Tuple[Path, Path]]:
|
||||
if mode == BatchMode.MANY_GARMENTS:
|
||||
if not print_paths:
|
||||
return []
|
||||
return [(garment_path, print_paths[0]) for garment_path in garment_paths]
|
||||
|
||||
if mode == BatchMode.MANY_PRINTS:
|
||||
if not garment_paths:
|
||||
return []
|
||||
return [(garment_paths[0], print_path) for print_path in print_paths]
|
||||
|
||||
if mode == BatchMode.ONE_TO_ONE:
|
||||
return list(zip(garment_paths, print_paths))
|
||||
|
||||
if mode == BatchMode.FULL_COMBO:
|
||||
return [
|
||||
(garment_path, print_path)
|
||||
for garment_path in garment_paths
|
||||
for print_path in print_paths
|
||||
]
|
||||
|
||||
raise ValueError("Unsupported batch mode: {}".format(mode))
|
||||
|
||||
|
||||
def _summarize(results: List[ComposeResult]) -> BatchResult:
|
||||
success_count = sum(1 for result in results if result.success)
|
||||
failure_count = len(results) - success_count
|
||||
return BatchResult(
|
||||
results=results,
|
||||
total=len(results),
|
||||
success_count=success_count,
|
||||
failure_count=failure_count,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user