Advertisement
Guest User

Python - nforge file organizer

a guest
Nov 4th, 2016
135
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.75 KB | None | 0 0
  1. #!/usr/bin/python
  2. '''
  3. Python 2.x compliant (change <> to != for Python3)
  4. File organizer by type/time/name
  5.  
  6. Copyright (C) 2013 Ignacio Alvarez <No email for you 4chan@gmail.com>
  7.  
  8. Permission is hereby granted, free of charge, to any person obtaining
  9. a copy of this software and associated documentation files (the "Software"),
  10. to deal in the Software without restriction, including without limitation
  11. the rights to use, copy, modify, merge, publish, distribute, sublicense,
  12. and/or sell copies of the Software, and to permit persons to whom the
  13. Software is furnished to do so, subject to the following conditions:
  14.  
  15. The above copyright notice and this permission notice shall be included
  16. in all copies or substantial portions of the Software.
  17. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  19. OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  20. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  21. DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  22. TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
  23. OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  24. '''
  25.  
  26. import sys, os, time, glob, re
  27. import shutil, filecmp
  28.  
  29. DEBUG_MODE = 0
  30.  
  31. #TODO: Make the arg parsing prettier, (using argparse maybe?).
  32. #      - Add the posibility to save a list of file moved.
  33. #      - Implement sorting by date.
  34. def main(args):
  35.     '''Main program entry point'''
  36.  
  37.     arguments = check_args(args, len(args))
  38.  
  39.     if arguments == "INVALID" or not arguments:
  40.         print(" Invalid arguments...\n  Use \'-h\' for help.")
  41.         return
  42.  
  43.     if arguments == "HELP":
  44.         print_help()
  45.         return
  46.  
  47.     directory = args[3]
  48.     if not directory or not os.path.exists(directory):
  49.         print(" Invalid directory...")
  50.         return
  51.  
  52.  
  53.     if arguments == "VALID":
  54.         filetypes = get_filetypes(args[2])
  55.         classificate_dir(args[1], filetypes, directory)
  56.         return
  57.  
  58.     if arguments == "GET_DATES":
  59.         dates, mode = get_dates(args[1])
  60.         classificate_dir(mode, filetypes, directory, dates)
  61.         return
  62.  
  63.  
  64. def change_dir(directory):
  65.     '''Changes current dir and returns it'''
  66.     if directory <> ".":
  67.         try:
  68.             os.chdir(directory)
  69.  
  70.         except IOError:
  71.             print("Cant access {0}...\n".format(directory))
  72.  
  73.         return os.getcwd()
  74.     else:
  75.         return directory
  76.  
  77.  
  78. def check_for_file(fname):
  79.     '''Checks if it's a valid file'''
  80.  
  81.     return not(os.path.islink(fname) or \
  82.             os.path.isdir(fname) or \
  83.             (fname == sys.argv[0]))
  84.  
  85.  
  86. def fix_repeated(file_a, file_b, directory):
  87.     '''Reacts on existing files, removing or saving with a different name.'''
  88.  
  89.     # If the files are the exact same remove it
  90.     if filecmp.cmp(file_a, file_b):
  91.         os.remove(file_a)
  92.     else:
  93.         # else we save rename and append -diff in route/'filename + diff'
  94.         backup_filename = file_a + "-diff"
  95.         fullroute = directory + backup_filename
  96.  
  97.         print("Saving as: {fullroute}\n".format(fullroute=fullroute))
  98.         os.rename(file_a, backup_filename)
  99.         shutil.move(backup_filename, fullroute)
  100.  
  101.  
  102. def move_file(fname, directory):
  103.     '''Move a file into specified dir, and checks for existing files'''
  104.  
  105.     try:
  106.         if DEBUG_MODE:
  107.             print("Moving \'{origin}\' ----> \'{destiny}\'".format(origin=fname, destiny=directory + fname))
  108.         shutil.move(fname, directory)
  109.  
  110.     except shutil.Error:
  111.         fullpath = directory + fname
  112.         print("{fullpath} already exists, skipping...\n".format(fullpath=fullpath))
  113.         fix_repeated(fname, fullpath, directory)
  114.  
  115.  
  116. def organize_by_symbols(filetypes, directory):
  117.     '''Organize files starting with "(,),-,_[]'''
  118.  
  119.     start_dir = os.getcwd()
  120.  
  121.     if os.path.exists(os.path.relpath(directory)):
  122.         current_dir = change_dir(directory)
  123.  
  124.     files_processed = 0
  125.  
  126.     for filetype in filetypes:
  127.  
  128.         match = ''.join(' [][()$-_=]*' + '.' + filetype)
  129.  
  130.         # For matched file in dir
  131.         for fname in glob.iglob(match):
  132.  
  133.             files_processed += 1
  134.  
  135.             # If folder for current letter doesn't exists
  136.             if not os.path.exists("#misc-symbols" + "/"):
  137.                 os.mkdir("#misc-symbols")
  138.  
  139.             move_file(fname, "#misc-symbols" + "/")
  140.  
  141.     if current_dir <>  start_dir:
  142.         change_dir(start_dir)
  143.  
  144.     return files_processed
  145.  
  146.  
  147. def organize_by_name(filetypes, directory):
  148.     '''Organize by name mode used in (-n and -n+)'''
  149.     start_dir = os.getcwd()
  150.  
  151.     if os.path.exists(os.path.relpath(directory)):
  152.         current_dir = change_dir(directory)
  153.  
  154.     files_processed = 0
  155.  
  156.     # For every chosen filetype
  157.     for filetype in filetypes:
  158.  
  159.         # For every file on dir
  160.         match = ''.join("^[A-Za-z0-9]*" + "." + filetype)
  161.  
  162.         for fname in glob.iglob(match):
  163.             files_processed += 1
  164.  
  165.             # If folder for current letter doesn't exists
  166.             dir_letter = fname[0].upper()
  167.             directory = dir_letter + "/"
  168.  
  169.             if not os.path.exists(directory):
  170.                 os.mkdir(dir_letter)
  171.  
  172.             move_file(fname, directory)
  173.  
  174.     if current_dir <> start_dir:
  175.         change_dir(start_dir)
  176.  
  177.     return files_processed
  178.  
  179.  
  180. #def organize_by_date(mode, filetypes, directory, dates):
  181. #    """Organize by date PENDING"""
  182. #    return 0"""
  183.  
  184.  
  185. def organize_by_filetype(filetypes, directory):
  186.     '''Organize by filetype mode (-f)'''
  187.  
  188.     files_processed = 0
  189.  
  190.     start_dir = os.getcwd()
  191.  
  192.     if os.path.exists(os.path.relpath(directory)):
  193.         current_dir = change_dir(directory)
  194.  
  195.  
  196.     for ftype in filetypes:
  197.         for fname in glob.glob("*." + ftype):
  198.  
  199.             files_processed += 1
  200.  
  201.             #Get filetype from filename
  202.             filetype = fname.partition(".")[2]
  203.  
  204.             # If folder for current file filetype doesn't exists
  205.             dirname = filetype + "/"
  206.  
  207.             if not os.path.exists(dirname):
  208.                 os.mkdir(filetype)
  209.             move_file(fname, dirname)
  210.         #for end
  211.     #for end
  212.     if current_dir <> start_dir:
  213.         change_dir(start_dir)
  214.  
  215.     return files_processed
  216.  
  217. #TODO: Refactor this mess
  218. def classificate_dir(mode, filetypes, directory, dates=0):
  219.     '''Calls the corresponding function to sort files in selected mode'''
  220.  
  221.     files_classified = 0
  222.     init_time = time.ctime()
  223.  
  224.     if "-n" in mode:
  225.         files_classified = organize_by_name(filetypes, directory)
  226.  
  227.         if mode == "-n+":
  228.             files_classified += organize_by_symbols(filetypes, directory)
  229.  
  230.     elif mode == "-tf" or mode == "-te": # time frame and time exact date modes
  231.         #files_classified = organize_by_date(mode, filetypes, directory, dates)
  232.         print("Not implemented yet... Sorry")
  233.         return
  234.  
  235.     elif mode == "-f":
  236.         files_classified = organize_by_filetype(filetypes, directory)
  237.  
  238.     else:
  239.         print("Mode inexistant, exiting....")
  240.         return
  241.  
  242.  
  243.     if files_classified == 0:
  244.         print("No files were organized. Ending...")
  245.  
  246.     else:
  247.         file_str = "files" if(files_classified > 1) else "file"
  248.  
  249.         entries = {
  250.                     'quantity': files_classified, 'files': file_str,
  251.                     'start': init_time, 'end': time.ctime()
  252.                   }
  253.  
  254.         #I really have to improve this
  255.         message = """\n {0} {1} were organized.\n\n\
  256.                Started at:\n\t{2}\n  \
  257.                Ended at:\n\t{3}\n""".format(files_classified, file_str, init_time, time.ctime()) #I wish passing 'entries' would be possible :'(
  258.         print(message)
  259.  
  260.  
  261. def print_help():
  262.     '''Print help string'''
  263.  
  264.     indent = "\t  "
  265.  
  266.     #Improve this calamity xD
  267.     help_string = """nforg.py - A file organizer in python -\n\
  268.            Arguments: nforg.py <mode> <filetypes> <dir>\n\
  269.            Example: nforg.py -n \"pdf,png,txt\" .\n\n\
  270.    Organizing mode:\n\t\t-n: by name\n\t\t-n+: by name (including numbers and symbols)\
  271.            \n\t\t-t: by time\n\t\t-f: by filetype\n\n    Using \"-t\" time mode:\n\
  272.    \n%sLooking for jpg files with the two specified dates, moving them to\n%sfolders with that specified date.\
  273.    \n\n\t\tnforg.py -t \"12/05/2010+5/06/2012\" \"jpg\" .\
  274.    \n\n%sLooking for ugly .doc files in a time frame and moving into\n%sfiles with the file day/month/year.\
  275.    \n\n\t\tnforg.py -t \"12/04/2008 8/06/2009\" \"doc\" .\n\
  276.    """
  277.  
  278.     print(help_string.format(indent, indent, indent, indent))
  279.  
  280.  
  281. def check_args(argv, args):
  282.     '''Checks and validates arguments'''
  283.  
  284.     global DEBUG_MODE
  285.     valid_args = {"-n", "-n+", "-t", "-f", "-h"}
  286.  
  287.     #argv[1] is valid then check for debug mode
  288.     if "d" in argv[1]:
  289.         DEBUG_MODE = 1
  290.         argv[1] = argv[1].replace('d', '')
  291.  
  292.     if args <= 1 or argv[1] not in valid_args:
  293.         return "INVALID"
  294.  
  295.  
  296.     if(args < 4 or argv[1] == "-h"):
  297.         return "HELP"
  298.  
  299.     if(argv[1] == "-t"):
  300.         return "GET_DATES"
  301.  
  302.     return "VALID"
  303.  
  304.  
  305. def get_filetypes(arg):
  306.     '''Parses filetypes from third argument'''
  307.  
  308.     if arg == "*":
  309.         return "*"
  310.  
  311.     else:
  312.         filetypes = arg.strip() #Remove possible whitespace input example: "pdf, png, tar"
  313.         filetypes = {x for x in filetypes.split(",")} #Split "pdf,png,tar" into a set -> {pdf, png, tar}
  314.         return filetypes
  315.  
  316.     return "ERROR"
  317.  
  318.  
  319. def get_dates(line):
  320.     '''Gets date and mode'''
  321.  
  322.     dates = []
  323.  
  324.     if "+" in line:
  325.         dates = line.split("+")
  326.         mode = "-te" #Exact date mode
  327.  
  328.     elif " " in line:
  329.         dates = line.split(" ")
  330.         mode = "-tf" #Time frame date mode
  331.  
  332.     return (dates, mode)
  333.  
  334.  
  335. if __name__ == "__main__":
  336.     main(sys.argv)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement