2
\$\begingroup\$

As a followup to this question, I removed the Repository class and do the copy in the Folder class.

Suppose I have the following image directories under the Pictures directory of a Windows user profile:

C:\Users\Kiska\Pictures\Computers
    - comp-amd-21343.jpg
    - 12345.jpg
C:\Users\Kiska\Pictures\Cars
    - car-random.jpg
    - 54321.jpg

In each of those directories, they're are two images each. Notice that the names aren't reflective of what the images depicts.

My script will take a source directory, rename the images based on the folder name and the order in which it was renamed, and move it to a destination directory.

So the above images would be renamed:

computers_1.jpg
computers_2.jpg

cars_1.jpg
cars_2.jpg

Source.txt:

Computers
Cars

Folder.py:

import os
import shutil

class Folder:

    def __init__(self, directory: str):
        self._check_if_str_parameters_are_empty(parameter=directory, error_message="Directory cannot be empty") 
        self._directory = directory.strip()

    def _check_if_str_parameters_are_empty(self, parameter, error_message):
        if not parameter:
            raise ValueError(error_message)

    @property
    def name(self) -> str:
        return self._directory

    def get_lst_of_files(self) -> list:
        return os.listdir(self._directory)

    def copy_files_with(self, extension: str, to_location: "Folder"):
        if self == to_location:
            raise ValueError("Source and Destination cannot be the same")
        if "." in extension:
            raise ValueError("Extension is incorrect type")

        lst_of_images_in_source = os.listdir(self._directory)
        number_of_images_in_source = len(lst_of_images_in_source)

        print(f"Number of images: {number_of_images_in_source}")

        if number_of_images_in_source:
            number_of_images_in_destination = len(to_location.get_lst_of_files()) + 1

            for number, image in enumerate(lst_of_images_in_source, start=number_of_images_in_destination):
                source_image = os.path.join(self._directory, image)
                destination_image = os.path.join(to_location.name,
                                            self._construct_destination_string(self._get_base_name(str_to_split=self._directory), 
                                            number, extension))
                print(f"{source_image} will be renamed to {destination_image}")
                shutil.move(source_image,  destination_image)
        else:
            print("No images to rename")

    def _get_base_name(self, str_to_split: str) -> str:
        return str_to_split.split("\\")[-1]

    def _construct_destination_string(self, image_name: str, current_number: str, extension: str) -> str:
        return "{0}_{1}.{2}".format(image_name.lower().replace(" ", "_"), current_number, extension)

    def __eq__(self, other):
        if isinstance(other, Folder):
            return (self._directory) == (other._directory)
        return NotImplemented

    def __hash__(self):
        return hash(self._directory)

Main.py:

import os
from Folder import Folder

def main():

    source_txt = "source.txt"
    source_pictures = "{0}\\{1}".format(os.getenv("USERPROFILE"), "Pictures")
    destination_pictures = None
    extension = "jpg"

    try:

        if not destination_pictures:
            raise ValueError("Please provide a valid destination path.")

        if os.path.getsize(source_txt):
            if os.path.exists(source_txt) and os.path.exists(destination_pictures):
                with open(source_txt) as folder_reader:
                    for folder in folder_reader:
                        source_folder = os.path.join(source_pictures, folder)
                        if os.path.exists(source_folder):
                            source = Folder(directory=source_folder)
                            destination_folder = os.path.join(destination_pictures, folder)
                            os.makedirs(destination_folder, exist_ok=True)
                            destination = Folder(directory=destination_folder)
                            source.copy_files_with(extension=extension, to_location=destination)
                        else:
                            print(f"{folder} doesn't exist")
            else:
                print("Source file or Destination drive is missing")
        else:
            print("Source file is empty")

    except(ValueError, OSError) as error:
        print(error)
    finally:
        pass

if __name__ == '__main__':
    main()

I removed all the custom validation because as pointed out in my previous question, it was unnecessary.

Areas of concern:

  • The finally block of the try statement has the pass under it. I handle the exceptions, and use the with statement to read the file, so I don't really know what should go there.

  • I use type hinting, but I should also use docstrings and comments, but I didn't add them to save space.

\$\endgroup\$
  • \$\begingroup\$ Am I understanding your code correctly? Are you basically copying all files from src/{name}/* to dst/{name}_{i}? \$\endgroup\$ – Peilonrayz Jul 14 at 19:03
  • \$\begingroup\$ @Peilonrayz - Not all the files in Pictures, just the ones specified in the text file, it gets renamed and placed in the equivalent destination folder. \$\endgroup\$ – киска Jul 14 at 20:53
2
\$\begingroup\$

I'm in a rush so this is half baked :(

  • It's best if you provide everything, currently your code isn't PEP 8 compliant due to you removing the docstrings. Some people may moan at you for this.
  • You should prefer guard statements over if else statements when possible. You can easily change if os.path.getsize(source_txt) to use a not which would reduce the amount of indentation and reduce the complexity to read.
  • You seem to have half baked error handling and I wouldn't be surprised if you're duplicating errors you can get from open and pathlib.
  • I don't think you need a Folder class. If you use pathlib then everything you need should be fairly simple. I'm probably missing a couple of features but I think what you want is as simple as the below code.
from pathlib import Path


def main(source, src, dst, extensions):
    src = Path(src)
    dst = Path(dst)
    with open(source) as folders:
        for folder_name in folders:
            for i, file in enumerate(
                file
                for file in (src / folder_name).iterdir()
                if file.suffix in extensions
            ):
                shutil.move(file,  dst / f'{folder_name}_{i}{file.suffix}')


if __name__ == '__main__':
    try:
        main(
            "source.txt",
            "{0}\\{1}".format(os.getenv("USERPROFILE"), "Pictures"),
            None,
            (".jpg",),
        )
    except Exception as e:
        print(e)
        raise SystemExit(1) from None
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.