python/googleDriveList.py

from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

def err(*m):
    print('error in', __file__ + ':', *m)
    x = 1 / 0

def credentials(crt):
    if type(crt) != str or len(crt) < 1:
        err(f"bad certification name: {crt} {type(crt)}")
    """ load the credentials form google OAuth2, 
            see Google APPI Console https://console.developers.google.com/?authuser=0&project=quickstart-1611556606696 
    """
    # If modifying these scopes, delete the file token.pickle.
    SCOPES =[ 'https://www.googleapis.com/auth/drive.metadata.readonly' # drive readonly
            , 'https://www.googleapis.com/auth/drive'                   # drive file update
            , 'https://www.googleapis.com/auth/documents.readonly'      # docs readonly
            #, 'https://www.googleapis.com/auth/documents'               # docs readWrite
            ]
    tokPiFi = f'token-{crt}.pickle'                                # the file to cache authorization
    credJson = '/wkData/pc/googleAPIcredentials.json'       # the credentials file generated by google API

    creds = None
    # The file token.pickle stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if os.path.exists(tokPiFi):
        with open(tokPiFi, 'rb') as token:
            creds = pickle.load(token)
    # If there are no (valid) credentials available, let the user log in.
    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(credJson, SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open(tokPiFi, 'wb') as token:
            pickle.dump(creds, token)
    return creds
import sys
def driveInfo():
    global srvDrive
    # Call the Drive v3 API
    ab = srvDrive.about().get(fields='user(displayName, emailAddress, kind), storageQuota').execute()
    # print(ab)   
    print(ab['user']['kind'], ab['user']['displayName'], ab['user']['emailAddress']
            , '; storage (MB) usage', round(int(ab['storageQuota']['usage'])/1048576), ', trash', round(int(ab['storageQuota']['usageInDriveTrash'])/1048576)
                       , ', limit', round(int(ab['storageQuota']['limit'])/1048576))

def testDrive():
    global srvDrive
    # Call the Drive v3 API
    print('abo', dir(srvDrive.about()))
    print('get', srvDrive.about().get(fields='*').execute())
    print('root', srvDrive.files().get(fileId='root',fields='*').execute())
    next = None
    for p in range(2):
        results = srvDrive.files().list(
            pageSize=10, pageToken=next, fields="nextPageToken, files(id, name, kind, parents)"
                , q="mimeType = 'application/vnd.google-apps.folder' and name = 'akt' or name = 'spWal' or name = 'sitzung'").execute()
        items = results.get('files', [])
        next = results.get('nextPageToken')
        print('page', p,  'next', next)

        if not items:
            print('No more files found.')
            return
        print('Files:', items)
        for item in items:
            print('\nfile',item['name'], item)
#           data = srvDrive.files().get(fileId=item['id'],fields='name,kind,mimeType,id,parents,shared,ownedByMe').execute()
            data = srvDrive.files().get(fileId=item['id'],fields='*').execute()
            print('get',data)
            if 'parents' in item:
                print("parent", srvDrive.files().get(fileId=item['parents'][0], fields='name,kind,mimeType,id,parents,shared,ownedByMe').execute())

def testList():
    next = None
    res = srvDrive.files().list(
        pageSize=500, pageToken=next, fields="nextPageToken, files(id, name, mimeType, kind, parents,modifiedTime,owners(emailAddress))"
            , q="'root' in parents", orderBy="folder, name desc"
            ).execute()
    fi = res.get('files', [])
    print('#files', len(fi))
    for f in fi:
        print(f)

def simList(fo): 
    next = None
    while True: 
        res = srvDrive.files().list(
            pageSize=5, pageToken=next, fields="nextPageToken, files(name, id,  kind, mimeType, modifiedTime, parents, owners(emailAddress))"
                , q="'" + fo + "' in parents", orderBy="name"
                ).execute()
        fi = res.get('files', [])
        next = res.get('nextPageToken')
        print('#files', len(fi), 'next', next)
        for f in fi:
            if len(f['parents']) != 1:
                err('parents in', f);
            f['parent'] = f['parents'][0]
            del f['parents']
            if len(f['owners']) != 1 or 'emailAddress' not in f['owners'][0]:
                err('owners in', f);
            f['owner'] =  f['owners'][0]['emailAddress']
            del f['owners']
            print(f)
        if next == None:
            break

def fldWalk(fo, cb, lv, nm): 
    fFlds = "name, id,  kind, mimeType, createdTime, modifiedTime, trashed, parents, owners(emailAddress)"
    ff = []
    if lv == -1 :
        ff.append(srvDrive.files().get(fileId=fo,fields=fFlds).execute())
    else: 
        next = None
        while True: 
            res = srvDrive.files().list(
                pageSize=500, pageToken=next, fields=f"nextPageToken, files({fFlds})"
                    , q="'" + fo + "' in parents", orderBy="name"
                    ).execute()
            ff += res.get('files', [])
            next = res.get('nextPageToken')
            if next == None:
                break
        fc = 0
        for f in ff:
            fc += int(f['mimeType'] == 'application/vnd.google-apps.folder')
        print('{:39} {}'.format('{:30}'.format('  ' * lv + nm) + ' #' + str(fc) + '/' + str(len(ff)) + '#', fo))

    for f in ff:
        print(1, f)
        if 'parents' not in f:
            if lv == -1:
                f['parent'] = ''
            else:
                err('no parent', f)
        elif len(f['parents']) != 1:
            err('parents in', f);
        else:
            f['parent'] = f['parents'][0]
            del f['parents']
        if len(f['owners']) != 1 or 'emailAddress' not in f['owners'][0]:
            err('owners in', f);
        f['owner'] =  f['owners'][0]['emailAddress']
        del f['owners']
        # print(f)
        cb(f)
    for f in ff:
        if f['mimeType'] == 'application/vnd.google-apps.folder':
            fldWalk(f['id'], cb, lv+1, f['name'])

import csv

def fo2csv(fo, fn, foN):
    with open(fn, 'w', newline='') as csvfile:
        writer = csv.DictWriter(csvfile, fieldnames=['name', 'id',  'kind', 'mimeType', 'createdTime', 'modifiedTime', 'trashed', 'trashedTime', 'parent', 'owner'])
        writer.writeheader()
        fldWalk(fo, lambda d: writer.writerow(d), -1, foN)

tSeq = 0
def treeListOne(fd, pNo):
    global tSeq
    tSeq+=1
    fd['seq'] = tSeq
    fd['paS'] = pNo
    print('{0:5d} {1:5d}'.format(pNo, tSeq), fd['name'], fd['mimeType'], fd['id'])
    return fd

fId2fi = dict()
fId2fo = dict()
def treeDictAdd(fd):
    global fId2fi
    if fd['id'] not in fId2fi:
        fId2fi[fd['id']] = fd
    next = None
    fls = []
    print('folder start', fd['name'])
    while True:
        results = srvDrive.files().list(
            pageSize=500, pageToken=next, fields="nextPageToken, files(id, name, mimeType, kind, parents)"
                , q="'" + fd['id'] + "' in parents").execute()
        fls += results.get('files', [])
        print('+= get', [ f['name'] for f in fls])
        next = results.get('nextPageToken')
        if next == None:
            break
    fls.sort(key=lambda f: f['name'])
    print('sort', [ f['name'] for f in fls])
    for f in fls:
        fId2fi[f['id']] = f
    print('dict', [ f['name'] for f in fId2fi.values()])
    for f in fls:
        if f['mimeType'] == 'application/vnd.google-apps.folder':
            treeDictAdd(f)

def testList():
    next = None
    res = srvDrive.files().list(
        pageSize=500, pageToken=next, fields="nextPageToken, files(id, name, mimeType, kind, parents,modifiedTime,owners(emailAddress))"
            , q="'root' in parents", orderBy="folder, name desc"
            ).execute()
    fi = res.get('files', [])
    print('#files', len(fi))
    for f in fi:
        print(f)
def allDict():
    global fId2fi, fId2fo
    fi = dict()
    root = srvDrive.files().get(fileId='root',fields='name,kind,mimeType,id,parents,modifiedTime,owners(emailAddress)').execute()
    fi[root['id']] = root
    print('root', root)
    fId2fo = { root['id']: []}
    next = None
    while True:
        results = srvDrive.files().list(
            pageSize=500, pageToken=next, fields="nextPageToken, files(id, name, mimeType, kind, parents,modifiedTime,owners(emailAddress))").execute()
        for f in results.get('files', []):
            if f['id'] in fi:
                print('duplicate file', f)
            else:
                fi[f['id']] = f
            if f['mimeType'] == 'application/vnd.google-apps.folder':
                fId2fo[f['id']] = []
        print('next', len(fi), len(fId2fo))
        next = results.get('nextPageToken')
        if next == None:
            break
    nopa = []
    for f in fi.values():
        if 'parents' not in f or len(f['parents']) == 0:
            nopa.append(f['id'])
            # print('orphan has no paren', f) 
        elif len(f['parents']) != 1:
            err('not 1 parents', f)
        else:
            p = f['parents'][0]
            if p not in fi:
                err('parent missing', f)
            elif fi[p]['mimeType'] != 'application/vnd.google-apps.folder':
                err('parent of', f , 'is not folder', fi[p])
            fId2fo[p].append(f['id'])
    del nopa[0:1]           
    nopa.sort(key=lambda i: fi[i]['name'])
    nopa[0:0] = [root['id']]
    allAdd(nopa, fi, fId2fo)
    foPrint(nopa, 0)

def allAdd(a, fi, fo):
    for i in a:
        fId2fi[i] = fi[i]
    for i in a:
        if i in fo:
            fo[i].sort(key=lambda k: fi[k]['name'])
            allAdd(fo[i], fi, fo)

def foPrint(fo, lv):
    global fId2fi, fId2fo
    for f in fo:
        fi = fId2fi[f]
        print('  ' * lv, fi['name'], fi['mimeType'])
        if fi['mimeType'] == 'application/vnd.google-apps.folder':
            foPrint(fId2fo[fi['id']], lv+1)

def treePrint(td): 
    seq=0
    f2s = dict()
    for id, f in td.items():
        seq += 1
        if f['mimeType'] == 'application/vnd.google-apps.folder':
            f2s[id] = seq
        if 'parents' not in f or len(f['parents']) == 0:
            p=-1
        elif len(f['parents']) == 1:
            p = f2s[f['parents'][0]]
        else:
            err('not 1 parents', f)
        if 'owners' not in f or len(f['owners']) < 1 :
            o = ''
        elif len(f['owners']) == 1 : 
            o = f['owners'][0]['emailAddress']
        else:
            o = '???selever users??? ' + ', '.join([ o['emailAddress'] for o in f['owners']])
        print('{0:5d} {1:5d}'.format(seq, p), f['name'], f['mimeType'], f['id'], f['modifiedTime'], o)

def chown(fold):
    global srvDrive, chownFlds
    print('get', dUser := srvDrive.about().get(fields='user/emailAddress').execute())
    print('fold', fo := srvDrive.files().get(fileId=fold,fields=chownFlds).execute())
    print('my user', dUser['user']['emailAddress'], 'folder', fo['name'], 'owners', fo['owners'][0]['emailAddress'])  
    chown3(dUser['user']['emailAddress'], fo['owners'][0]['emailAddress'], fo)
   
def chown3(ownO, ownN, fold):
    global srvDrive, chownFlds
    print('chown3', ownO, '==>', ownN, 'in', fold)
    fls = []
    next = None
    while True:
        res = srvDrive.files().list(
            pageSize=2, pageToken=next, fields="nextPageToken, files(" + chownFlds + ")"
                , q="'" + fold['id'] + "' in parents and ('" + ownO + "' in owners or mimeType = 'application/vnd.google-apps.folder')").execute()
        fls += res.get('files')
        next = res.get('nextPageToken')
        if next == None:
            break
    print('list got', [ f['name'] for f in fls])
    for f in fls:
        if f['owners'][0]['emailAddress'] == ownO :
            print('f', f)
            prms = srvDrive.permissions().list(fileId=f['id'], fields='*').execute()['permissions']
            print('prms', prms)
            pN = [p for p in prms if p['emailAddress'] == ownN]
            print('pN ***', len(pN), pN)
            res = srvDrive.permissions().update(fileId=f['id'], permissionId=pN[0]['id'], transferOwnership=True, body={'role': 'owner'}).execute()
            print(res)
            print('file now', srvDrive.files().get(fileId=f['id'],fields=chownFlds).execute())
    for f in fls:
        if f['mimeType'] == 'application/vnd.google-apps.folder':
            chown3(ownO, ownN, f) 
        


def mainOld():
    global srvDrive, fId2fi
    srvDrive = build('drive', 'v3', credentials=credentials())
    driveInfo()
  #  print('get', srvDrive.about().get(fields='*').execute())
    #print('root', srvDrive.files().get(fileId='root',fields='*').execute())
    fo2csv('root', 'ffid.csv', 'root')
    exit()
    # testDrive(srvDrive)
    # treeDictAdd(srvDrive.files().get(fileId='root',fields='name,kind,mimeType,id,parents').execute())
    allDict()
    #sys.stdout = open('out.txt', 'w') 
    treePrint(fId2fi)
    chown('1tskVV0t5SGgLCGYSe39hnuJiNL01-B4h') #testWK  
#    chown('1SGWizH84Yn5vgUc-b5Cc2xOcJd-KpVHA') #spWal 
    #err('error in code want to exit', 3)

import argparse
def main():
    global srvDrive
    parser = argparse.ArgumentParser(description="ffId.py: google drive files folders Id's: list print to ffId.csv")
    parser.add_argument('cert', help='prefix for certificate. hint a=admin@spWallisellen.ch f=fiwiko@wlkl.ch w=wa@wlkl.ch')
    parser.add_argument('fun', nargs='?', default='l', help='function to perform')
#    parser.print_help()
#    print('after print_help')
    print(f"{(args := parser.parse_args())=}")
    print(f"{args.cert=} {args.fun=}")
    srvDrive = build('drive', 'v3', credentials=credentials(args.cert))
    driveInfo()
    if args.fun == 'l':
        fo2csv('root', 'ffId.csv', 'root')
    else:
        err('bad fun:', args.fun)

if __name__ == '__main__':
    main()