From 5f3ce38fe5f2d1be1534ebfc74aa884ea8254451 Mon Sep 17 00:00:00 2001 From: PragatiBasnet29 Date: Sun, 20 Oct 2024 19:01:04 +0545 Subject: [PATCH] Added duplicate file remover and updated Dockerfile --- Dockerfile | 23 ++++--- organise_desktop/Clean.py | 76 +++++++++++++-------- organise_desktop/cronCleanUp.py | 7 +- organise_desktop/cronController.py | 51 +++++++------- organise_desktop/duplicateCleaner.py | 62 +++++++++++++++++ virtual_desktop/{ => Folders}/OrgDesk.zip | Bin virtual_desktop/{ => Images}/OrgDesk.jpg | Bin virtual_desktop/{ => Music}/OrgDesk.mp3 | Bin virtual_desktop/{ => Text}/OrgDesk.doc | Bin virtual_desktop/{ => Text}/OrgDesk.docx | Bin virtual_desktop/{ => Text}/OrgDesk.pdf | Bin virtual_desktop/{ => Text}/OrgDesk.xls | Bin virtual_desktop/{ => Text}/OrgDesk.xlsx | Bin virtual_desktop/{ => Text}/random_text.txt | 0 14 files changed, 155 insertions(+), 64 deletions(-) create mode 100644 organise_desktop/duplicateCleaner.py rename virtual_desktop/{ => Folders}/OrgDesk.zip (100%) rename virtual_desktop/{ => Images}/OrgDesk.jpg (100%) rename virtual_desktop/{ => Music}/OrgDesk.mp3 (100%) rename virtual_desktop/{ => Text}/OrgDesk.doc (100%) rename virtual_desktop/{ => Text}/OrgDesk.docx (100%) rename virtual_desktop/{ => Text}/OrgDesk.pdf (100%) rename virtual_desktop/{ => Text}/OrgDesk.xls (100%) rename virtual_desktop/{ => Text}/OrgDesk.xlsx (100%) rename virtual_desktop/{ => Text}/random_text.txt (100%) diff --git a/Dockerfile b/Dockerfile index aedf3cd..ad53e2d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,19 +4,22 @@ FROM python:3 # Set the working directory to /code WORKDIR /code -# Copy the current directory content into the container at /code -ADD . /code +# Copy the Pipfile and Pipfile.lock first to leverage Docker cache +COPY Pipfile Pipfile.lock /code/ -# Udate and install all in the container -RUN apt-get update && \ - apt-get install -y - -# Install pipenv and install all dependency +# Install pipenv and dependencies RUN pip install --upgrade pip RUN pip install pipenv -COPY ./Pipfile /code/Pipfile + +# Install the dependencies in the system (not in a virtualenv, since you're using --system) RUN pipenv install --deploy --system --skip-lock --dev -# Set enviroment variables +# Copy the rest of the application code +COPY . /code + +# Set environment variables ENV PYTHONDONTWRITEBYTECODE 1 -ENV PYTHONUNBUFFERED 1 \ No newline at end of file +ENV PYTHONUNBUFFERED 1 + +# Run your application (replace 'your_app.py' with your entry point file) +CMD ["python", "."] diff --git a/organise_desktop/Clean.py b/organise_desktop/Clean.py index f5dcdee..14b94da 100644 --- a/organise_desktop/Clean.py +++ b/organise_desktop/Clean.py @@ -1,9 +1,11 @@ import sys import json import os -from .cronController import schedule_end, schedule_start +from .cronController import schedule_end, schedule_start from .organiseDesktop import undo, organise_desktop +from .duplicateCleaner import clean_duplicates # Importing the duplicate cleaner +# Import Tkinter based on Python version if sys.version_info >= (3,): from tkinter import * from tkinter import messagebox as tkMessageBox @@ -11,77 +13,95 @@ from tkinter import * import tkMessageBox +# Get the directory of the current file pwd = os.path.dirname(os.path.abspath(__file__)) -Extensions = json.load(open(pwd+'/Extension.json', 'r')) -folders = [x for x in Extensions] +# Load extensions from the JSON file +with open(os.path.join(pwd, 'Extension.json'), 'r') as ext_file: + Extensions = json.load(ext_file) + +folders = list(Extensions.keys()) class App(Frame): - """define the GUI""" + """Define the GUI for the desktop cleaner.""" + def clean(self): - checked_extensions = {} - for x in folders: - checked_extensions[x] = Extensions[x] + """Clean the desktop by organizing files with checked extensions.""" + checked_extensions = {x: Extensions[x] for x in folders} organise_desktop(checked_extensions) tkMessageBox.showinfo('Complete', 'Desktop clean finished.') + def clean_duplicates_gui(self): + """Clean duplicate files by using the duplicate cleaner.""" + directory = tkMessageBox.askdirectory(title="Select Directory to Clean Duplicates") + if directory: + clean_duplicates(directory) + tkMessageBox.showinfo('Complete', 'Duplicate file cleanup finished.') + def quit_all(self): + """Exit the application.""" sys.exit(0) def check(self, item): - global folders + """Toggle the selection of a file extension.""" if item in folders: folders.remove(item) else: folders.append(item) def on_schedule_start(self): + """Start the scheduling process for organizing the desktop.""" schedule_start(folders) def make_checkbutton(self, text): + """Create a checkbutton for a file extension.""" cb = Checkbutton(self, text=text, command=lambda: self.check(text)) - cb.select() - cb.pack({'side': 'top'}) + cb.select() # Select the checkbutton by default + cb.pack(side='top') return cb def make_button(self, text, command): + """Create a button with the specified command.""" btn = Button(self, text=text, command=command) - btn.pack({'side': 'left'}) + btn.pack(side='left') return btn def create(self): + """Create the GUI layout.""" self.winfo_toplevel().title('Desktop Cleaner') + # Create checkbuttons for each file extension for ext in sorted(Extensions.keys()): self.make_checkbutton(ext) - # buttons and their respective functions - buttons = {'Clean': self.clean, - 'Exit': self.quit_all, - 'Undo': undo, - 'Schedule': self.on_schedule_start, - 'Remove\nSchedule': schedule_end - } + # Create buttons and their respective functions + buttons = { + 'Clean': self.clean, + 'Exit': self.quit_all, + 'Undo': undo, + 'Schedule': self.on_schedule_start, + 'Remove\nSchedule': schedule_end, + 'Clean\nDuplicates': self.clean_duplicates_gui # Added button for cleaning duplicates + } for key in buttons: self.make_button(key, buttons[key]) def __init__(self, master=None): + """Initialize the application.""" Frame.__init__(self, master) self.pack() self.create() def main(): + """Main function to run the application.""" root = Tk() - # root.resizable = False # commenting this approach and applying the below one. - root.resizable(FALSE,FALSE) # To make the application's size constant and restore button in windows as greyed out(with width=350 and height=330 as mentioned below) + root.resizable(FALSE, FALSE) # Make the application's size constant root.minsize(width=350, height=330) - root.maxsize(width=350, height=330) - - '''Logic to launch the app in center - start''' - positionRight = int(root.winfo_screenwidth() / 2 - 330 / 2) #considering width=330 - positionDown = int(root.winfo_screenheight() / 2 - 350 / 2) #considering height=350 - root.geometry("+{}+{}".format(positionRight, positionDown)) - '''Logic to launch the app in center - end''' + + # Center the application window on the screen + positionRight = int(root.winfo_screenwidth() / 2 - 350 / 2) + positionDown = int(root.winfo_screenheight() / 2 - 330 / 2) + root.geometry(f"+{positionRight}+{positionDown}") app = App(root) root.protocol('WM_DELETE_WINDOW', app.quit_all) @@ -89,4 +109,4 @@ def main(): root.destroy() if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/organise_desktop/cronCleanUp.py b/organise_desktop/cronCleanUp.py index 0ca3887..315e40d 100644 --- a/organise_desktop/cronCleanUp.py +++ b/organise_desktop/cronCleanUp.py @@ -3,12 +3,17 @@ import os import sys +# Determine the file path separator based on the operating system separator = "" if sys.platform == 'win32': separator = '\\' else: separator = '/' -with open(os.path.dirname(os.path.join(os.path.abspath(__file__), 'settings.txt'), 'rb') as setting_file: + +# Correctly open the settings file using the right file path +settings_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'settings.txt') +with open(settings_file_path, 'rb') as setting_file: folders = pickle.load(setting_file) +# Call the main function from the Clean module with the loaded folders Clean.main(folders) diff --git a/organise_desktop/cronController.py b/organise_desktop/cronController.py index 488839e..fb05ad8 100644 --- a/organise_desktop/cronController.py +++ b/organise_desktop/cronController.py @@ -1,54 +1,55 @@ __author__ = "Remigius Kalimba" -"""Add a timer so it does this automatically everyday at a set time""" +"""Add a timer so it does this automatically every day at a set time""" import pickle -import crontab as CronTab -import json import os import sys -from subprocess import call - import getpass - +from subprocess import call +from crontab import CronTab pwd = os.path.dirname(os.path.abspath(__file__)) def schedule_start(folders): + """Starts a schedule to organize the desktop once a day""" - """Starts a schedule to organise the desktop once a day""" - + # Save the folder settings with open('./settings.txt', 'wb') as setting_file: pickle.dump(folders, setting_file) - - if sys.platform == 'darwin' or sys.platform == 'linux': + + # Schedule the job based on the operating system + if sys.platform in ['darwin', 'linux']: my_cron = CronTab(user=getpass.getuser()) - job = my_cron.new(command=str(sys.executable + ' ' + pwd + '/cronCleanUp.py'), - comment='OrganiseDesktop') + # Create a new cron job + job = my_cron.new(command=f"{sys.executable} {pwd}/cronCleanUp.py", comment='OrganiseDesktop') - job.day.every(1) + # Set the job to run every day at a specific time (e.g., 2:00 AM) + job.setall("0 2 * * *") # This sets the time to 2:00 AM every day my_cron.write() - else: - if not os.path.isfile(pwd + '\\cronCleanUp.pyw'): - call('copy' + pwd + '\\cronCleanUp.py' + pwd + '\\cronCleanUp.pyw', shell=True) - - call('SCHTASKS /Create /SC DAILY /TN OrganiseDesktop /TR' + pwd + '\\cronCleanUp.pyw /F', + else: # For Windows + # Copy the script if it doesn't exist + if not os.path.isfile(os.path.join(pwd, 'cronCleanUp.pyw')): + call(f'copy {os.path.join(pwd, "cronCleanUp.py")} {os.path.join(pwd, "cronCleanUp.pyw")}', shell=True) + + # Create a new scheduled task + call(f'SCHTASKS /Create /SC DAILY /TN OrganiseDesktop /TR "{os.path.join(pwd, "cronCleanUp.pyw")}" /F', shell=True) def schedule_end(): - """Removes the schedule if one is defined""" - os.remove('./settings.txt') + # Remove the settings file + if os.path.isfile('./settings.txt'): + os.remove('./settings.txt') - if sys.platform == 'darwin' or sys.platform == 'linux': + # Remove the scheduled job based on the operating system + if sys.platform in ['darwin', 'linux']: my_cron = CronTab(user=getpass.getuser()) my_cron.remove_all(comment='OrganiseDesktop') my_cron.write() - - else: - call('SCHTASKS /Delete /TN OrganiseDesktop /F', - shell=True) + else: # For Windows + call('SCHTASKS /Delete /TN OrganiseDesktop /F', shell=True) diff --git a/organise_desktop/duplicateCleaner.py b/organise_desktop/duplicateCleaner.py new file mode 100644 index 0000000..094d17d --- /dev/null +++ b/organise_desktop/duplicateCleaner.py @@ -0,0 +1,62 @@ +import os +import hashlib + +def calculate_file_hash(file_path, block_size=65536): + """ + Calculate and return MD5 hash of a file for duplicate detection. + """ + hash_md5 = hashlib.md5() + with open(file_path, "rb") as f: + for block in iter(lambda: f.read(block_size), b""): + hash_md5.update(block) + return hash_md5.hexdigest() + +def find_duplicate_files(directory): + """ + Find and return a dictionary of duplicate files in the directory. + """ + files_hash_map = {} + for root, _, files in os.walk(directory): + for file in files: + file_path = os.path.join(root, file) + file_hash = calculate_file_hash(file_path) + + if file_hash in files_hash_map: + files_hash_map[file_hash].append(file_path) + else: + files_hash_map[file_hash] = [file_path] + + duplicates = {hash: paths for hash, paths in files_hash_map.items() if len(paths) > 1} + return duplicates + +def handle_duplicates(duplicates): + """ + Display duplicate files and ask the user whether to delete them. + """ + if not duplicates: + print("No duplicates found.") + return + + print("Duplicate files found:") + for hash_value, file_paths in duplicates.items(): + print(f"\nHash: {hash_value}") + for i, file_path in enumerate(file_paths, 1): + print(f"{i}. {file_path}") + + delete = input(f"\nDo you want to delete duplicates for hash {hash_value}? (yes/no): ").strip().lower() + if delete == 'yes': + for file_path in file_paths[1:]: + try: + print(f"Deleting {file_path}...") + os.remove(file_path) + except Exception as e: + print(f"Error deleting {file_path}: {e}") + + print("Duplicate file cleanup complete.") + +def clean_duplicates(directory): + """ + Main function to clean duplicates in the given directory. + """ + duplicates = find_duplicate_files(directory) + handle_duplicates(duplicates) diff --git a/virtual_desktop/OrgDesk.zip b/virtual_desktop/Folders/OrgDesk.zip similarity index 100% rename from virtual_desktop/OrgDesk.zip rename to virtual_desktop/Folders/OrgDesk.zip diff --git a/virtual_desktop/OrgDesk.jpg b/virtual_desktop/Images/OrgDesk.jpg similarity index 100% rename from virtual_desktop/OrgDesk.jpg rename to virtual_desktop/Images/OrgDesk.jpg diff --git a/virtual_desktop/OrgDesk.mp3 b/virtual_desktop/Music/OrgDesk.mp3 similarity index 100% rename from virtual_desktop/OrgDesk.mp3 rename to virtual_desktop/Music/OrgDesk.mp3 diff --git a/virtual_desktop/OrgDesk.doc b/virtual_desktop/Text/OrgDesk.doc similarity index 100% rename from virtual_desktop/OrgDesk.doc rename to virtual_desktop/Text/OrgDesk.doc diff --git a/virtual_desktop/OrgDesk.docx b/virtual_desktop/Text/OrgDesk.docx similarity index 100% rename from virtual_desktop/OrgDesk.docx rename to virtual_desktop/Text/OrgDesk.docx diff --git a/virtual_desktop/OrgDesk.pdf b/virtual_desktop/Text/OrgDesk.pdf similarity index 100% rename from virtual_desktop/OrgDesk.pdf rename to virtual_desktop/Text/OrgDesk.pdf diff --git a/virtual_desktop/OrgDesk.xls b/virtual_desktop/Text/OrgDesk.xls similarity index 100% rename from virtual_desktop/OrgDesk.xls rename to virtual_desktop/Text/OrgDesk.xls diff --git a/virtual_desktop/OrgDesk.xlsx b/virtual_desktop/Text/OrgDesk.xlsx similarity index 100% rename from virtual_desktop/OrgDesk.xlsx rename to virtual_desktop/Text/OrgDesk.xlsx diff --git a/virtual_desktop/random_text.txt b/virtual_desktop/Text/random_text.txt similarity index 100% rename from virtual_desktop/random_text.txt rename to virtual_desktop/Text/random_text.txt