File size: 2,784 Bytes
c0e26bd
9d416ac
bcc0e71
c0e26bd
54a4bb3
 
c0e26bd
 
bcc0e71
c0e26bd
9d416ac
54a4bb3
c0e26bd
 
bcc0e71
c0e26bd
 
 
54a4bb3
 
 
bcc0e71
 
 
 
c0e26bd
 
 
 
 
 
 
 
 
 
 
 
 
 
bcc0e71
 
 
 
 
 
c0e26bd
bcc0e71
 
 
c0e26bd
bcc0e71
c0e26bd
 
bcc0e71
 
 
 
 
c0e26bd
 
bcc0e71
c0e26bd
 
 
 
 
 
bcc0e71
c0e26bd
 
 
 
 
 
 
 
 
 
 
 
 
bcc0e71
c0e26bd
 
 
 
bcc0e71
 
 
 
 
 
 
c0e26bd
bcc0e71
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import io
import json
from typing import Optional, List, Dict

from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
from googleapiclient.http import MediaIoBaseUpload

# Scope is enough to create/update files your app creates/has access to in Drive
DRIVE_SCOPES = ["https://www.googleapis.com/auth/drive.file"]


def build_drive_service(token_json_str: str):
    """
    token_json_str = HF secret GOOGLE_TOKEN_JSON (contents of token.json)
    """
    info = json.loads(token_json_str)
    creds = Credentials.from_authorized_user_info(info, scopes=DRIVE_SCOPES)
    return build("drive", "v3", credentials=creds, cache_discovery=False)


def list_children(service, parent_folder_id: str, page_size: int = 20) -> List[Dict]:
    """
    List immediate children in a folder (files + folders).
    """
    q = f"'{parent_folder_id}' in parents and trashed = false"
    resp = (
        service.files()
        .list(
            q=q,
            pageSize=page_size,
            fields="files(id,name,modifiedTime,mimeType)",
        )
        .execute()
    )
    return resp.get("files", [])


def find_file_by_name(service, parent_folder_id: str, filename: str) -> Optional[str]:
    """
    Returns the fileId if a file exists with this exact name in the folder.
    """
    # IMPORTANT: sanitize BEFORE f-string (no backslashes inside f-string expressions)
    safe_name = filename.replace("'", "\\'")

    q = (
        f"'{parent_folder_id}' in parents "
        f"and trashed = false "
        f"and name = '{safe_name}'"
    )

    resp = (
        service.files()
        .list(
            q=q,
            pageSize=1,
            fields="files(id,name)",
        )
        .execute()
    )

    files = resp.get("files", [])
    return files[0]["id"] if files else None


def upsert_json_file(service, parent_folder_id: str, filename: str, payload: dict) -> str:
    """
    Create the JSON file if missing, otherwise update it.
    Returns the Drive fileId.
    """
    data = json.dumps(payload, indent=2).encode("utf-8")
    media = MediaIoBaseUpload(io.BytesIO(data), mimetype="application/json", resumable=False)

    existing_id = find_file_by_name(service, parent_folder_id, filename)

    if existing_id:
        updated = (
            service.files()
            .update(
                fileId=existing_id,
                media_body=media,
                fields="id,modifiedTime,name",
            )
            .execute()
        )
        return updated["id"]

    created = (
        service.files()
        .create(
            body={"name": filename, "parents": [parent_folder_id]},
            media_body=media,
            fields="id,modifiedTime,name",
        )
        .execute()
    )
    return created["id"]