A project to automate file organization in Linux using Python. I've always wanted to do this project way before I know how to code. Now that I did, I want to improve it even more.
Used Language: Python.
Compatible System: Linux.
Used Modules: OS Module.
How it works: When you save/move/copy a file to one of the main directories(ex: Downloads, Documents...etc), it will automatically move it to its specified directory. It will use both extension and the 2/3 letter code that I provide at the beginning of the file name. It will run in the background once I open my computer.
The program works only on files only, not directories (I don't usually have them and when I do, I want to do it personally).
Questions:
- How readable my code is?
- Are there any logical errors that I'm not aware of?
- How to improve it?
- Is there a better way to approach the problem?
- Overall, how would you rate it out of 10?
Thank you in advance.
CODE
FileOrganizer.py:
#!/usr/bin/env python
import os
import time
import os.path
from random import randint
from ExtraInfo import types, locations, docs, working_directories
class FileOrganizer:
def __init__(self, directory_path):
self.directory_path = directory_path
def path_maker(self, root, file_name):
"""(str, str) -> str
Returns a string containing the full path of a file,
from root of the file and its name.
>>> path_maker("/home/hama/Downloads", "area.cpp")
"/home/hama/Downloads/area.cpp"
>>> path_maker("/home/hama/Downloads/", "FuzzBuzz.py")
"/home/hama/Downloads/FuzzBuzz.py"
"""
return os.path.join(root, file_name)
def extension_finder(self, path):
"""(str) -> str
Takes in a string of full path of a file. If exists,
returns a string of its extension, else returns False.
>>> extension_finder("/home/hama/Downloads/area.cpp")
".cpp"
>>> extension_finder("/home/hama/Downloads/FuzzBuzz.py")
".py"
"""
if os.path.exists(path):
if os.path.isfile(path):
return os.path.splitext(path)[1]
return False
def category_selector(self, extension):
"""(str) -> str
Takes in a string of an extension of a file. If not False,
returns the category of the extension, else returns False.
Precondition: The extension must be in one of the categories.
>>> category_selector(".cpp")
"programming-files"
>>> category_selector(".mp4")
"video"
"""
if extension != False:
for category in types:
if extension in types[category]:
return category
break
return False
def get_prefix(self, path):
"""(str) -> str
Takes in a string of full path of a file. If it is one of the doc
categories returns the first 3 characters of name of the file, else 2.
Precondition: A prefix of a specific directory should be provided
at the beginning of the name of the file.
>>> get_prefix("/home/hama/Downloads/umaMath-week11.pdf")
"uma"
>>> get_prefix("/home/hama/Downloads/pyFuzzBuzz.py")
"py"
"""
prefix = os.path.basename(path)
if self.category_selector(self.extension_finder(path)) not in docs:
return prefix[:2]
else:
return prefix[:3]
def get_original_name(self, path):
"""(str) -> str
Takes in a string of full path of a file. returns a string of
the original file name without any prefix.
Precondition: A prefix of a specific directory should be provided
at the beginning of the name of the file.
>>> get_original_name("/home/hama/Downloads/umaMath-week11.pdf")
"Math-week11.pdf"
>>> get_original_name("/home/hama/Downloads/pyFuzzBuzz.py")
"FuzzBuzz.py"
"""
file_name = os.path.basename(path)
if self.category_selector(self.extension_finder(path)) not in docs:
return file_name[2:]
else:
return file_name[3:]
def random_name_generator(self, path):
"""(str) -> str
Takes in a string of full path of a file. Generates a random
integer at the end of the name of the file, the returns the new name.
>>> random_name_generator("/home/hama/Downloads/umaMath-week11.pdf")
"Math-week11.pdf"
>>> random_name_generator("/home/hama/Downloads/pyFuzzBuzz.py")
"FuzzBuzz.py"
"""
file_name = os.path.splitext(path)[0]
extension = os.path.splitext(path)[1]
return f"""{file_name}-{randint(1, 250) % randint(1, 250)}{extension}"""
def copy(self, file_source, destination_root):
"""(str, str) -> str
Returns a string containing the full path of the newly moved file,
from a full path of a file and root of the destination.
Note: If a file with the same name already exists, a new name will be generated.
>>> copy("/home/hama/Downloads/area.cpp", "/home/hama/Codes/C++/")
"/home/hama/Codes/C++/area.cpp"
>>> copy("/home/hama/Downloads/FuzzBuzz.py", "/home/hama/Codes/Python/")
"/home/hama/Codes/Python/FuzzBuzz.py"
"""
if not os.path.exists(self.path_maker(destination_root, self.get_original_name(file_source))):
file_name = os.path.basename(file_source)
file_destination = self.path_maker(
destination_root, self.get_original_name(file_source))
os.system(f"cp -pa {file_source} {file_destination}")
return file_destination
else:
file_name = self.random_name_generator(self.path_maker(
destination_root, self.get_original_name(file_source)))
file_destination = self.path_maker(destination_root, file_name)
os.system(f"cp -pa {file_source} {file_destination}")
return file_destination
# Activated on these directories
paths = [FileOrganizer(f"{directory}") for directory in working_directories]
while True:
for path in paths:
# Get the files and directories in the root directory.
for root, directories, files in os.walk(path.directory_path):
root, directories, files = root, directories, files
break
# List the files in the directory
list_of_files = []
for file in files:
list_of_files.append(path.path_maker(root, file))
# Loop through the files and copy each one of them.
proccess = True
for file in list_of_files:
if proccess:
current_file = file
file_category = path.category_selector(
path.extension_finder(current_file))
if file_category in locations:
if locations[file_category].get(path.get_prefix(current_file)) != None:
destination_root = locations[file_category].get(
path.get_prefix(current_file))
# Check if there is a whitespace in the path, cause it cause infinite loop.
if not (" " in current_file):
new_file_destination = path.copy(
current_file, destination_root)
else:
continue
if os.path.exists(new_file_destination):
os.remove(current_file)
# Check if the file is moved and the proccess is done, otherwise wait until it is done.
if not os.path.exists(current_file) and os.path.exists(new_file_destination):
proccess = True
else:
proccess = False
while not proccess:
if not os.path.exists(current_file) and os.path.exists(new_file_destination):
proccess = True
else:
proccess = False
time.sleep(10)
time.sleep(5)
# By: Hama
# Software Engineer to be.
ExtraInfo.py:
#!/usr/bin/env python
types = {
'audio': ['.wpl', '.wma', '.wav', '.ogg', '.mpa', '.mp3', '.mid', '.midi', '.cda', '.aif'],
'database': ['.csv', '.dat', '.db', '.dbf', 'log', '.mdb', '.sav', 'sqlite', '.sql', '.tar', '.xml'],
'fonts': ['.fnt', '.fon', '.otf', '.ttf'],
'image': ['.ai', '.bmp', '.gif', '.ico', '.jpeg', '.jpg', '.png', '.ps', '.psd', '.svg', '.tif', '.tiff'],
'doc-presentation': ['.key', '.odp', '.pps', '.ppt', '.pptx'],
'internet': ['.asp', '.srt', '.aspx', '.cer', '.cfm', '.cgi', '.htm', '.jsp', '.part', '.rss', '.xhtml', '.torrent'],
'programming-files': ['.c', '.class', '.cpp', '.cs', '.h', '.java', '.pl', '.sh', '.swift', '.vb', '.php', '.html', '.css', '.js', '.py'],
'doc-spreadsheet': ['.ods', '.xls', '.xlsm', '.xlsx'],
'video': ['.3g2', '.3gp', '.avi', '.flv', '.h264', '.264', '.m4v', '.mkv', '.mov', '.mp4', '.mpg', '.mpeg', '.rm', '.swf', '.vob', '.wmv'],
'doc-word': ['.doc', '.docx', '.odt', '.rtf', '.tex', '.wpd'],
'doc-pdf': ['.pdf', '.epub', '.mobi'],
'text': ['.txt']
}
locations = {
'audio': {'na': '/home/hama/Music/Others'},
'database': {'na': '/home/hama/Documents/Others/Database'},
'fonts': {'na': '/home/hama/Documents/Others/Fonts'},
'internet': {'na': "/home/hama/Documents/Others/Internet-Related"},
'image': {'my': '/home/hama/Pictures/Myself', 'ot': '/home/hama/Pictures/Others', 'wa': '/home/hama/Pictures/Wallpapers'},
'video': {'my': '/home/hama/Videos/Myself', 'ot': '/home/hama/Videos/Others', 'mv': '/home/hama/Videos/Movies', 'tu': '/home/hama/Videos/Tutorials', 'se': '/home/hama/Videos/Series'},
'programming-files': {'ot': '/home/hama/Codes/Others', 'wb': '/home/hama/Codes/Web', 'cp': '/home/hama/Codes/C++', 'py': '/home/hama/Codes/Python'},
'doc-spreadsheet': {'bop': "/home/hama/Documents/Books/Programming-Books", 'bon': "/home/hama/Documents/Books/Novels", 'boo': "/home/hama/Documents/Books/Others", 'duc': "/home/hama/Documents/Documents/Spreadsheet", 'tmp': "/home/hama/Documents/Temp", 'uot': "/home/hama/Documents/UKH/Undergraduate-I/Other-Documents", 'uma': "/home/hama/Documents/UKH/Undergraduate-I/Semester-II/Engineering-Mathematics-II", 'udl': "/home/hama/Documents/UKH/Undergraduate-I/Semester-II/Introduction-to-Digital-Logic-&-Electronics", 'usp': "/home/hama/Documents/UKH/Undergraduate-I/Semester-II/Structured-Programming", 'uen': "/home/hama/Documents/UKH/Undergraduate-I/Semester-II/English-Composition-II"},
'doc-presentation': {'bop': "/home/hama/Documents/Books/Programming-Books", 'bon': "/home/hama/Documents/Books/Novels", 'boo': "/home/hama/Documents/Books/Others", 'duc': "/home/hama/Documents/Documents/Presentations", 'tmp': "/home/hama/Documents/Temp", 'uot': "/home/hama/Documents/UKH/Undergraduate-I/Other-Documents", 'uma': "/home/hama/Documents/UKH/Undergraduate-I/Semester-II/Engineering-Mathematics-II", 'udl': "/home/hama/Documents/UKH/Undergraduate-I/Semester-II/Introduction-to-Digital-Logic-&-Electronics", 'usp': "/home/hama/Documents/UKH/Undergraduate-I/Semester-II/Structured-Programming", 'uen': "/home/hama/Documents/UKH/Undergraduate-I/Semester-II/English-Composition-II"},
'doc-word': {'bop': "/home/hama/Documents/Books/Programming-Books", 'bon': "/home/hama/Documents/Books/Novels", 'boo': "/home/hama/Documents/Books/Others", 'duc': "/home/hama/Documents/Documents/Word", 'tmp': "/home/hama/Documents/Temp", 'uot': "/home/hama/Documents/UKH/Undergraduate-I/Other-Documents", 'uma': "/home/hama/Documents/UKH/Undergraduate-I/Semester-II/Engineering-Mathematics-II", 'udl': "/home/hama/Documents/UKH/Undergraduate-I/Semester-II/Introduction-to-Digital-Logic-&-Electronics", 'usp': "/home/hama/Documents/UKH/Undergraduate-I/Semester-II/Structured-Programming", 'uen': "/home/hama/Documents/UKH/Undergraduate-I/Semester-II/English-Composition-II"},
'doc-pdf': {'bop': "/home/hama/Documents/Books/Programming-Books", 'bon': "/home/hama/Documents/Books/Novels", 'boo': "/home/hama/Documents/Books/Others", 'duc': "/home/hama/Documents/Documents/PDF", 'tmp': "/home/hama/Documents/Temp", 'uot': "/home/hama/Documents/UKH/Undergraduate-I/Other-Documents", 'uma': "/home/hama/Documents/UKH/Undergraduate-I/Semester-II/Engineering-Mathematics-II", 'udl': "/home/hama/Documents/UKH/Undergraduate-I/Semester-II/Introduction-to-Digital-Logic-&-Electronics", 'usp': "/home/hama/Documents/UKH/Undergraduate-I/Semester-II/Structured-Programming", 'uen': "/home/hama/Documents/UKH/Undergraduate-I/Semester-II/English-Composition-II"},
'text': {'tx': "/home/hama/Documents/Documents/PDF"}
}
docs = ['doc-spreadsheet', 'doc-presentation', 'doc-word', 'doc-pdf']
working_directories = ["/home/hama/Downloads/", "/home/hama/Documents/", "/home/hama/Codes/",
"/home/hama/Desktop/", "/home/hama/Music/", "/home/hama/Pictures/", "/home/hama/Videos/"]
script.sh:
#!/bin/bash
nohup python3 -u /home/hama/Codes/Python/FileAutomationV1.0/FileOrganizer.py &