python script to help sort directories containing one text file each

add this script to “actions for nautilus”

import os
import shutil
import tkinter as tk
from tkinter import scrolledtext, messagebox, filedialog
from tkinter import font as tkfont
from tkinter import ttk

class DirectoryMoverApp(tk.Tk):
    def __init__(self, main_dir):
        super().__init__()

        self.main_dir = main_dir
        self.dir_list = sorted(list_subdirectories(main_dir), key=lambda x: x.lower())  # Sort directories alphabetically
        self.destination_dirs = []
        self.moved_dirs = []
        self.last_moved_dir = None
        self.current_index = 0  # Track which subdirectory we're on
        self.moved_counter = 0  # Counter for moved directories

        self.load_destinations()
        self.create_widgets()
        self.display_subdirectories()

    def load_destinations(self):
        script_dir = os.path.dirname(os.path.realpath(__file__))
        dest_file = os.path.join(script_dir, 'destination_dirs.txt')

        try:
            with open(dest_file, 'r') as file:
                self.destination_dirs = sorted(
                    [line.strip() for line in file.readlines()],
                    key=lambda x: os.path.basename(x).lower()
                )
        except FileNotFoundError:
            messagebox.showerror("Error", "Destination directories file not found!")
            self.destination_dirs = []

    def create_widgets(self):
        self.title("Directory Mover")
        self.geometry(f"{int(self.winfo_screenwidth()*1/2)}x{int(self.winfo_screenheight()*2/3)}")
        self.center_window()

        self.subdir_label = tk.Label(self, text="Current Directory: ", font=("Arial", 14))
        self.subdir_label.grid(row=0, column=0, padx=10, pady=5, sticky="w")

        self.text_file_counter_label = tk.Label(self, text="Text File Count: ", font=("Arial", 12))
        self.text_file_counter_label.grid(row=1, column=0, padx=10, pady=5, sticky="w")

        self.file_contents_field = scrolledtext.ScrolledText(self, wrap=tk.WORD, width=80, height=35)
        self.grid_columnconfigure(0, weight=1, uniform="file_contents")
        self.file_contents_field.grid_propagate(False)
        self.file_contents_field.grid(row=2, column=0, rowspan=4, padx=10, pady=10, sticky="nsew")

        self.right_frame = tk.Frame(self)
        self.right_frame.grid(row=2, column=1, padx=10, pady=10, sticky="nsew")

        self.counter_label = tk.Label(self.right_frame, text=f"Moved directories: {self.moved_counter}", font=("Arial", 12))
        self.counter_label.pack(pady=10)

        self.edit_button = tk.Button(self.right_frame, text="Edit Destinations", command=self.edit_destinations, fg="blue")
        self.edit_button.pack(pady=10)

        self.reload_button = tk.Button(self.right_frame, text="Reload Destinations", command=self.reload_destinations, fg="blue")
        self.reload_button.pack(pady=10)

        self.undo_button = tk.Button(self.right_frame, text="Undo last move", command=self.undo_last_move, state=tk.DISABLED, fg="red")
        self.undo_button.pack(pady=10)

        self.separator_label = tk.Label(self.right_frame, text="-------------------------------------", font=("Arial", 12))
        self.separator_label.pack(pady=10)

        self.move_label = tk.Label(self.right_frame, text="Move to:", font=("Arial", 12))
        self.move_label.pack(pady=10)

        self.select_directory_button = tk.Button(
            self.right_frame,
            text="Select Directory",
            fg="green",
            command=self.select_directory_action
        )
        self.select_directory_button.pack(pady=10)

        self.move_frame = tk.Frame(self)
        self.move_frame.grid(row=3, column=1, padx=10, pady=10, sticky="nsew")

        self.move_buttons = []
        self.create_move_buttons()

    def create_move_buttons(self, max_buttons_per_column=9):
        row = 0
        col = 0

        for i, dir_name in enumerate(self.destination_dirs):
            btn = tk.Button(
                self.move_frame,
                text=f"{os.path.basename(dir_name)}",
                fg="green",
                command=lambda d=dir_name: self.move_to_directory(d)
            )
            btn.grid(row=row, column=col, padx=5, pady=5)

            self.move_buttons.append(btn)
            row += 1
            if row >= max_buttons_per_column:
                row = 0
                col += 1

    def select_directory_action(self):
        directory = filedialog.askdirectory(initialdir=self.main_dir, title="Select a Directory")
        if directory and os.path.isdir(directory):
            self.move_to_directory(directory)

    def display_subdirectories(self):
        if self.current_index < len(self.dir_list):
            subdir = self.dir_list[self.current_index]
            self.subdir_label.config(text=f"Current Directory: {subdir}")

            full_path = os.path.join(self.main_dir, subdir)
            content = get_first_txt_md_content(full_path)

            self.file_contents_field.delete(1.0, tk.END)
            self.file_contents_field.insert(tk.END, content)

            check_text_md_files(full_path, self.text_file_counter_label)
        else:
            self.subdir_label.config(text="Current Directory: None")
            self.file_contents_field.delete(1.0, tk.END)
            self.file_contents_field.insert(tk.END, "Sorting Completed!")

    def move_to_directory(self, destination):
        if self.current_index < len(self.dir_list):
            selected_dir_name = self.dir_list[self.current_index]
            selected_dir_path = os.path.join(self.main_dir, selected_dir_name)
            dest_dir_path = os.path.join(destination, selected_dir_name)

            os.makedirs(dest_dir_path, exist_ok=True)
            for root, dirs, files in os.walk(selected_dir_path):
                relative_path = os.path.relpath(root, selected_dir_path)
                target_root = os.path.join(dest_dir_path, relative_path)

                os.makedirs(target_root, exist_ok=True)
                for file in files:
                    src_file = os.path.join(root, file)
                    dest_file = os.path.join(target_root, file)
                    if os.path.exists(dest_file):
                        base, ext = os.path.splitext(file)
                        counter = 1
                        new_dest_file = f"{os.path.join(target_root, base)}_{counter}{ext}"
                        while os.path.exists(new_dest_file):
                            counter += 1
                            new_dest_file = f"{os.path.join(target_root, base)}_{counter}{ext}"
                        dest_file = new_dest_file

                    shutil.move(src_file, dest_file)

            if selected_dir_path != dest_dir_path:
                shutil.rmtree(selected_dir_path)
            self.moved_dirs.append(selected_dir_path)
            self.last_moved_dir = selected_dir_path
            self.moved_counter += 1
            self.counter_label.config(text=f"Moved directories: {self.moved_counter}")
            self.undo_button.config(state=tk.NORMAL)

            self.current_index += 1
            self.display_subdirectories()

    def undo_last_move(self):
        if self.last_moved_dir:
            original_path = os.path.join(self.main_dir, self.last_moved_dir)
            shutil.move(self.last_moved_dir, original_path)
            self.moved_counter -= 1
            self.counter_label.config(text=f"Moved directories: {self.moved_counter}")
            self.undo_button.config(state=tk.DISABLED)

    def edit_destinations(self):
        script_dir = os.path.dirname(os.path.realpath(__file__))
        dest_file = os.path.join(script_dir, 'destination_dirs.txt')
        os.system(f"xdg-open {dest_file}")

    def reload_destinations(self):
        self.load_destinations()
        for btn in self.move_buttons:
            btn.destroy()
        self.move_buttons.clear()
        self.create_move_buttons()

    def center_window(self):
        self.update_idletasks()
        width = self.winfo_width()
        height = self.winfo_height()
        x = (self.winfo_screenwidth() // 2) - (width // 2)
        y = (self.winfo_screenheight() // 2) - (height // 2)
        self.geometry(f'{width}x{height}+{x}+{y}')

def list_subdirectories(main_dir):
    return [d for d in os.listdir(main_dir) if os.path.isdir(os.path.join(main_dir, d))]

def check_text_md_files(directory, text_file_counter_label):
    file_extensions = ('.txt', '.md')
    total_files = sum(1 for file in os.listdir(directory) if file.endswith(file_extensions))
    if total_files > 1:
        text_file_counter_label.config(fg="red")
    else:
        text_file_counter_label.config(fg="black")
    text_file_counter_label.config(text=f"Total .txt/.md files: {total_files}")

def get_first_txt_md_content(subdir):
    for file in os.listdir(subdir):
        if file.endswith('.txt') or file.endswith('.md'):
            with open(os.path.join(subdir, file), 'r') as f:
                return f.read()
    return "No .txt or .md file found."

if __name__ == "__main__":
    import sys
    if len(sys.argv) != 2:
        print("Usage: python3 app.py <directory_path>")
        sys.exit(1)

    main_directory = sys.argv[1]
    if not os.path.isdir(main_directory):
        print(f"Error: {main_directory} is not a valid directory.")
        sys.exit(1)

    app = DirectoryMoverApp(main_directory)
    app.mainloop()