File Management just got easier
Tags: softwaredevelopment, tutorial, programming, python
Published: 2024-04-18 23:04:11


People ask me, "how are you able to keep the files on your drive so arranged? Like Bro, everything is so findable on the spot." Well, There are two ways to achieve this.

**The first way** is the usual human way. You sit down one faithful evening and just start making folders and begin to drag and drop files in to these folders. I did this once or maybe twice {who knows how many people do this everyday}.

**The second way**, this is the less stressful, less time-consuming process and I will show you how to achieve this in a few moments. We'll be using Python, because I love Python and it's simple to understand {at least 😂}.

## PREREQUISITES

Well, before I started this project, I went out to explore and meet an "friend", and I asked, "Do we need anything before we can create the project?"

It replied me with a list of items to make sure you have before you can successfully execute the code.

- **Python 3.7 or higher installed**
- **A Code Editor/IDE** to help you write the code well and ensure you take note of the indentation and see any errors in spellings. The Editor/IDE that I'd be using is VS Code. Visual Studio Code is a wonderful and popular editor which allows for seamless integration with a variety of programming languages, extensive customization through plugins and themes, and efficient code editing with features like IntelliSense… bla bla bla. What I'm trying to say is that It's good so use it 😏
- **A really fancy naming system**. So I went with the name "Arrange" but it felt too ordinary, and I decided to take it up a notch {😁}. From Arrange, It became "**@rrang3**". Looks cool right? Yeah, thought so at first. If you like the name, you can "borrow" it 🙃 or you know, maybe come up with your own name.

Once you have all these prerequisites set, we can begin.

## SETUP

First of all, I love being arranged even without the python script we're about to write so, It would be a good step to create a new folder to handle all the files of the project. Give the folder the name of your project, which is "**@rrang3**" in my case.

Once you're done with that, you can open that folder in your chosen code editor/IDE and create a new python file with a good name for the file, like "**app.py**", "**index.py**", "**project.py**", or anything as long as you are able to run the project.

I'm naming my project as "**app.py**". Once you've done that, you can move on to the next part of this article.

Also, to check if you have Python installed and the version you are running, you can run `python --version` in your terminal.

## IDEATION and SCRIPTING

**STEP 1**: After the research I carried out, My "Friend" confirmed my hypothesis and told me that I needed three modules to be imported into my code. I was happy, because I was sure about the `os` module, but I didn't think of `shutil`.

- **os**: This module allows us to carry out operations that interact with the operating system, such as listing file directories and creating folders.
- **shutil**: This allows us to carry out the file processes like copying, and moving files.
- **time**: I added this one because I didn't like the fact everything happened so fast, I wanted to have some delay in some parts of the process. {In shorter words, I like suspense in my code 😀}

We begin by adding these three modules to the code. Once you have added these, you can move on to continue writing the script.

```python
import os
import shutil
import time
```

In a script like this, you'd want it to run with a sense of direction right? Like, you'd want to be specific so that you don't go off arranging the wrong folder right 😂

The first thing in the script is to ask for the directory you want to arrange. I handled that by creating a simple function that asks for the directory you want to arrange.

```python
# Checks Work Directory
def check_cwd():
    while True:
        current_cwd = input("Enter Directory: ")
        if os.path.isdir(current_cwd):
            return current_cwd.lower()
        else: print("Invalid Directory. Please enter a valid directory")
```

This is a simple function. A loop is initiated to ensure that the path inputted is true. In the loop, the `input()` function is used to prompt the user to enter the directory they would like to work on and then their answer is saved in a variable, `current_cwd`. An `if...else` block then checks if the path inputted is an existing directory and if this is true, the variable is then returned as the value of this function, else, a message is returned prompting the user to enter a valid directory. {Simple logic right? 😅}

**STEP 2**: The next thing is to list the files in the chosen directory.

```python
# Lists the files in the chosen directory
def list_files(directory):
    try:
        cwd_files = [f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))]
        file_num = len(cwd_files)

        if file_num >= 1:
            return {"cwd_files": cwd_files, "file_num": file_num}
        else:
            print("File number in the chosen directory is too low to continue.")
            return {"cwd_files": [], "file_num": 0}
  
    except OSError as e:
        print(f"Error occured: {e}")
        return {"cwd_files": [], "file_num": 0}
```

I used list comprehension to create a list of files in the given directory `(directory)`.

