New script to move finished plots (VERSION 2)

Version 1 = here:

The requirements for the script are the same as in version 1. read beforehand if necessary.
Due to an idea from another user => here version 2 with extended output.

The new version now shows the amount of plots that would fit.
Also the ones that already exist.

Also new is an overview table:
See 2nd table on the right.
But this is just for now. Surely there is still some more information possible. E.g. with maximum c30 plots = how many EB would result. Or percentage added value, etc etc.
But I didn’t want to make the table too long. Anyone can add to it themselves.
The number of possible plots is available. You only need to multiply them with “ziel_plot_groesse_gib”.

Have fun with it

P.s. As before: Without guarantee :wink:

import os
import time
from pathlib import Path
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from datetime import timedelta
import locale
import datetime

# Konfiguration
testlauf = 0  # 1 für Testmodus, 0 für echten Löschvorgang
wait_time_minutes = 30  # Wartezeit in Minuten zwischen den Durchläufen
min_free_space_gb = 44  # Mindestmenge an freiem Speicherplatz in GB
to_delete_patterns = ["plot-k32-c07", "plot-k32-c05", "plot-k32-202"]  # Muster für zu löschende Dateien
not_to_delete_pattern = "plot-k32-c30"  # Muster für nicht zu löschende Dateien
plot_name_short = "c30" ## Nur fĂĽr die Textliche Ausgabe ohne Berechnung
ziel_plot_groesse_gib = 43.3  # Zielgröße für Plots in GiB
min_plotting_time_in_min = "2:10"  # Die Laufzeit fĂĽr den schnellsten Plotvorgang in Minuten
max_plotting_time_in_min = "2:30"  # Die Laufzeit fĂĽr den langsamsten Plotvorgang in Minuten
directories = [
    "/mnt/01", "/mnt/02", "/mnt/03", "/mnt/04", "/mnt/05", "/mnt/06", "/mnt/07", "/mnt/08",
    "/mnt/09", "/mnt/10", "/mnt/11", "/mnt/12", "/mnt/13", "/mnt/14", "/mnt/15", "/mnt/16",
    "/mnt/17", "/mnt/18", "/mnt/19", "/mnt/20", "/mnt/21", "/mnt/22", "/mnt/23", "/mnt/24",
    "/mnt/25", "/mnt/26", "/mnt/27", "/mnt/28", "/mnt/29", "/mnt/30", "/mnt/31", "/mnt/32",
    "/mnt/97", "/mnt/98", "/mnt/99", "/mnt/100"

# Setze das Locale, um Tausender-Trennzeichen entsprechend zu formatieren
locale.setlocale(locale.LC_ALL, '')

def format_number(number):
    """Formatiert die Zahl mit Tausender-Trennzeichen und rundet sie ab."""
    return f"{number:n}"

def calculate_rest_time(total_plots, min_time, max_time):
    """Berechnet die geschätzte Restlaufzeit in Minuten basierend auf der Anzahl der verbleibenden Plots und Plot-Zeiten in Minuten."""
    min_time_minutes = convert_time_to_minutes(min_time)
    max_time_minutes = convert_time_to_minutes(max_time)

    # Berechnung der Restlaufzeit direkt in Minuten
    total_min_rest_time_minutes = total_plots * min_time_minutes
    total_max_rest_time_minutes = total_plots * max_time_minutes

    return total_min_rest_time_minutes, total_max_rest_time_minutes

def convert_time_to_minutes(time_str):
    """Konvertiert eine Zeit im Format 'MM:SS' direkt in Minuten."""
    minutes, seconds = map(int, time_str.split(":"))
    return minutes + seconds / 60

def format_time(minutes):
    """Formatiert Minuten in Tage, Stunden und Minuten."""
    days, remainder = divmod(minutes, 1440)
    hours, minutes = divmod(remainder, 60)
    return f"{int(days)}d {int(hours)}h {int(minutes)}m"

def gb_to_gib(size_gb):
    """Umrechnung von GB in GiB."""
    return size_gb / 1.07374

def get_free_space_gb(folder):
    """Gibt den freien Speicherplatz des Verzeichnisses in Gigabyte zurĂĽck."""
    st = os.statvfs(folder)
    free_space_bytes = st.f_bavail * st.f_frsize
    free_space_gb = free_space_bytes / 1024 / 1024 / 1024
    return free_space_gb

def get_total_space_gb(folder):
    """Gibt die Gesamtkapazität des Verzeichnisses in Gigabyte zurück."""
    st = os.statvfs(folder)
    total_space_bytes = st.f_blocks * st.f_frsize
    total_space_gb = total_space_bytes / 1024 / 1024 / 1024
    return total_space_gb

def can_delete_any_file(directory):
    """Überprüft, ob eine löschbare Datei im Verzeichnis vorhanden ist."""
    for root, dirs, files in os.walk(directory):
        for file in files:
            if any(file.startswith(pattern) for pattern in to_delete_patterns) and not file.startswith(not_to_delete_pattern):
                return True
    return False

def count_c30_plots(directory):
    """Zählt die Anzahl der c30 Plots im Verzeichnis."""
    count = 0
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.startswith(not_to_delete_pattern):
                count += 1
    return count

def delete_file(directory, console):
    """Löscht eine Datei aus dem Verzeichnis basierend auf den gegebenen Kriterien."""
    deleted = False
    for root, dirs, files in os.walk(directory):
        for file in files:
            if any(file.startswith(pattern) for pattern in to_delete_patterns) and not file.startswith(not_to_delete_pattern):
                file_path = Path(root) / file
                current_time ="%d.%m.%Y %H:%M")
                if testlauf == 1:
                    console.print(f"[white]{current_time}[/white] [TESTMODUS] Würde Datei löschen: {file_path}", style="yellow")
                    console.print(f"[white]{current_time}[/white] Datei gelöscht: {file_path}", style="green")
                deleted = True
        if deleted:
    return deleted

