#!/usr/bin/env python3 """ drive_upload.py — push Drogheda branded artifacts to the Project-Drogheda Drive folders. WHY THIS EXISTS The Drive MCP connector can create folders/native Docs but NOT upload binary files, and the browser extension can't drive Drive's native file-picker dialog. This script uses the Google Drive API directly, so it CAN upload the PDFs/xlsx. It "upserts": if a file with the same name already exists in the target folder, it updates it; otherwise it creates it. Run it again any time you rebuild a doc. ONE-TIME SETUP 1) pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib 2) In Google Cloud Console: create an OAuth client (Desktop app), enable the Drive API, download the JSON as credentials.json next to this script. 3) Run: python3 drive_upload.py → a browser opens once to authorize; a token.json is cached so future runs are non-interactive. USAGE cd ~/Documents/Claude/Projects/Drogheda python3 00-Project-Management/Drive-Upload/drive_upload.py # upload all python3 00-Project-Management/Drive-Upload/drive_upload.py --dry-run # show plan only """ import os, sys, argparse REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) # file (relative to repo root) -> Drive folder ID (Project-Drogheda subfolders) MAPPING = [ ("01-Requirements/Drogheda-HBOX-Ficha-Tecnica.pdf", "1R_ysNsVV7_f0vHevfMcSkXAdaSYgzI95"), # 01-Requirements ("01-Requirements/Drogheda-SRS-Requerimientos.pdf", "1R_ysNsVV7_f0vHevfMcSkXAdaSYgzI95"), # 01-Requirements ("01-Requirements/Drogheda-Requirements-Tracker.xlsx", "1R_ysNsVV7_f0vHevfMcSkXAdaSYgzI95"), # 01-Requirements ("03-Architecture-Hardware/Drogheda-Manual-Instalacion-Operacion.pdf", "1LBUHOk9dnrrb5UxD5q9k-jBbO7lTlXos"), # 03-Architecture-Hardware ("03-Architecture-Hardware/Drogheda-BOM-Maestro.xlsx", "1LBUHOk9dnrrb5UxD5q9k-jBbO7lTlXos"), # 03-Architecture-Hardware ("06-Testing-Validation/Drogheda-RCA-Informe-Falla.pdf", "1CNLnjSa7ro3rkRGQ6w_DBUOIp5ukizi2"), # 06-Testing-Validation ("Marketing/HBOX-Datasheet/Drogheda-HBOX-Datasheet.pdf", "1orZKyRi36nebjuHBXDLZIroXx_eWHXYZ"), # Commercial (Cliente) — ficha técnica ("Marketing/HBOX-Brochure-RTK/Drogheda-HBOX-Brochure.pdf", "1orZKyRi36nebjuHBXDLZIroXx_eWHXYZ"), # Commercial (Cliente) — flyer intro ] SCOPES = ["https://www.googleapis.com/auth/drive.file"] HERE = os.path.dirname(os.path.abspath(__file__)) def service(): from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from google.auth.transport.requests import Request from googleapiclient.discovery import build tok, cred = os.path.join(HERE, "token.json"), os.path.join(HERE, "credentials.json") creds = Credentials.from_authorized_user_file(tok, SCOPES) if os.path.exists(tok) else None if not creds or not creds.valid: if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) else: if not os.path.exists(cred): sys.exit("Missing credentials.json — see ONE-TIME SETUP in this file's header.") creds = InstalledAppFlow.from_client_secrets_file(cred, SCOPES).run_local_server(port=0) open(tok, "w").write(creds.to_json()) return build("drive", "v3", credentials=creds) def upsert(svc, path, folder_id, dry): from googleapiclient.http import MediaFileUpload name = os.path.basename(path) full = os.path.join(REPO, path) if not os.path.exists(full): print(f" SKIP {name} (not found at {path})"); return if dry: print(f" PLAN {name} -> {folder_id}"); return q = f"name = '{name}' and '{folder_id}' in parents and trashed = false" hits = svc.files().list(q=q, fields="files(id)").execute().get("files", []) media = MediaFileUpload(full, resumable=True) if hits: svc.files().update(fileId=hits[0]["id"], media_body=media).execute() print(f" UPDATED {name}") else: svc.files().create(body={"name": name, "parents": [folder_id]}, media_body=media, fields="id").execute() print(f" CREATED {name}") def main(): ap = argparse.ArgumentParser() ap.add_argument("--dry-run", action="store_true") args = ap.parse_args() svc = None if args.dry_run else service() print("Uploading Drogheda artifacts to Project-Drogheda (Drive):") for path, fid in MAPPING: upsert(svc, path, fid, args.dry_run) print("Done.") if __name__ == "__main__": main()