4
\$\begingroup\$

I'm working on creating a batch file-renaming application that renames specifically the way I coded it.

Essentially, these files should be renamed from

  • RootFolder
    • ThisFolder
      • ThisItem
      • [Incorrectly Labeled] ThatItem
    • ThatFolder
      • ThisThatItem
      • ThatOneSillyItem

to

  • RootFolder
    • ThisFolder
      • [ThisFolder] ThisItem
      • [ThisFolder] ThatItem
    • ThatFolder
      • [ThatFolder] ThisThatItem
      • [ThatFolder] ThatOneSillyItem

This is the working code:

""" Renames all files in a given directory """ from os import path, stat, walk, rename from os.path import join, sep, isdir from re import compile, split from time import time import codecs class BatchFileRename: """ Renames all files in a given directory """ def __init__(self, root): self._open_file = None self._prefix = self._label = "" self._root = root self._re = compile('\\[[\\w\\s]*\\]?') self._index = len(self._root.split(path.sep)) self._count = 0 self._size = 0.0 # ####################################### # Public methods # -------------- def batch_rename(self): """ Main class driver. Renames all files in a given directory (set by constructor) """ # Raises an exception if there is no directory if not isdir(self._root): raise NotADirectoryError("self._root is empty") # Opens the output file to document changes with codecs.open('output' + str(time()) + '.txt', 'wb', "utf-8") as self._open_file: # Walk through the root folder for root, dirs, files in walk(self._root): # Sets the label self._set_label(root) # For each label folder, iterate through the files to rename them for name in files: self._add_size(root, name) # Adds the file size to counter new_file = self._rename_file(root, name) # Renames the file self._write("\"{0}\" renamed to \"{1}\"".format(name, new_file)) # Writes change to output file self._count += 1 # Counts # of files self._write() # Documents total files and sizes at the end. self._write("Total files: {0}".format(self._count)) self._write("Total size: {0}".format(self._get_total_size())) # ####################################### # Private methods # -------------- def _add_size(self, root_path, file_name): """ Adds the file size to the counter :param root_path: folder the file is in :param file_name: file name """ the_path = join(root_path, file_name) size = stat(the_path).st_size self._size += size def _get_total_size(self): """ Returns total size (string) :return: Formatted string of total size. """ index = 0 tier = ["B", "KB", "MB", "GB", "TB"] while self._size / 1024 >= 1: self._size /= 1024.0 index += 1 return "{0:.2f} {1}".format(self._size, tier[index]) def _rename_file(self, root_path, file_name): """ Renames the given file :param root_path: Folder the file is in :param file_name: file name :return: New file name. """ # If root_path is the root folder, # just assign root_path to split_names if root_path == self._root: split_names = file_name # Otherwise, # split it based on regex else: split_names = split(pattern=self._re, string=file_name) # Arrange a new file_name and strip the extra whitespaces new_file = self._prefix + ' ' + ''.join(split_names).replace(' ', ' ') new_file = new_file.strip() # Rename the file rename(join(root_path, file_name), join(root_path, new_file)) return new_file def _set_label(self, root_path): """ Sets the label :param root_path: the existing folder """ dir_name = None if self._root == root_path else root_path.split(sep)[self._index] if self._label != dir_name and dir_name is not None: self._write() self._write("*"*40) self._write("Leaving the \"{0}\" folder.".format(self._label)) self._write("Opened \"{0}\" folder...".format(dir_name)) self._write("*"*40) self._write() self._label = dir_name self._prefix = "[{0}]".format(self._label) if "[" not in self._label else self._label def _write(self, message="", new_line=True): """ Writes the message in the output file :param message: The message to write :param new_line: New line is necessary """ self._open_file.write(message + ("\n" if new_line or len(message) > 0 else "")) if __name__ == '__main__': br = BatchFileRename(r"Z:\Private") br.batch_rename() 

If I could get a feedback on this code, that would be awesome!

Thank you!

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

You might break this into two lines:

 self._prefix = self._label = "" 

I propose a slightly longer class docstring, which ends in a period:

"""Renames all files in a given directory by pushing them one level down.""" 

It doesn't change regex behavior in this case, but I'd prefer compile(r'foo') to the compile('foo') in your ctor. Then your double backwhacks become single ones. A + is probably more appropriate than *.

Recommend you delete this redundant comment: "# Raises an exception if there is no directory", as the code is clear.

Kudos on leading underscore for methods not exposed as part of public API, that's helpful to the Gentle Reader.

Here's the one glaring issue I noticed:

while self._size / 1024 = 1: 

Assignment (=) is different from equality test (==) -- I have trouble believing the while does what you intended.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks for the helpful suggestions! I changed my code to reflect most of your suggestions. while self._size / 1024 = 1: was supposed to be while self._size / 1024 >= 1:. I just fixed it in my question... I disagree with the quantifier suggestion because there's a possibility that the files do not have any bracketed word groups in their names, which is why I used the * instead of +. \$\endgroup\$ Commented Aug 9, 2017 at 1:38
  • \$\begingroup\$ Sure, that's cool. I suppose there's an opportunity to flesh out the spec. of what valid inputs look like, or maybe add some unit test inputs which illustrate that. \$\endgroup\$ Commented Aug 9, 2017 at 6:13

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.