- `try...except`: This allows us to handle errors ourselves and choose what to do when these errors happen in the code {Makes the script better for both the user and the developer if used properly}.
- `os.listdir(directory)`: This part fetches a list of all items (both files and directories) in the specified directory (`directory`). It's like listing all the contents of a folder.
- `os.path.join(directory, f)`: Here, this function creates the full path to each item in the directory. It concatenates `directory` and `f` to form the complete path to each file in the directory. For example, if `directory = "/path/to/directory"` and `f = "file.txt"`, `os.path.join(directory, f)` will return "/path/to/directory/file.txt".
- `os.path.isfile()`: This part checks if a given path corresponds to a regular file. In our case, it checks if the paths obtained in the step above (`os.path.join(directory, f)`) are files.
- `[f for f in ... if ...]`: This is the list comprehension itself. It iterates over each item (`f`) obtained from `os.listdir(directory)`, applies the condition (`os.path.isfile(os.path.join(directory, f))`), and includes only those items for which the condition is True in the final list (`cwd_files`).
- In short and simpler terms, the comprehension selects only the items from the directory that are regular files (not directories or other types of items) and stores their names in the list `cwd_files`.
- Then, the `file_num` variable stores the number of files from the list(`cwd_files`) by using the `len()` function.
- I then decided to limit the number of files that can be arranged to 1 using an `if` statement which then returns the two variables defined (`cwd_files` and `file_num`) in a dictionary so that their values can be later accessed after the function has been called that is if the number of files in the `cwd_files` variable is greater than or equal to 1.
- If it is not, it prints a message saying the file number in the chosen directory is too low to continue and then it returns a dictionary with which the values of the `cwd_files` variable is an empty list and that of the `file_num` variable is 0.
- Outside of this `if` statement, the except block watches for an error defined as an `OSError`. This error occurs due to possible errors from the `os` operations saved in the `cwd_files` variable. To handle it, we accept it as `e` and then print a message telling the user of what error happened, and then return a dictionary with empty values for the variables defined {Just like I explained earlier in  the last point 😃}

**STEP 3**: Files can be arranged and sorted and so on... but if there are no folders to keep them, to me, not much work has been done. The main aim of this script is to arrange the files and put them into folders based on their file types.
We achieve this by creating another function.

```python
# Static Folders
def create_static_folders(directory):
    try:
        # Creates folders for file grouping
        image_folder = os.path.join(directory, "Images")
        audio_folder = os.path.join(directory, "Audio")
        document_folder = os.path.join(directory, "Docs")
        others_folder = os.path.join(directory, "Others")

        # Checks folder existence
        os.makedirs(image_folder, exist_ok=True)
        os.makedirs(audio_folder, exist_ok=True)
        os.makedirs(document_folder, exist_ok=True)
        os.makedirs(others_folder, exist_ok=True)
    
        return {"image_folder": image_folder, "audio_folder": audio_folder, "document_folder": document_folder, "others": others_folder}
    except OSError as e:
        print(f"Error occured: {e}")
        return {"image_folder": None, "audio_folder": None, "document_folder": None, "others": None}
```

This function (`create_static_folders()`) has one argument (`directory`) which will be passed when the script is being run.
It creates folders for the file grouping process in the chosen directory. Just in case, the others folder is created to keep files which are not recognized.

```python
image_folder = os.path.join(directory, "Images")
audio_folder = os.path.join(directory, "Audio")
document_folder = os.path.join(directory, "Docs")
others_folder = os.path.join(directory, "Others")
```

It then verifies the existence of these folders and ensures they have been created. Once the verification is true, it then returns the variables storing the folders as a dictionary.

```python
# Checks folder existence
os.makedirs(image_folder, exist_ok=True)
os.makedirs(audio_folder, exist_ok=True)
os.makedirs(document_folder, exist_ok=True)
os.makedirs(others_folder, exist_ok=True)

return {"image_folder": image_folder, "audio_folder": audio_folder, "document_folder": document_folder, "others": others_folder}
```

The content of the function is wrapped in a `try...except` block to help in catching and handling errors and as so, if an `OSError` occurs, the script will throw an error message on the screen and return None to as the values of the variables in the dictionary to be returned as the value of the code.

```python
except OSError as e:
    print(f"Error occured: {e}")
    return {"image_folder": None, "audio_folder": None, "document_folder": None, "others": None}
```

