Skip to content

fsdb_clean_images_metadata

FSDB images.json Metadata Cleanup CLILink

Removes the redundant biological / hardware metadata entries from every scan's metadata/images.json in a database.

These entries are duplicated from metadata/metadata.json (which now holds the canonical MIAPPE-aligned tree):

  • older implementation (plant-imager): top-level object and hardware;
  • newer implementation (Plant-Imager3): a Metadata entry holding hardware and object.

Everything else in images.json (channels, task_params, workspace, picamera/picamera2, ScanPath, created, ...) is left untouched.

Usage ExamplesLink

# Dry-run first (shows what would be cleaned, writes nothing):
fsdb_clean_images_metadata /romi_db --dry-run

# Actually clean the files (a ``.bak`` copy is written for each changed file):
fsdb_clean_images_metadata /romi_db

# Clean without keeping backups:
fsdb_clean_images_metadata /romi_db --no-backup

clean_images_metadata Link

clean_images_metadata(scan_path, dry_run=False, backup=True)

Clean a single scan's metadata/images.json in place.

Parameters:

Name Type Description Default

scan_path Link

Path

Path to the scan directory.

required

dry_run Link

bool

If True, report what would change without writing anything.

False

backup Link

bool

If True and dry_run is False, write a .bak copy of each changed file before overwriting it.

True

Returns:

Type Description
bool

True if the file needed cleaning (and was cleaned unless dry_run was set), False if there was nothing to do.

Source code in plantdb/commons/cli/fsdb_clean_images_metadata.py
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
def clean_images_metadata(scan_path: Path, dry_run: bool = False,
                          backup: bool = True) -> bool:
    """Clean a single scan's ``metadata/images.json`` in place.

    Parameters
    ----------
    scan_path : pathlib.Path
        Path to the scan directory.
    dry_run : bool, optional
        If ``True``, report what would change without writing anything.
    backup : bool, optional
        If ``True`` and ``dry_run`` is ``False``, write a ``.bak`` copy of
        each changed file before overwriting it.

    Returns
    -------
    bool
        ``True`` if the file needed cleaning (and was cleaned unless
        ``dry_run`` was set), ``False`` if there was nothing to do.
    """
    img_path = scan_path / "metadata" / "images.json"
    if not img_path.is_file():
        return False
    with img_path.open() as f:
        images = json.load(f)
    if not isinstance(images, dict):
        logger.warning(f"Skipping non-dict images.json: {img_path}")
        return False
    if not _clean_images_metadata(images):
        return False
    if dry_run:
        logger.info(f"[dry-run] Would clean '{img_path}'.")
        return True
    if backup:
        bak_path = img_path.with_suffix(img_path.suffix + ".bak")
        shutil.copy2(img_path, bak_path)
    with img_path.open("w") as f:
        json.dump(images, f, sort_keys=True, indent=4, separators=(',', ': '))
    logger.info(f"Cleaned '{img_path}'.")
    return True

main Link

main(db_path, dry_run, no_backup, log_level)

Clean redundant metadata entries from every scan's images.json.

Source code in plantdb/commons/cli/fsdb_clean_images_metadata.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
@click.command(context_settings=dict(help_option_names=["-h", "--help"]))
@click.argument('db_path', type=click.Path(exists=True))
@click.option(
    "--dry-run",
    is_flag=True,
    help="Report what would be cleaned without modifying any file.",
)
@click.option(
    "--no-backup",
    is_flag=True,
    help="Do not write a '.bak' copy of changed files.",
)
@click.option(
    "--log-level",
    type=click.Choice(LOG_LEVELS, case_sensitive=False),
    default=DEFAULT_LOG_LEVEL,
    show_default=True,
    help="Logging level.",
)
def main(db_path, dry_run, no_backup, log_level):
    """Clean redundant metadata entries from every scan's images.json."""
    logger.setLevel(log_level)
    db_path = Path(db_path).resolve()
    cleaned = unchanged = 0
    for scan_path in db_path.iterdir():
        if not scan_path.is_dir():
            continue
        if clean_images_metadata(scan_path, dry_run=dry_run, backup=not no_backup):
            cleaned += 1
        else:
            unchanged += 1
    verb = "Would clean" if dry_run else "Cleaned"
    logger.info(f"Done: {verb} {cleaned} scan(s), {unchanged} unchanged.")