#!/usr/bin/python3
# ====================================================================
# file organizer
#
# The definition of "file name" is ambiguous. In common use it can
# have several meanings. In this code a "file name" is a file
# specification made up of three parts. They are:
#
# path directory path to file
# name file name
# type file extension
#
# Note:
# 1. On windows, path includes a volume or dive.
# 2. this code does not copy or move directories (only files).
# 3. this code uses the default file mode (0o777).
# 4. I am not happy with using two modules (os and shutil).
# modify the code to use only one of them if possible.
# ====================================================================
# links:
# datagy.io/python-copy-file/
# docs.python.org/3/library/shutil.html
# docs.python.org/3/library/pathlib.html
# stackoverflow.com/questions/17752078/difference-between-os-path-
# exists-and-os-path-isfile
# ====================================================================
# THIS PROGRAM IS STRUCTURED AS A DEMONSTRATION PROGRAM AND SHOULD
# BE RE-STRUCTURED AS AN EFFICIENT "FILE ORGANIZER" PROGRAM.
# ====================================================================
import os
import sys
import shutil
import user_interface as ui
# -------------------------------------------------------------------
# ---- given a directory,
# ---- return a list of files and directories in it
# -------------------------------------------------------------------
def get_list_of_files_directories(dir):
# ---- dir must end with '/' character
if not dir.endswith('/'):
dir = dir + '/'
# --- get a list of entries in the directory
entries = os.listdir(dir)
# --- collect file names and file types
dirs = []
files = []
links = []
for f in entries:
ff = dir + f
if os.path.islink(ff):
link_count += 1
continue
if os.path.isfile(ff):
files.append(ff)
continue
if os.path.isdir(ff):
dirs.append(ff)
continue
print(f'we should never get here ({ff})')
sys.exit()
dirs.sort()
files.sort()
links.sort()
return (files,dirs,links)
# -------------------------------------------------------------------
# ---- given a list of files (each has up to 3 parts)
# --- split them and return them as a list of tuples
# ----
# ---- path directory path to file
# ---- name file name
# ---- type file extension
# ----
# ---- for example, if file = '/a/b/c/xyz/xyz.txt'
# ---- path = '/a/b/c/'
# ---- name = 'xyz'
# ---- type = '.txt'
# -------------------------------------------------------------------
def split_file_paths(files):
# --- collect file path, file names and file types
ftypes = []
full_path = []
for f in files:
path,file = os.path.split(f)
name,type = os.path.splitext(file)
ftypes.append((path,name,type))
##print(f'path : {path}')
##print(f'file : {file}')
##print(f'name : {name}')
##print(f'type : {type}')
# ---- sorted new list by file type
#new_list = sorted(ftypes,key=lambda tup: tup[2])
# ---- sort in place
ftypes.sort(key=lambda tup:tup[2])
return (ftypes)
# --------------------------------------------------------------------
# ---- delete a directory tree
# ----
# ---- path is a complete path to a tree root directory
# --------------------------------------------------------------------
def delete_directory_tree(path:str) -> bool:
print(f'delete dir: {path}')
if not os.path.exists(path):
print(f'delete dir: directory does not exists')
return True
try:
shutil.rmtree(path)
print(f'delete dir: directory tree deleted')
except Exception as e:
print(e)
print(f'delete dir: unable to delete directory tree')
return False
return True
# --------------------------------------------------------------------
# ---- create a directory tree
# ----
# ---- path is a complete path to the new directory
# ---- return True if at the end a new directory
# ---- mkdir create a directory,
# ---- if the directory already exists, do nothing
# ---- makedirs will create any directories that do no exist
# --------------------------------------------------------------------
def create_directory_tree(path:str) -> bool:
print(f'create dir tree: {path}')
# ---- does the directory exist
# ---- if it exists, do not delete files in it
if os.path.exists(path):
print(f'create dir tree: no existing file deleted')
return True
# create the directory tree if does not exist
try:
os.makedir(path)
print(f'create dir tree: directory tree created')
except Exception as e:
##print(e)
print(f'create dir tree: unable to create directory tree')
return False
return True
# --------------------------------------------------------------------
# ---- create a directory
# ----
# ---- path is a complete path to the new directory
# ----
# ---- mkdir creats a directory if the path to it already exists
# ---- makedirs will create any directories that do no exist
# --------------------------------------------------------------------
def create_directory(path:str) -> bool:
print(f'create dir: {path}')
# ---- does the directory exist
# ---- if it exists, do not delete files in it
if os.path.exists(path):
print(f'create dir: directory already exists')
print('create dir: no files have been deleted')
return True
# create the directory
try:
os.mkdir(path)
print(f'create dir: directory created')
except Exception as e:
##print(e)
print(f'create dir: unable to create directory')
return False
return True
# --------------------------------------------------------------------
# ---- test 1 - delete existing directories, then create directory
# --------------------------------------------------------------------
def test1(path):
print()
print('create and delete directories')
print()
# ---- create a new directory (not a directory tree))
# ---- delete directories first
tf = delete_directory_tree(path)
if not tf:
print('delete directory tree failed')
return False
tf = create_directory(path)
if not tf:
print('create directory failed')
return False
return True
# --------------------------------------------------------------------
# ---- test 2 - files and file types
# --------------------------------------------------------------------
def test2(directory):
print()
print(f'files and file types')
print()
# ---- list of files and directories
files,dirs,links = get_list_of_files_directories(directory)
print(f'directory {directory}')
print(f'number of files {len(files)}')
print(f'number of directories {len(dirs)}')
print(f'number of links {len(links)}')
# --- count the number of each file type
paths = split_file_paths(files)
count_dict = {}
for i,p in enumerate(paths,1):
if p[2] in count_dict:
count_dict[p[2]] += 1 # increment type count
else:
count_dict[p[2]] = 1 # add new type
print()
for k,v in sorted(count_dict.items()):
print(f'{k:8} {v}')
return True
# --------------------------------------------------------------------
# ---- test 3 - copy files
# --------------------------------------------------------------------
def test3(source,destination):
# ---- directories must end with '/' character
if not source.endswith('/'):
source = source + '/'
if not destination.endswith('/'):
destination = destination + '/'
print()
print('copy files')
print(f'source is {source}')
print(f'destination is {destination}')
print()
# ---- do the directories exist
if not os.path.exists(source):
print(f'copy files: source directory does not exist')
return False
if not os.path.exists(destination):
print(f'copy files: destination directory does not exist')
return False
# ---- get a list of files in the source directory
files,dirs,links = get_list_of_files_directories(source)
# ---- copy files to the destination directory
c = 0
for file in files:
c += 1
shutil.copy(file,destination)
print(f'{c} files copied')
print()
return True
# --------------------------------------------------------------------
# ---- test 4 - move files
# --------------------------------------------------------------------
def test4(source,destination):
# ---- directories must end with '/' character
if not source.endswith('/'):
source = source + '/'
if not destination.endswith('/'):
destination = destination + '/'
print()
print('move files')
print(f'source is {source}')
print(f'destination is {destination}')
print()
files,dirs,links = get_list_of_files_directories(source)
print(f'{len(files)} source files found')
# ---- move files to the destination directory
move_c = 0
not_move_c = 0
for file in files:
move_c += 1
if os.path.isfile(file):
shutil.copy(file,destination)
os.remove(file)
else:
not_move_c += 1
print(f'{move_c} files moved')
print(f'{not_move_c} files not moved')
print()
return True
# --------------------------------------------------------------------
# ---- main
# --------------------------------------------------------------------
if __name__ == '__main__':
dir_1 = './zzzz' # source only
dir_2 = './aaaa' # source and destination
dir_3 = './bbbb' # destination only
menu = '''\
+------------------------------------------------+
| Test Functionality Needed for a File Organizer |
+------------------------------------------------+
|WARNING: do not use real files or directories |
| these directories are only for testing |
| |
| dir_1 = './zzzz' # source only |
| dir_2 = './aaaa' # source and destination |
| dir_3 = './bbbb' # destination only |
+------------------------------------------------+
option description
------ ---------------------------------------
0 exit program
1 create directory (delete existing dir)
2 delete directory
3 get list of files and file types
4 copy files
5 move files'''
while True:
print(menu)
print()
s = ui.get_user_input('Select option: ')
if not s: break
tf,i = ui.is_integer(s)
if not tf:
print()
print(' error: non integer entered')
ui.pause()
continue
# --- option 0
if i == 0: break
# --- option 1 create and delete directories
if i == 1:
test1(dir_2)
ui.pause()
continue
# --- option 2 delete directory
if i == 2:
delete_directory_tree(dir_2)
ui.pause()
continue
# --- option 3 files and file types
if i == 3:
test2(dir_1)
ui.pause()
continue
# --- option 4 copy files
if i == 4:
test3(dir_1,dir_2)
ui.pause()
continue
# --- option 5 move files
if i == 5:
test4(dir_2,dir_3)
ui.pause()
continue
# --- selection error
print()
print(f'Error: illegal selection {i}')
ui.pause()