#!/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()