Build a Tag-Based File Organizer
Automate file routing to Google Drive folders using Python and hashtag tags.
Introduction
30 Second Summary
Every computer has that one folder where files go to get lost. Downloads, notes, and reports pile up until finding anything means scrolling through chaos.
In this project, you will build a Python script that watches a local folder and automatically sorts files into the right Google Drive folder based on hashtags in their names.
What You'll Build
Drop a file named budget #finance.txt into your watched folder, and within seconds it appears in a "finance" folder in your Google Drive with a clean name.
By the end of this project, you'll have:
- A real-time file watcher that detects new files the instant they land in your inbox folder and processes them automatically.
- Automatic tag parsing that reads hashtags from filenames, routes each file to the matching Google Drive folder, and strips the tag so the file arrives with a clean name.
- A live OAuth 2.0 authentication flow with Google Drive, so your script can create folders and upload files on your behalf.
- Secret Mission: Handle multiple tags on a single file so it gets uploaded to more than one folder at once.
Are there any prerequisites?
You should be comfortable with basic Python concepts like variables, functions, loops, and if statements. No prior experience with Google APIs or file automation is needed.
Before We Start
Before we dive in, let's lock in what you're about to build and why it matters.
Think about the folder chaos you want to solve. What types of files pile up on your computer, and what tags would help you sort them automatically into the right Google Drive folders?
Set Up Your Environment and Google Cloud Credentials
Your file organizer needs to communicate with Google Drive to upload files automatically. For that to happen, Google needs proof that your script is authorized to access your account.
In this step, you'll install the Python libraries that power file watching and Drive communication. Then you'll create a Google Cloud project and set up the credentials your script needs to authenticate.
In this step, get ready to:
- Install the Python libraries for file watching and Google Drive access.
- Create a Google Cloud project and enable the Drive API.
- Set up OAuth 2.0 credentials so your script can authenticate as you.
Install the Python libraries
Your project depends on four libraries. watchdog monitors your local folder for new files using macOS FSEvents. The three google-* packages handle authentication and communication with the Google Drive API.
- Install all four libraries by running this command in your terminal:
pip install watchdog==6.0.0 google-api-python-client==2.198.0 google-auth-oauthlib==1.4.0 google-auth-httplib2==0.4.0
You'll see pip downloading and installing each package. Once it finishes, your terminal returns to the prompt with no errors.
Seeing 'command not found' or permission errors?
If pip is not recognized, try pip3 instead. Some systems use pip3 for Python 3 packages.
If you see a permissions error, add --user at the end of the command to install packages in your user directory.
help me fix my pip install error for this project
Create a Google Cloud project and enable the Drive API
A Google Cloud project is a container that holds your API credentials and tracks which services your app can use. You need one before Google will let any script access Drive on your behalf.
- Open the Google Cloud Console by going to https://console.cloud.google.com/ in your browser.
- Sign in with your Google account if prompted.
- Click the project selector dropdown in the top bar (near the Google Cloud logo).
- Click New Project.
- Enter File Organizer as the project name.
- Click Create.
Wait a few seconds for the project to be created. A notification appears when it is ready.
- Click Select Project in the notification to switch to your new project.
- Open the navigation menu (the hamburger icon in the top-left corner).
- Select APIs & Services, then Library.
- Search for Google Drive API in the search bar.
- Click on Google Drive API in the results.
- Click Enable.
The page refreshes to show the API overview, confirming it is now enabled for your project.
What is the Google Drive API?
The Google Drive API is a REST API that lets programs create folders, upload files, search, and manage permissions in Google Drive. Your Python script will use it to create tag-based folders and upload files into them.
Usage of the Google Drive API is free. You only consume your existing 15 GB of Google Drive storage.
Create OAuth 2.0 credentials and configure the consent screen
OAuth 2.0 is the protocol Google uses to let third-party apps access your data with your explicit permission. Before creating credentials, you need to configure a consent screen that identifies your app.
- In the Google Cloud Console, open the navigation menu.
- Select Google Auth platform, then Branding.
- Click Get Started.
- Enter File Organizer as the App name.
- Select your email address as the support email.
- Choose External as the user type.
- Add your email address as the contact email.
- Agree to the User Data Policy and click Create.
Why choose External user type?
Personal Google accounts (not Google Workspace) must use External. Your app will be in "Testing" mode, limited to test users you add manually. Since only you are using this tool, that is exactly what you need.
Now add yourself as a test user so your script can authenticate.
- Go to Audience in the left sidebar.
- Click Add users under the Test users section.
- Enter your own email address and click Save.
Finally, create the OAuth client credentials your script will use.
- In the left sidebar, go to Google Auth platform, then Clients.
- Click Create Client.
- Select Desktop app as the Application type.
- Enter File Organizer Desktop as the name.
- Click Create.
A dialog appears showing your client was created.
- Click Download JSON to download the credentials file.
- Move or save this file into your project folder and rename it to credentials.json.
Keep credentials.json private
This file identifies your app to Google. Do not share it publicly or commit it to version control. If you use Git, add credentials.json to your .gitignore file.
Can't find the Download JSON button?
If you dismissed the dialog, go back to Google Auth platform > Clients. Click on your File Organizer Desktop client to open its details, then download the JSON from there.
help me find and download my OAuth credentials from Google Cloud Console
Verify your setup
Before you move on, confirm that all four libraries installed correctly and that your credentials file is in place.
What do you think this command will output if everything is installed correctly?
- Run this verification command in your terminal from your project folder:
python3 -c "import watchdog; from googleapiclient.discovery import build; print('All libraries installed!')"
You should see All libraries installed! printed in your terminal.
- Confirm credentials.json exists in your project folder by running:
ls credentials.json
You should see credentials.json listed in the output.
Seeing an ImportError?
If the import fails, the library did not install into the same Python that python3 points to. Try reinstalling with python3 -m pip install instead of pip install to ensure the packages land in the right location.
help me troubleshoot my Python import error after pip install
Your environment is locked and loaded. Next up, you'll use these credentials to connect to Google Drive and prove the API link is live.
Connect to Google Drive and List Your Files
Your Google Cloud project is configured and your credentials.json file is ready to go. Now it's time to write the Python code that actually uses those credentials to talk to Google Drive.
Before building the full file organizer, you need to confirm the API connection works. In this step, you'll write a script that authenticates with Google Drive and lists your files. Think of it as proof that the wiring is live.
In this step, get ready to:
- Write the OAuth 2.0 authentication function.
- Connect to the Google Drive API and list your files.
- Run the script and authorize access through your browser.
Write the authentication code
OAuth 2.0 is the protocol Google uses to let third-party apps access your data without sharing your password. Your script will open a browser window, you'll sign in and grant access, and then a token.json file stores that permission so you only do this once.
- Create a new file called drive_organizer.py in your project folder.
- Add the imports and configuration constants by pasting the following code:
import os
import re
import time
import sys
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from googleapiclient.http import MediaFileUpload
# Full Drive access scope for creating folders and uploading files
SCOPES = ["https://www.googleapis.com/auth/drive"]
# The local folder the script will watch for new files
INBOX_FOLDER = os.path.expanduser("~/FileOrganizer/inbox")
What does this code do?
- The standard library imports (os, re, time, sys) handle file paths, regex, timing, and system operations.
- The watchdog imports give you file system monitoring (you'll use these in a later step).
- The google imports handle authentication, token refresh, and talking to the Drive API.
- SCOPES tells Google what level of access your script needs. Full Drive access lets you create folders and upload files.
- INBOX_FOLDER defines the local folder the script will monitor for new files.
- Save drive_organizer.py.
- Confirm the imports resolve by running this command:
python3 drive_organizer.py
You should see no output and no error messages. A silent exit means all four libraries imported successfully.
Seeing a ModuleNotFoundError?
Make sure you installed the packages in Step 1 using pip install. If you have multiple Python versions, you may need to use pip3 instead of pip.
help me fix a ModuleNotFoundError when importing Google Drive libraries in Python
Now add the authentication function below the constants. This function checks for an existing token, refreshes it if expired, or runs a fresh browser login.
- Add the following function below the INBOX_FOLDER line in drive_organizer.py:
def authenticate():
"""Authenticate with Google Drive and return the service object."""
creds = None
# Check if we already have a saved token
if os.path.exists("token.json"):
creds = Credentials.from_authorized_user_file("token.json", SCOPES)
# If no valid credentials, start the login flow
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
"credentials.json", SCOPES
)
creds = flow.run_local_server(port=0)
# Save the token for future runs
with open("token.json", "w") as token:
token.write(creds.to_json())
service = build("drive", "v3", credentials=creds)
return service
What does this code do?
- First, it checks if a token.json file already exists from a previous login. If so, it loads those credentials.
- If the token is missing or expired, it either refreshes silently or opens your browser for a fresh Google sign-in.
- After authenticating, it saves the token to token.json so future runs skip the browser step entirely.
- Finally, it builds a Drive API service object you can use to list, create, and upload files.
- Save drive_organizer.py.
Add code to list files from your Drive
The authenticate() function gives you a service object, but nothing calls it yet. You need a main() function that uses the service to fetch and print your Drive files.
- Add the following code below the authenticate() function in drive_organizer.py:
def main():
# Create the inbox folder if it doesn't exist yet
os.makedirs(INBOX_FOLDER, exist_ok=True)
print("Authenticating with Google Drive...")
service = authenticate()
print("Authenticated successfully!\n")
# List the first 10 files from your Drive
results = (
service.files()
.list(pageSize=10, fields="files(id, name)")
.execute()
)
items = results.get("files", [])
if items:
print("Connected! Your recent Drive files:")
for item in items:
print(f" {item['name']}")
else:
print("Connected! (No files found in Drive)")
if __name__ == "__main__":
main()
What does this code do?
- os.makedirs(INBOX_FOLDER, exist_ok=True) creates the ~/FileOrganizer/inbox folder on your Mac if it doesn't already exist.
- service.files().list() calls the Google Drive API to retrieve up to 10 files.
- The fields parameter tells the API to only return each file's ID and name, keeping the response lightweight.
- The if __name__ == "__main__" block ensures main() runs when you execute the script directly.
- Save drive_organizer.py.
Run and authorize the script
Everything is wired up. When you run the script, it will open your browser and ask you to sign in with Google.
What will happen when I run this?
A browser tab opens showing Google's sign-in page. Because your app is in testing mode and hasn't been verified by Google, you'll see a warning screen saying the app isn't verified. This is expected for personal-use apps with fewer than 100 users.
Click Advanced, then click Go to File Organizer (unsafe) to proceed. After granting access, the browser shows a success message and your terminal prints your Drive file names.
Before you run this, do you think the script will print file names or an error?
- Run the script by entering this command:
python3 drive_organizer.py
- A browser tab opens. Sign in with the Google account you added as a test user in Step 1.
- On the warning screen, click Advanced.
- Click Go to File Organizer (unsafe).
- Click Continue to grant your script access to Google Drive.
Your terminal prints a list of file names from your Google Drive. A token.json file now exists in your project folder, storing your credentials for future runs.
Browser didn't open or seeing an error?
- Make sure credentials.json is in the same folder as drive_organizer.py. The script looks for it in the current directory.
- If you see a redirect error, confirm you created a Desktop app OAuth client (not Web application) in Step 1.
- If the browser opened but you got a 403 error, make sure you added your email as a test user under Audience in the Google Cloud Console.
help me fix my Google Drive OAuth authentication flow in Python
- Confirm token.json was created by running:
ls token.json
You should see token.json listed in the output.
✔️ Awesome, I've got everything!
Great. Double check you've saved drive_organizer.py and confirm your terminal printed file names from Google Drive.
ⓧ I'd like to double check the full code
Your drive_organizer.py should have these sections in order. Scroll up and confirm each chunk is present:
- Imports (os, re, time, sys, watchdog, google auth libraries).
- SCOPES and INBOX_FOLDER constants.
- The authenticate() function with token check, refresh/login flow, and service builder.
- The main() function that creates the inbox folder and lists 10 Drive files.
- The if __name__ == "__main__" block calling main().
Your script can now talk to Google Drive and prove it by listing your files. Next up, you'll add the core logic: parsing tags from filenames and uploading files to the matching Drive folder.
Upload a Tagged File to the Right Folder
Your script can now talk to Google Drive and list files. That proves the connection works.
Now you need the core logic: parse a hashtag from a filename, find (or create) the matching folder in Drive, and upload the file there with a clean name. Without this, your files would pile up with no organization.
In this step, get ready to:
- Write a tag parser that extracts #tags from filenames and returns a clean name.
- Write functions that find or create a Drive folder and upload a file into it.
- Test the full flow by uploading a tagged file and verifying it arrives in the correct Drive folder.
Write the tag parser
A filename like test_doc #networking.txt contains two pieces of information: the tag (networking) and the clean name (test_doc.txt). You need a function that splits these apart using regex pattern matching.
- In drive_organizer.py, find the end of the authenticate() function.
- Add the following parse_tags function below it (above def main():):
def parse_tags(filename):
"""Extract tags and clean filename."""
name, ext = os.path.splitext(filename)
tags = re.findall(r"#(\w+)", name)
clean_name = re.sub(r"\s*#\w+", "", name).strip()
clean_filename = clean_name + ext
return clean_filename, tags
What does this code do?
- os.path.splitext(filename) separates the file extension (.txt) from the rest of the name so the tag lives in the name portion only.
- re.findall(r"#(\w+)", name) finds every #word pattern and returns just the word part (without the #).
- re.sub(r"\s*#\w+", "", name) strips all tags (and any leading space before each tag) from the filename, leaving a clean name.
- Save drive_organizer.py.
- Test the parser by running this command:
python3 -c "from drive_organizer import parse_tags; print(parse_tags('test_doc #networking.txt'))"
You should see:
('test_doc.txt', ['networking'])
The tag is extracted and the filename is clean.
Seeing a ModuleNotFoundError?
- Make sure you are running the command from the same folder where drive_organizer.py lives.
- Check for typos in the function name or filename.
Why isn't my parse_tags import working?
Write the folder finder/creator and upload function
When your script encounters a tag like #networking, it needs to check whether a folder called "networking" already exists in Drive. If not, it creates one. Then it uploads the file into that folder.
- In drive_organizer.py, add the find_or_create_folder function directly below parse_tags:
def find_or_create_folder(service, folder_name):
"""Find a folder by name in Drive, or create it if it doesn't exist."""
query = (
f"name='{folder_name}' and "
f"mimeType='application/vnd.google-apps.folder' and "
f"trashed=false"
)
results = service.files().list(q=query, fields="files(id, name)").execute()
folders = results.get("files", [])
if folders:
return folders[0]["id"]
file_metadata = {
"name": folder_name,
"mimeType": "application/vnd.google-apps.folder",
}
folder = service.files().create(body=file_metadata, fields="id").execute()
print(f" Created folder: {folder_name}")
return folder.get("id")
What does this code do?
- The query string searches Drive for a folder with the exact tag name that is not in the trash.
- If a match is found, it returns that folder's ID immediately.
- If no match exists, it creates a new folder using the application/vnd.google-apps.folder MIME type and returns the new folder's ID.
- Save drive_organizer.py.
- Test that the function can create a folder in Drive by running:
python3 -c "from drive_organizer import authenticate, find_or_create_folder; service = authenticate(); folder_id = find_or_create_folder(service, 'networking'); print(f'Folder ID: {folder_id}')"
You should see a folder ID printed (a long alphanumeric string) and a "Created folder: networking" message.
Getting an HttpError or permission error?
- Make sure credentials.json and token.json are in the same folder as drive_organizer.py.
- If your token expired, delete token.json and run again to re-authenticate through the browser.
Why is my Google Drive API call failing?
- Now add the upload_file function below find_or_create_folder:
def upload_file(service, file_path, folder_id, clean_name):
"""Upload a file to a specific folder in Google Drive."""
file_metadata = {"name": clean_name, "parents": [folder_id]}
media = MediaFileUpload(file_path, resumable=True)
file = (
service.files()
.create(body=file_metadata, media_body=media, fields="id")
.execute()
)
print(f" Uploaded '{clean_name}' (ID: {file.get('id')})")
What does this code do?
- file_metadata sets the uploaded file's name to the clean version (no tag) and places it inside the target folder via parents.
- MediaFileUpload handles reading the local file and streaming it to Drive. The resumable=True flag lets large uploads recover from interruptions.
- Add the process_file function below upload_file. This orchestrates the full flow for a single file:
def process_file(service, file_path):
"""Parse tags from filename and upload to the matching Drive folder(s)."""
filename = os.path.basename(file_path)
clean_name, tags = parse_tags(filename)
if not tags:
print(f" No tags found in '{filename}', skipping.")
return
for tag in tags:
folder_id = find_or_create_folder(service, tag)
upload_file(service, file_path, folder_id, clean_name)
print(f" -> Routed to folder: {tag}")
What does this code do?
- os.path.basename(file_path) extracts just the filename from a full path.
- If no tags are found, the file is skipped. No upload happens.
- The for tag in tags loop handles each tag. For now your files have one tag, but this loop is already ready for multiple tags later.
- Save drive_organizer.py.
Test with a real file
Time to see the full pipeline in action. You will create a tagged test file and run process_file on it to confirm it arrives in the correct Drive folder with a clean name.
- Create a test file by running this command:
echo "This is a test document for the file organizer." > "test_doc #networking.txt"
- Confirm the file exists by running:
ls
You should see test_doc #networking.txt in the output alongside your other project files.
Before you run the next command, what do you think will happen? Will the file upload with the #networking in its name, or will it arrive as something else?
- Run the upload by executing:
python3 -c "from drive_organizer import authenticate, process_file; service = authenticate(); process_file(service, 'test_doc #networking.txt')"
You should see output confirming the upload:
Uploaded 'test_doc.txt' (ID: ...)
-> Routed to folder: networking
The tag was stripped and the file was uploaded with the clean name test_doc.txt. Since the "networking" folder already exists from your earlier test, the function found it rather than creating a duplicate.
- Open Google Drive in your browser.
- Find the folder named networking.
- Click into it. You should see a file named test_doc.txt inside.
Don't see the networking folder or file?
- Check the terminal output. If you see "No tags found", make sure the filename has a space before the # character: test_doc #networking.txt (not test_doc#networking.txt).
- If you see an HttpError, delete token.json and run again to refresh your authentication.
- Make sure you are running the command from the folder where both drive_organizer.py and the test file live.
Why isn't my file appearing in Google Drive?
✔️ Awesome, I've got everything!
Great. Double check you have saved drive_organizer.py with all four new functions.
ⓧ I'd like to double check the full code
Your drive_organizer.py should contain these sections in order. Scroll up and confirm each one is present:
- Imports (os, re, time, sys, watchdog, google auth libraries).
- SCOPES and INBOX_FOLDER constants.
- authenticate() function.
- parse_tags(filename) function.
- find_or_create_folder(service, folder_name) function.
- upload_file(service, file_path, folder_id, clean_name) function.
- process_file(service, file_path) function.
- main() function (unchanged from the previous step).
- if __name__ == "__main__": main() at the bottom.
Your organizer can now parse tags, find or create folders, and upload files. Next up, you will make this fully automatic so it detects new files the instant they appear in your inbox folder.
Add Real-Time Folder Watching
Your script can now parse tags and upload files to Google Drive. But you have to run it manually each time, pointing it at a specific file.
In this step, you'll make the script detect new files the moment they appear in your inbox folder. This is where the project comes alive. Drop a file in, watch it vanish into the cloud.
In this step, get ready to:
- Build a custom event handler that reacts to new files instantly.
- Wire up a folder watcher that runs continuously in your terminal.
- Test the full automation by dropping a tagged file into your inbox.
Set up watchdog with a custom event handler
The watchdog library uses macOS FSEvents to detect file system changes instantly. Instead of polling a folder on a timer, it reacts to operating system notifications the moment a file appears.
You need a custom class that tells watchdog what to do when it detects a new file. This class will call your existing process_file function to handle the tag parsing and upload.
- In drive_organizer.py, find the process_file function. Add the following class directly below it, before def main()::
class InboxHandler(FileSystemEventHandler):
"""Watches for new files and processes them."""
def __init__(self, service):
self.service = service
def on_created(self, event):
if event.is_directory:
return
# Small delay to ensure file write is complete
time.sleep(1)
file_path = event.src_path
filename = os.path.basename(file_path)
print(f"\nNew file detected: {filename}")
try:
process_file(self.service, file_path)
except HttpError as error:
print(f" Error: {error}")
What does this code do?
This class inherits from FileSystemEventHandler, which is watchdog's base class for reacting to file system events.
- __init__ stores the authenticated Drive service so the handler can upload files.
- on_created fires every time a new file appears in the watched folder. It skips directories, waits 1 second for the file write to finish, then calls process_file to parse tags and upload.
- The try/except block catches any Google Drive API errors so one failed upload does not crash the entire watcher.
- Save drive_organizer.py. The next substep wires this handler into a running observer.
Wire up the Observer and run continuously
The InboxHandler knows what to do when a file appears, but nothing is telling it to watch a folder yet. You need an Observer to point it at your inbox and keep the script running.
- In drive_organizer.py, find the end of your main() function. Locate this line:
else:
print("Connected! (No files found in Drive)")
- Add the following code directly below that line, still inside main():
print(f"\nWatching folder: {INBOX_FOLDER}")
print("Drop tagged files here (e.g., 'notes #work.txt')")
print("Press Ctrl+C to stop.\n")
# Start watching
event_handler = InboxHandler(service)
observer = Observer()
observer.schedule(event_handler, INBOX_FOLDER, recursive=False)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\nStopping file organizer...")
observer.stop()
observer.join()
print("Done!")
What does this code do?
- InboxHandler(service) creates your event handler with the authenticated Drive connection.
- observer.schedule(event_handler, INBOX_FOLDER, recursive=False) tells the Observer to monitor your inbox folder for changes. Setting recursive=False means it only watches the top level, not subfolders.
- The while True loop keeps the script alive. When you press Ctrl+C, the KeyboardInterrupt is caught and the observer shuts down gracefully.
- Save drive_organizer.py.
- Run the script to confirm it starts watching by running:
python3 drive_organizer.py
Your terminal prints your Drive files, then shows the watching message. The script keeps running and waits for new files.
Script exits immediately?
- Make sure the new code is indented inside main(). If it sits at the module level (no indentation), it runs before authentication and fails.
- Check that the InboxHandler class is defined above main(), not inside it. Python needs to see the class before main() references it.
- If you see a NameError, confirm you placed the class between process_file and main().
Why isn't my file organizer script running continuously?
✔️ Awesome, I've got everything!
Great. Double check you have saved drive_organizer.py and that the script is still running in your terminal.
ⓧ I'd like to double check the full code
Your drive_organizer.py is now over 100 lines, so here is a structural checklist. Scroll up through your file and confirm each piece is present in this order:
- Imports at the top (os, re, time, sys, watchdog imports, Google auth imports).
- Constants: SCOPES and INBOX_FOLDER.
- authenticate() function.
- parse_tags() function.
- find_or_create_folder() function.
- upload_file() function.
- process_file() function.
- InboxHandler class with __init__ and on_created methods.
- main() function ending with the Observer loop and KeyboardInterrupt handler.
- if __name__ == "__main__": block at the very bottom.
Test the full automation
Your script is running and watching the inbox folder. Time to prove it works end-to-end by dropping a tagged file in and seeing it appear in Google Drive.
- Keep the script running in your current terminal. Open a second terminal window by pressing Cmd+Space (macOS) or the Windows key (Windows), typing Terminal, and pressing Enter.
- Create a test file in your inbox by running:
echo "Monthly budget data" > ~/FileOrganizer/inbox/"budget #finance.txt"
Before you check your first terminal, what do you expect to see there? Think about what your InboxHandler prints when it detects a file.
- Switch back to the terminal running your script.
You should see output like this:
New file detected: budget #finance.txt
Created folder: finance
Uploaded 'budget.txt' (ID: ...)
-> Routed to folder: finance
The script detected the file, parsed the #finance tag, created a new folder, and uploaded the file with a clean name.
- Open Google Drive in your browser. You should see a new folder called finance containing a file named budget.txt.
- Stop the script by pressing Ctrl+C in the terminal running it. You should see "Stopping file organizer..." followed by "Done!".
File not detected?
- Make sure the script was still running when you created the file. If it exited, restart it with python3 drive_organizer.py and create the test file again.
- Confirm you created the file inside ~/FileOrganizer/inbox/ and not a different folder.
- If the file was detected but the upload failed, check that your token.json is still valid. Delete it and re-run the script to re-authenticate.
My watchdog script isn't detecting new files in the inbox folder.
Your file organizer is fully automated. Drop a tagged file in, and it flows to the right place in Google Drive without you lifting a finger. Next up, you'll tackle a stretch challenge: handling files with multiple tags.
Secret mission
Handle Multiple Tags
Real files often belong in more than one category. A security audit report might be both #networking and #security. Can your organizer handle uploading one file to multiple Drive folders at once?
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally and uses the Google Drive API at no cost. There are no ongoing charges to worry about.
Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- Google Cloud project ("File Organizer") with OAuth 2.0 credentials.
- Google Drive folders and uploaded test files (networking, finance, security).
- Local project files (drive_organizer.py, credentials.json, token.json).
- Local inbox folder (~/FileOrganizer/inbox).
- Python packages (watchdog, google-api-python-client, google-auth-oauthlib, google-auth-httplib2).
Keep everything running
No action needed. Choose this if you plan to keep using the file organizer or want to extend it further.
- Your Google Cloud project stays active with your OAuth credentials ready.
- Your local files remain in place. Run python3 drive_organizer.py any time to start watching again.
- The Python packages stay installed in your environment.
Pause - I'll come back to this later
Stop the running script to free up your terminal. Your files and credentials stay in place for next time.
- Press Ctrl+C in the terminal where the script is running to stop the file watcher.
- Your token.json and credentials.json remain saved. You will not need to re-authenticate next time.
- Your Google Cloud project stays active at no cost.
Delete - I don't want to use this again
Remove all project resources and start fresh. Follow each step below.
- Delete the test folders and files from Google Drive:
- Go to drive.google.com.
- Right-click the networking folder and click Move to Trash.
- Repeat for the finance and security folders.
- Click Trash in the left sidebar, then click Empty trash to permanently delete.
- Delete the Google Cloud project:
- Go to the IAM & Admin Settings page in the Google Cloud Console.
- Select the File Organizer project.
- Click Shut down.
- Enter the project ID and click Shut down anyway.
- Delete the local inbox folder by running this command:
rm -rf ~/FileOrganizer
- Delete the local project files by running:
rm drive_organizer.py credentials.json token.json
- Uninstall the Python packages by running:
pip uninstall watchdog google-api-python-client google-auth-oauthlib google-auth-httplib2 -y
Nice Work!
Nice Work!
You just built a real-time file automation that turns a local folder into a smart inbox for your Google Drive. Drop in a tagged file, and it routes itself to the right place in the cloud.
You've learned how to:
- Build a real-time file monitor using the watchdog library with macOS FSEvents to detect new files the instant they appear.
- Set up a Google Cloud project with OAuth 2.0 credentials and connect to the Google Drive API to create folders and upload files programmatically.
- Parse hashtag-based tags from filenames using regex and route files to the correct Drive folders automatically.
- Secret Mission: Extended the organizer to handle multiple tags on a single file, uploading it to multiple folders simultaneously.
Ready to quiz yourself?