Cybersecurity-Projects/src/utils/display.py

141 lines
4.6 KiB
Python

"""
Display utilities for rich terminal output.
This module provides functions for displaying metadata and batch processing
results in beautifully formatted tables and panels using the Rich library.
"""
from typing import Any
from rich import box
from rich.console import Console
from rich.panel import Panel
from rich.table import Table
from rich.text import Text
from src.utils.formatter import clean_value
console = Console()
def print_metadata_table(metadata: dict[str, Any]):
"""
Display metadata in a formatted table organized by logical groups.
Organizes metadata into categories (Device Info, Exposure Settings,
Image Data, Dates) and displays them in a Rich panel with color coding.
Args:
metadata: Dict of metadata key-value pairs to display.
"""
# Define the groups using simple lists of keys
groups = {
"📸 Device Info": ["Make", "Model", "Software", "ExifVersion"],
"⚙️ Exposure Settings": [
"ExposureTime",
"FNumber",
"ISOSpeedRatings",
"ShutterSpeedValue",
"ApertureValue",
"Flash",
"FocalLength",
],
"🖼️ Image Data": [
"ImageWidth",
"ImageLength",
"PixelXDimension",
"PixelYDimension",
"Orientation",
"ResolutionUnit",
],
"📅 Dates": ["DateTime", "DateTimeOriginal", "DateTimeDigitized", "OffsetTime"],
}
# Create the main table
table = Table(box=box.ROUNDED, show_header=True, header_style="bold magenta")
table.add_column("Property", style="cyan")
table.add_column("Value", style="green")
# Track which keys we have displayed to handle the "leftovers"
displayed_keys = set()
# Loop through the defined groups to create sections
for section_name, keys in groups.items():
# Check if we have any data for this section
section_data = {k: metadata[k] for k in keys if k in metadata}
if section_data:
# Add a section row (acts as a sub-header)
table.add_row(Text(section_name, style="bold yellow"), "")
for key, val in section_data.items():
table.add_row(f" {key}", clean_value(val))
displayed_keys.add(key)
# Add a blank row for spacing
table.add_section()
# Handle "Other" (Any keys that isn't in the groups)
leftovers = {
k: v
for k, v in metadata.items()
if k not in displayed_keys and k != "JPEGInterchangeFormat"
} # skip binary blobs
if leftovers:
table.add_row(Text("📝 Other", style="bold yellow"), "")
for key, val in leftovers.items():
table.add_row(f" {key}", clean_value(val))
# Print nicely inside a panel
console.print(
Panel(table, title="Metadata Report", border_style="blue", expand=False)
)
def print_batch_summary(summary) -> None:
"""
Display batch processing results in a rich panel.
Args:
summary: BatchSummary object with processing statistics.
"""
# Build the summary table
table = Table(box=box.ROUNDED, show_header=False, expand=False)
table.add_column("Metric", style="cyan")
table.add_column("Value", style="green")
if summary.dry_run:
table.add_row("Mode", "[yellow]DRY-RUN (no changes made)[/yellow]")
table.add_row("Would process", str(summary.success))
table.add_row("Would skip", str(summary.skipped))
else:
table.add_row("Total processed", str(summary.total))
table.add_row("✅ Success", f"[green]{summary.success}[/green]")
table.add_row("⚠️ Skipped", f"[yellow]{summary.skipped}[/yellow]")
if summary.output_dir:
table.add_row("📁 Output", str(summary.output_dir.resolve()))
# Show failed files if any
failed = [r for r in summary.results if not r.success]
if failed:
table.add_section()
table.add_row(Text("Failed files:", style="bold red"), "")
for result in failed[:5]: # Show max 5 failures
table.add_row(f" {result.filepath.name}", f"[dim]{result.error}[/dim]")
if len(failed) > 5:
table.add_row("", f"[dim]... and {len(failed) - 5} more[/dim]")
# Determine panel title and style
if summary.dry_run:
title = "🔍 Dry-Run Summary"
border_style = "yellow"
elif summary.skipped == 0:
title = "✅ Scrub Complete"
border_style = "green"
else:
title = "⚠️ Scrub Complete (with warnings)"
border_style = "yellow"
console.print(Panel(table, title=title, border_style=border_style, expand=False))