def main():
    console = Console()
    gesamt_speicherplatz_gb = 0
    for directory in directories:
        gesamt_speicherplatz_gb += get_total_space_gb(directory)

    gesamt_mögliche_plots = int(gesamt_speicherplatz_gb / ziel_plot_groesse_gib)
    # Initialisiere gesamt_vorhandene_plots hier, basierend auf der Summe der c30-Plots in allen Verzeichnissen
    gesamt_vorhandene_plots = sum(count_c30_plots(directory) for directory in directories)

    while True:
        gesamt_speicherplatz_gb = 0

        # Hier gesamt_vorhandene_plots erneut berechnen, falls sich die Anzahl der Plots ändert
        gesamt_vorhandene_plots = sum(count_c30_plots(directory) for directory in directories)
        gesamt_verbleibende_plots = gesamt_mögliche_plots - gesamt_vorhandene_plots  # Korrekte Anzahl der verbleibenden Plots
        min_rest_time_min, max_rest_time_min = calculate_rest_time(gesamt_verbleibende_plots, min_plotting_time_in_min, max_plotting_time_in_min)
        min_rest_time_hours = min_rest_time_min / 60
        max_rest_time_hours = max_rest_time_min / 60
        min_rest_time_days = min_rest_time_hours / 24
        max_rest_time_days = max_rest_time_hours / 24

        # Initialisiere die Tabellen neu fĂĽr diesen Durchlauf
        table_needs_action = Table(show_header=True, header_style="bold magenta")
        table_needs_action.add_column("Verzeichnis", style="dim", width=12)
        table_needs_action.add_column("Free Space(GB)", justify="right")
        table_needs_action.add_column("Plots", justify="right")
        table_needs_action.add_column("Status", justify="left")

        table_fulfilled = Table(show_header=True, header_style="bold cyan")
        table_fulfilled.add_column("Verzeichnis", style="dim", width=12)
        table_fulfilled.add_column("Free Space(GB)", justify="right")
        table_fulfilled.add_column("Plots", justify="right")
        table_fulfilled.add_column("Status", justify="left")

        for directory in directories:
            total_space_gb = get_total_space_gb(directory)
            gesamt_speicherplatz_gb += total_space_gb
            free_space_gb = get_free_space_gb(directory)
            free_space_gib = gb_to_gib(free_space_gb)
            total_plots_possible = int(total_space_gb / ziel_plot_groesse_gib)
            c30_plots_count = count_c30_plots(directory)
            plots_info = f"{c30_plots_count}/{total_plots_possible}"
            free_space_with_plots = f"{free_space_gb:.2f} ({int(free_space_gib / ziel_plot_groesse_gib)})"

            if get_free_space_gb(directory) < min_free_space_gb and can_delete_any_file(directory):
                delete_file(directory, console)

            if free_space_gb < min_free_space_gb and can_delete_any_file(directory):
                table_needs_action.add_row(directory, free_space_with_plots, plots_info, "[bold red]Benötigte Ursprünglich mehr Platz.")
            elif free_space_gb >= min_free_space_gb:
                table_needs_action.add_row(directory, free_space_with_plots, plots_info, "[bold green]GenĂĽgend Speicherplatz")
                table_fulfilled.add_row(directory, f"{free_space_gb:.2f}", plots_info, "[bold cyan]Die Vorgaben sind erfĂĽllt.")

        gesamt_mögliche_plots = int(gesamt_speicherplatz_gb / ziel_plot_groesse_gib)

        # NEUE Tabelle für zusätzliche Informationen
        additional_info_table = Table(show_header=True, header_style="bold blue")
        additional_info_table.add_column("Info", style="dim")
        additional_info_table.add_column("Details", justify="left")

        # HinzufĂĽgen der Informationen zur neuen Tabelle
        additional_info_table.add_row("Gesamtspeicherplatz in GB", f"{format_number(gesamt_speicherplatz_gb).replace(',', '.')} GB")
        additional_info_table.add_row("Gesamtspeicherplatz in TB", f"{format_number(gesamt_speicherplatz_gb / 1024).replace(',', '.')} TB")
        if gesamt_speicherplatz_gb /1024 >= 500:  # Verwende PB nur, wenn der Wert signifikant ist
            additional_info_table.add_row("Gesamtspeicherplatz in PB", f"{format_number((gesamt_speicherplatz_gb/1024)/1024)} PB")
        additional_info_table.add_row("", "")  # Leerzeile
        additional_info_table.add_row(f"Mögliche {plot_name_short}-Plots", format_number(gesamt_mögliche_plots))
        additional_info_table.add_row(f"Vorhandene {plot_name_short}-Plots", format_number(gesamt_vorhandene_plots))
        additional_info_table.add_row("", "")  # Leerzeile
        additional_info_table.add_row("Geschätzte Restlaufzeit in Min", f"{format_number(min_rest_time_min/60).replace(',', '.')} bis {format_number(max_rest_time_min/60).replace(',', '.')}")
        additional_info_table.add_row("Geschätzte Restlaufzeit in Std", f"{str(int(min_rest_time_hours)).replace(',', '.')} bis {str(int(max_rest_time_hours)).replace(',', '.')}")
        additional_info_table.add_row("Geschätzte Restlaufzeit in Tagen", f"{str(int(min_rest_time_days)).replace(',', '.')} bis {str(int(max_rest_time_days)).replace(',', '.')}")

        additional_info_table.add_row("", "")  # Leerzeile

        # Erstelle die ĂĽbergeordnete Tabelle und fĂĽge die untergeordneten Tabellen hinzu
        main_table = Table(box=None)
        main_table.add_column("Aktive Laufwerke", justify="left")
        main_table.add_column("Erfüllte Laufwerke + Zusätzliche Informationen", justify="left")
        fulfilled_plus_additional = Table(box=None, show_header=False)
        main_table.add_row(table_needs_action, fulfilled_plus_additional)


        next_run_time = ( + datetime.timedelta(minutes=wait_time_minutes)).strftime("%d.%m.%Y %H:%M")
        console.print(f"Warte {wait_time_minutes} Minute(n), bevor der nächste Durchlauf beginnt... [white]{next_run_time}[/white]", style="bold blue")
        time.sleep(wait_time_minutes * 60)

if __name__ == "__main__":