Some helper scripts for the day to day work with Ubuntu in WSL2
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

92 lines
3.0 KiB

""" 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)