Ever stared at a messy Downloads folder and wished it’d sort itself? I did—and then I found a Python tool to fix it. As a Python developer who spends half my life wrangling data, I’m all about automation. So when I stumbled across downloads-folder-automation on GitHub, a neat little script that organizes your Windows Downloads folder, I knew I’d found a gem. Better yet? It was open source, and I could make it my own. downloads-folder-automation downloads-folder-automation On my job, I mostly sling Pandas and NumPy for data analysis, with some JavaScript on the side. This project spoke to me: clean code, a real-world problem, and a chance to contribute. My mission? Add support for rtf, odt, and ods files—formats I bump into often, especially the OpenDocument ones from my data-crunching days. Here’s how I jumped into open source, made my mark, and turned a messy folder into a tidy win. rtf odt ods The Project: A Python-Powered Cleanup Crew First, let’s break down what downloads-folder-automation does. It’s a Python script that scans your Downloads folder, reads a config.json file to map file extensions to subfolders (like “Documents” or “Pictures”), and moves everything where it belongs. Think .pdf files sliding into “Documents,” .jpg pics landing in “Pictures,” and anything weird getting dumped in “Miscellaneous.” It’s simple, elegant, and saves you from the dread of manual sorting. downloads-folder-automation The config file is the brains of the operation. Here’s a peek at the version I worked with, including my additions: [ { "name": "Documents", "extensions": [ ".doc", ".docx", ".txt", ".pdf", ".xls", ".ppt", ".xlsx", ".pptx", ".rtf", ".odt", ".ods" ] }, { "name": "Pictures", "extensions": [ ".jpg", ".jpeg", ".png", ".svg", ".gif", ".tif", ".tiff" ] }, // ... other categories like "Programs," "Music," etc. ] [ { "name": "Documents", "extensions": [ ".doc", ".docx", ".txt", ".pdf", ".xls", ".ppt", ".xlsx", ".pptx", ".rtf", ".odt", ".ods" ] }, { "name": "Pictures", "extensions": [ ".jpg", ".jpeg", ".png", ".svg", ".gif", ".tif", ".tiff" ] }, // ... other categories like "Programs," "Music," etc. ] The script itself uses Python’s pathlib for path handling, shutil for moving files, and json to parse that config. It loops through files, checks their extensions, and shuffles them to the right spot. If a folder doesn’t exist (say, “Documents”), it creates it. pathlib shutil Adding More File Types So why tweak it? As a data analyst, I deal with .ods (OpenDocument Spreadsheet) files all the time—think LibreOffice’s answer to Excel. And .odt (OpenDocument Text) and .rtf (Rich Text Format) pop up in my world too. The original config covered a lot—.pdf, .mp3, .zip—but missed these. I figured, why not make it more inclusive? ods .odt .rtf .pdf .mp3 .zip Here’s how it went down: Fork and Clone: I forked eric-mahasi/downloads-folder-automation to my GitHub, cloned it locally, and opened it up. Git’s still a bit of a wild beast to me, but I managed! Edit the Config: In config.json, I added .rtf, .odt, and .ods to the “Documents” category. Logical spot—spreadsheets could’ve gone elsewhere, but I kept it simple. Test It: I tossed some test files into my Downloads folder—an .odt note, an .ods dataset, a random .rtf. Ran the script (python main.py), and bam—they zipped into a fresh “Documents” subfolder. Pull Request: I branched my changes (git checkout -b add-more-extensions), committed them (git commit -m "Add rtf, odt, ods to config"), pushed to my fork, and opened a PR. Described it like: “Added support for .rtf, .odt, .ods—tested and working!” Now I wait for Eric’s feedback. Fork and Clone: I forked eric-mahasi/downloads-folder-automation to my GitHub, cloned it locally, and opened it up. Git’s still a bit of a wild beast to me, but I managed! Fork and Clone: I forked eric-mahasi/downloads-folder-automation to my GitHub, cloned it locally, and opened it up. Git’s still a bit of a wild beast to me, but I managed! Fork and Clone eric-mahasi/downloads-folder-automation Edit the Config: In config.json, I added .rtf, .odt, and .ods to the “Documents” category. Logical spot—spreadsheets could’ve gone elsewhere, but I kept it simple. Edit the Config: In config.json, I added .rtf, .odt, and .ods to the “Documents” category. Logical spot—spreadsheets could’ve gone elsewhere, but I kept it simple. Edit the Config config.json .rtf .odt .ods Test It: I tossed some test files into my Downloads folder—an .odt note, an .ods dataset, a random .rtf. Ran the script (python main.py), and bam—they zipped into a fresh “Documents” subfolder. Test It: I tossed some test files into my Downloads folder—an .odt note, an .ods dataset, a random .rtf. Ran the script (python main.py), and bam—they zipped into a fresh “Documents” subfolder. Test It .odt .ods .rtf python main.py Pull Request: I branched my changes (git checkout -b add-more-extensions), committed them (git commit -m "Add rtf, odt, ods to config"), pushed to my fork, and opened a PR. Described it like: “Added support for .rtf, .odt, .ods—tested and working!” Now I wait for Eric’s feedback. Pull Request: I branched my changes (git checkout -b add-more-extensions), committed them (git commit -m "Add rtf, odt, ods to config"), pushed to my fork, and opened a PR. Described it like: “Added support for .rtf, .odt, .ods—tested and working!” Now I wait for Eric’s feedback. Pull Request: git checkout -b add-more-extensions git commit -m "Add rtf, odt, ods to config To give my spin on the code—and avoid just parroting the original—here’s how I’d write it today: import shutil from pathlib import Path import json def relocate_file(source_file, target_dir): """Moves a file to a specified folder, creating the folder if it doesn’t exist. Args: source_file (Path): Path to the file being moved target_dir (Path): Path to the destination directory """ try: if not target_dir.exists(): target_dir.mkdir(parents=True, exist_ok=True) shutil.move(source_file, target_dir) except shutil.Error as error: print(f"Error moving file: {error}") def organize_directory(directory_path): """Sorts files in a directory into subfolders based on their extensions. Args: directory_path (Path): Path to the directory to organize """ with open('config.json', encoding='utf-8') as config_file: file_groups = json.load(config_file) extension_to_folder = {} for group in file_groups: folder = group['name'] for ext in group['extensions']: extension_to_folder[ext] = folder for item in directory_path.iterdir(): if item.is_file() and not item.name.startswith('.'): target_folder = extension_to_folder.get(item.suffix, 'Miscellaneous') relocate_file(item, directory_path / target_folder) if __name__ == '__main__': user_home = Path.home() downloads_dir = user_home / 'Downloads' organize_directory(downloads_dir) import shutil from pathlib import Path import json def relocate_file(source_file, target_dir): """Moves a file to a specified folder, creating the folder if it doesn’t exist. Args: source_file (Path): Path to the file being moved target_dir (Path): Path to the destination directory """ try: if not target_dir.exists(): target_dir.mkdir(parents=True, exist_ok=True) shutil.move(source_file, target_dir) except shutil.Error as error: print(f"Error moving file: {error}") def organize_directory(directory_path): """Sorts files in a directory into subfolders based on their extensions. Args: directory_path (Path): Path to the directory to organize """ with open('config.json', encoding='utf-8') as config_file: file_groups = json.load(config_file) extension_to_folder = {} for group in file_groups: folder = group['name'] for ext in group['extensions']: extension_to_folder[ext] = folder for item in directory_path.iterdir(): if item.is_file() and not item.name.startswith('.'): target_folder = extension_to_folder.get(item.suffix, 'Miscellaneous') relocate_file(item, directory_path / target_folder) if __name__ == '__main__': user_home = Path.home() downloads_dir = user_home / 'Downloads' organize_directory(downloads_dir) Try It Yourself Want to tame your own Downloads folder or any folder? Fork the repo, grab my updated config.json, and run the script. Add your own file types—maybe .py for “Scripts” or .csv for “Data”? It’s yours to hack. And if you’re new to open source, this is a perfect sandbox—simple, useful, and welcoming. config.json .py .csv