In this step 3, we have created the image folder, audio folder, document folder, and an extra folder (others) to handle files that are not recognised, and also made it able to handle errors in cases they occur. {Oh Yes, we're almost at the core of the script. Nice work getting to this point 👍}

**STEP 4**: After the creation of these folders in the `create_static_folders()` function. The next would be to sort and move the files. A function `sort_files()` is created. It would be accepting two arguments (`directory` and `folders`).
A variable is then created to use the `list_files()` function on the directory argument.

I then thought. You know, It would be nice to know files that were Organized and those that were not. So, I created two variables (`organized_files` and `unorganized_files`) and equated them both to zero (0). This would help us keep count.

```python
def sort_files(directory, folders):
    try:
        files = list_files(directory)
        organized_files = 0
        unorganized_files = 0

        for file in files["cwd_files"]:
            file_path = os.path.join(directory, file)
            file_extension = os.path.splitext(file)[-1]
        
            if file_extension.lower() in [".jpg", ".jpeg", ".png", ".gif"]:
                shutil.move(file_path, folders["image_folder"])
                organized_files += 1
            elif file_extension.lower() in [".mp3", ".wav", ".wma"]:
                shutil.move(file_path, folders["audio_folder"])
                organized_files += 1
            elif file_extension.lower() in [".pdf", ".doc", ".docx", ".txt"]:
                shutil.move(file_path, folders["document_folder"])
                organized_files += 1
            else:
                print(f'- Could not sort file: {file} as its type is not recognized.')
                shutil.move(file_path, folders["others"])
            
                unorganized_files += 1
        return {"organized_files": organized_files, "unorganized_files": unorganized_files}

    except Exception as e:
        print(f'Error occured: {e}')
        return {"organized_files": 0, "unorganized_files": 0}
```

Once I created the variables (`files`, `organized_files` and `unorganized_files`), I began a for loop.
It iterates through the `cwd_files` variables we created earlier in the `list_files()` function to identify the files in the chosen directory.

In the for loop, two variables are defined.

1. `file_path` is the variable created to store the complete path of the current file gotten from the combination of the `directory` path and the filename from `files["cwd_files"]` using `os.path.join(directory, file)`.
2. The file extension from the file is extracted using `os.path.splitext(file_path)[-1]` in the code. The `os.path.splitext()` function splits the filename into its base name and extension, and the [-1] index selects the extension from the resulting tuple. This selected extension is then saved in a variable, `file_extension`.

In the for loop, after the `file_path` and `file_extension` have been defined, an `if ... elif` block is used to check the file extension and move the file to the corresponding folder. For instance, if the extension is `.jpg` or `.jpeg`, the file is moved to the `image_folder` using the `shutil.move()` function. Similar logic applies for other extensions like `.mp3`, `.wav`, `.pdf`, and `.doc`.

Once the `shutil.move()` function runs and moves a file into the image, audio or document folder, the `organized_files` variable is upgraded with one. Same logic in the `else` block.
The `unorganized_files` variable is upgraded with one for every time a file is moved into the `others` folder.
Once the loop is exhausted, the function returns a dictionary with the organized files and the unorganized files.

In cases where an error occurs, a dictionary with 0 as values is returned and an error is thrown {Been a common logic through out this whole process. I hope you do get it 😃}

---

At this point, we have successfully written the logic for the script. You can start warming up your dancing feet because you just wrote your own code to help you arrange your files. Amazing right? If you got to this point, high five to you 🖐👌. \

If you got confused along the way, don't worry. You can go to the end of this article and a link to a well commented version of the code on my Github account will be there. You can open the file and go through it to see where you had problems. Don't forget to give the repository a star.

---

Alright. Back to the script. We've defined all the functions. Now we initiate them.

To do this, we create a `main()` function which will run all the other functions.

```python
def main():
    try:
        cwd = check_cwd()
        print(f"Chosen Work Directory: \"{cwd}\"")
        print("----------------------------------------------")
    
        files = list_files(cwd)
        print(f"Number of files:", files["file_num"])
        time.sleep(1)
        print("----------------------------------------------")

        print("Process Initiating...")
        time.sleep(2)

        folders = create_static_folders(cwd)
        if None in folders.values():
            print("Error creating folders. Exiting.")
            return
        print("----------------------------------------------")

        sort_function = sort_files(cwd, folders)
        print("----------------------------------------------")

        time.sleep(2)
        print("Total Number of Organized Files:", sort_function["organized_files"])
        print("Total Number of Unorganized Files:", sort_function["unorganized_files"])
        print("----------------------------------------------")

    except Exception as e:
        print(f"An unexpected error occured: {e}")

    except KeyboardInterrupt:
        print(f"\nProcess interrupted by user.")
```

In the function we save the value of the `check_cwd()` function in a variable (`cwd`).

I then added a print statement to show the user the directory which has been entered.

The same logic was applied to the other variables present (`files`, `folders` and `sort_function`). These variables hold the value of the functions created earlier. Once this has been done, the values can then be used and manipulated without having to call any of the function all over again.

Then I used the `time.sleep()` function to add some delay to the script {It makes it look like some progress is actually happening 😉}, and also some separators to separate the "progress" texts in the scripts. All these are additional "candies" I add to make my programs "sweet" and enjoyable.
You could add them too, or choose not to. A personal choice up to you.

Once the `folders` variable is created and the function is declared in it, an `if` statement is brought up to check if `None` is a value in the dictionary being returned by the `create_static_folders(cwd)` function. Once it finds that `None` is present, a message is printed of an error which has occured which brings an end to the script process.

Well. Once you're done with this function, you can call it and test it. {Don't forget to call the function at the end of the code unless it won't run}

You call the function by doing this:

```python
if __name__ == "__main__":      
    main()
```

That should initiate the process. I would love to hear how everything went in the comments section and be careful. With great power, comes great responsibility, so right now, you should be careful of the directory you input. {I am not the cause of a wrong folder being arranged 😂}.

Anyways, that's all for this one. A round of applause to My "Friend", starred by the Internet🌐 AKA www.google.com {yo yo ✌🎉}. Here's one to improved internet access and more resources {Mostly you, AI}.

What have we learnt today, There are two ways to arrange your files. You just learnt the simple, fast, and easy way to do it. If you have questions, the comments section is wide open. So ask, and answers could come {as long as they have answers 😂}.

I've said too much. Bye for now. Catch you in the next one.

Github Link: [Link to GitHub Repository](https://github.com/Sammy949/Arrange)