Holger Frey
2 years ago
3 changed files with 94 additions and 0 deletions
@ -0,0 +1,92 @@
@@ -0,0 +1,92 @@
|
||||
""" rename sensospot images """ |
||||
|
||||
import click |
||||
import pathlib |
||||
import shutil |
||||
|
||||
|
||||
RENAME_MAP_NAME = "rename_map.txt" |
||||
IMAGE_SUFFIXES = {".tif", ".jpg"} |
||||
|
||||
|
||||
def get_map_file_path(directory:str) -> pathlib.Path: |
||||
return pathlib.Path(directory) / RENAME_MAP_NAME |
||||
|
||||
|
||||
def list_images(directory:str) -> list[pathlib.Path]: |
||||
parent = pathlib.Path(directory) |
||||
non_hidden = (i for i in parent.iterdir() if not i.name.startswith(".")) |
||||
return [i for i in non_hidden if i.suffix.lower() in IMAGE_SUFFIXES] |
||||
|
||||
|
||||
def get_image_parts(image_path:pathlib.Path) -> tuple[str, str, str]: |
||||
return image_path.stem.rsplit("_", maxsplit=2) |
||||
|
||||
|
||||
def get_unique_parts(images:list[pathlib.Path]) -> list[list[str], list[str], list[str]]: |
||||
parts = [get_image_parts(p) for p in images] |
||||
return [sorted(set(items)) for items in zip(*parts)] |
||||
|
||||
|
||||
def write_rename_map(map_file:pathlib.Path) -> None: |
||||
images = list_images(directory) |
||||
parts = get_unique_parts(images) |
||||
headers = ["stems", "wells", "exposures"] |
||||
|
||||
lines = [] |
||||
for header, items in zip(headers, parts): |
||||
lines.append(f"# {header}") |
||||
lines.extend([f"{i}\t" for i in items]) |
||||
lines.append("") |
||||
lines.append("") |
||||
|
||||
map_file.write_text("\n".join(lines)) |
||||
|
||||
|
||||
def _parse_rename_map(content:str) -> tuple[str, str]: |
||||
lines = [line.strip() for line in content.splitlines()] |
||||
for i, line in enumerate(lines, start=1): |
||||
if not line or line.startswith("#"): |
||||
continue |
||||
if not "\t" in line: |
||||
raise ValueError(f"No tab in line {i}: '{line}'") |
||||
yield line.split("\t", maxsplit=1) |
||||
|
||||
|
||||
def read_rename_map(map_file:pathlib.Path) -> dict[str, str]: |
||||
content = map_file.read_text() |
||||
return {k: v for k, v in _parse_rename_map(content)} |
||||
|
||||
|
||||
def prepare_rename(images:list[pathlib.Path], rename_map:dict[str, str], sep="_") -> list[tuple[pathlib.Path, pathlib.Path]]: |
||||
for path in images: |
||||
renamed_parts = [rename_map[p] for p in get_image_parts(path)] |
||||
yield path, path.with_stem(sep.join(renamed_parts)) |
||||
|
||||
|
||||
@click.command() |
||||
@click.argument("directory", type=click.Path(exists=True, file_okay=False, dir_okay=True)) |
||||
def sensospot_rename(directory): |
||||
images = list_images(directory) |
||||
if not images: |
||||
raise click.UsageError(f"No images found in '{directory}'") |
||||
|
||||
map_file = get_map_file_path(directory) |
||||
if not map_file.is_file(): |
||||
write_rename_map(map_file) |
||||
click.echo(f"Prepared rename map at '{map_file!s}'") |
||||
click.echo(f"Rerun the command after editing the file.") |
||||
else: |
||||
click.echo(f"Reading rename map at '{map_file!s}'") |
||||
try: |
||||
rename_map = read_rename_map(map_file) |
||||
except ValueError as e: |
||||
raise click.UsageError(str(e)) |
||||
try: |
||||
prepared = list(prepare_rename(images, rename_map)) |
||||
except Exception: |
||||
raise click.UsageError("Could not rename images. Please check the image directory and rename map file.") |
||||
|
||||
for src, dst in prepared: |
||||
click.echo(f"renaming: {src} -> {dst}") |
||||
shutil.move(src, dst) |
Loading…
Reference in new issue