mirror of
https://github.com/GAM-team/GAM.git
synced 2026-07-04 12:51:36 +00:00
1071 lines
49 KiB
Python
1071 lines
49 KiB
Python
"""File counts, comments, paths, disk usage, share counts, tree printing.
|
|
|
|
Part of the drive sub-package, extracted from drive.py."""
|
|
|
|
"""GAM Google Drive file, permission, shared drive, and label management."""
|
|
|
|
import re
|
|
import sys
|
|
|
|
from gamlib import glaction
|
|
from gamlib import glapi as API
|
|
from gamlib import glcfg as GC
|
|
from gamlib import glclargs
|
|
from gamlib import glentity
|
|
from gamlib import glgapi as GAPI
|
|
from gamlib import glglobals as GM
|
|
from gamlib import glindent
|
|
from gamlib import glmsgs as Msg
|
|
|
|
Act = glaction.GamAction()
|
|
Ent = glentity.GamEntity()
|
|
Ind = glindent.GamIndent()
|
|
Cmd = glclargs.GamCLArgs()
|
|
|
|
APPLICATION_VND_GOOGLE_APPS = 'application/vnd.google-apps.'
|
|
MIMETYPE_GA_DOCUMENT = f'{APPLICATION_VND_GOOGLE_APPS}document'
|
|
MIMETYPE_GA_DRAWING = f'{APPLICATION_VND_GOOGLE_APPS}drawing'
|
|
MIMETYPE_GA_FILE = f'{APPLICATION_VND_GOOGLE_APPS}file'
|
|
MIMETYPE_GA_FOLDER = f'{APPLICATION_VND_GOOGLE_APPS}folder'
|
|
MIMETYPE_GA_FORM = f'{APPLICATION_VND_GOOGLE_APPS}form'
|
|
MIMETYPE_GA_FUSIONTABLE = f'{APPLICATION_VND_GOOGLE_APPS}fusiontable'
|
|
MIMETYPE_GA_JAM = f'{APPLICATION_VND_GOOGLE_APPS}jam'
|
|
MIMETYPE_GA_MAP = f'{APPLICATION_VND_GOOGLE_APPS}map'
|
|
MIMETYPE_GA_PRESENTATION = f'{APPLICATION_VND_GOOGLE_APPS}presentation'
|
|
MIMETYPE_GA_SCRIPT = f'{APPLICATION_VND_GOOGLE_APPS}script'
|
|
MIMETYPE_GA_SCRIPT_JSON = f'{APPLICATION_VND_GOOGLE_APPS}script+json'
|
|
MIMETYPE_GA_SHORTCUT = f'{APPLICATION_VND_GOOGLE_APPS}shortcut'
|
|
MIMETYPE_GA_3P_SHORTCUT = f'{APPLICATION_VND_GOOGLE_APPS}drive-sdk'
|
|
MIMETYPE_GA_SITE = f'{APPLICATION_VND_GOOGLE_APPS}site'
|
|
MIMETYPE_GA_SPREADSHEET = f'{APPLICATION_VND_GOOGLE_APPS}spreadsheet'
|
|
ME_IN_OWNERS = "'me' in owners"
|
|
ME_IN_OWNERS_AND = ME_IN_OWNERS + " and "
|
|
NOT_ME_IN_OWNERS = "not " + ME_IN_OWNERS
|
|
NOT_ME_IN_OWNERS_AND = NOT_ME_IN_OWNERS + " and "
|
|
WITH_ANY_FILE_NAME = "name = '{0}'"
|
|
WITH_MY_FILE_NAME = ME_IN_OWNERS_AND + WITH_ANY_FILE_NAME
|
|
WITH_OTHER_FILE_NAME = NOT_ME_IN_OWNERS_AND + WITH_ANY_FILE_NAME
|
|
ROOT = 'root'
|
|
ORPHANS = 'Orphans'
|
|
SHARED_WITHME = 'SharedWithMe'
|
|
SHARED_DRIVES = 'SharedDrives'
|
|
|
|
def _getMain():
|
|
return sys.modules['gam']
|
|
|
|
from gam.cmd.drive.core import (
|
|
MimeTypeCheck, _getSharedDriveNameFromId, _simpleFileIdEntityList,
|
|
_validateUserGetFileIDs, _validateUserSharedDrive,
|
|
cleanFileIDsList, getDriveFileEntity, getSharedDriveEntity, initDriveFileEntity,
|
|
)
|
|
from gam.cmd.drive.filepaths import DRIVEFILE_ORDERBY_CHOICE_MAP, _setGetCheckFilePermissions
|
|
from gam.cmd.drive.filetree import (
|
|
DriveListParameters,
|
|
FILECOUNT_SUMMARY_CHOICE_MAP, FILECOUNT_SUMMARY_NONE,
|
|
FILECOUNT_SUMMARY_ONLY, FILECOUNT_SUMMARY_USER,
|
|
OWNED_BY_ME_FIELDS_TITLES, SHOW_OWNED_BY_CHOICE_MAP,
|
|
SIZE_FIELD_CHOICE_MAP, _getGettingEntity,
|
|
extendFileTree, extendFileTreeParents, initFileTree,
|
|
)
|
|
from gam.cmd.drive.filelist import (
|
|
_checkUpdateLastModifiction, _getLastModificationPath,
|
|
_initLastModification, _showLastModification, _updateLastModificationRow,
|
|
)
|
|
|
|
|
|
|
|
|
|
def __getattr__(name):
|
|
"""Fall back to gam module for any undefined names."""
|
|
main = _getMain()
|
|
try:
|
|
return getattr(main, name)
|
|
except AttributeError:
|
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
|
|
SHARED_DRIVE_MAX_FILES_FOLDERS = 500000
|
|
TEAM_DRIVE = 'Drive'
|
|
MY_DRIVE = 'My Drive'
|
|
|
|
def printShowFileCounts(users):
|
|
def _setSelectionFields():
|
|
if DLP.showOwnedBy is not None:
|
|
fieldsList.extend(OWNED_BY_ME_FIELDS_TITLES)
|
|
if (showSize or showSizeUnits) or (DLP.minimumFileSize is not None) or (DLP.maximumFileSize is not None):
|
|
fieldsList.append(sizeField)
|
|
if showLastModification:
|
|
fieldsList.extend(['id,name,modifiedTime,lastModifyingUser(me, displayName, emailAddress),parents'])
|
|
if DLP.filenameMatchPattern:
|
|
fieldsList.append('name')
|
|
if DLP.excludeTrashed:
|
|
fieldsList.append('trashed')
|
|
if DLP.PM.permissionMatches:
|
|
fieldsList.extend(['id', 'permissions'])
|
|
if DLP.onlySharedDrives or getPermissionsForSharedDrives:
|
|
fieldsList.append('driveId')
|
|
|
|
def showMimeTypeInfo(user, mimeTypeInfo, sharedDriveId, sharedDriveName, lastModification, i, count):
|
|
if summary != FILECOUNT_SUMMARY_NONE:
|
|
if count != 0:
|
|
for mimeType, mtinfo in mimeTypeInfo.items():
|
|
summaryMimeTypeInfo.setdefault(mimeType, {'count': 0, 'size': 0})
|
|
summaryMimeTypeInfo[mimeType]['count'] += mtinfo['count']
|
|
summaryMimeTypeInfo[mimeType]['size'] += mtinfo['size']
|
|
if summary == FILECOUNT_SUMMARY_ONLY:
|
|
return
|
|
countTotal = sizeTotal = 0
|
|
for mtinfo in mimeTypeInfo.values():
|
|
countTotal += mtinfo['count']
|
|
sizeTotal += mtinfo['size']
|
|
if not csvPF:
|
|
if sharedDriveId:
|
|
kvList = [Ent.USER, user, Ent.SHAREDDRIVE, f'{sharedDriveName} ({sharedDriveId})']
|
|
else:
|
|
kvList = [Ent.USER, user]
|
|
dataList = [Ent.Choose(Ent.DRIVE_FILE_OR_FOLDER, countTotal), countTotal]
|
|
if showSize:
|
|
dataList.extend(['Size', sizeTotal])
|
|
if showSizeUnits:
|
|
dataList.extend(['SizeUnits', _getMain().formatFileSize(sizeTotal)])
|
|
if sharedDriveId:
|
|
dataList.extend(['Item cap', f"{countTotal/SHARED_DRIVE_MAX_FILES_FOLDERS:.2%}"])
|
|
_getMain().printEntityKVList(kvList, dataList, i, count)
|
|
Ind.Increment()
|
|
if showLastModification:
|
|
_showLastModification(lastModification)
|
|
for mimeType, mtinfo in sorted(mimeTypeInfo.items()):
|
|
if not showMimeTypeSize:
|
|
_getMain().printKeyValueList([mimeType, mtinfo['count']])
|
|
else:
|
|
_getMain().printKeyValueList([mimeType, f"{mtinfo['count']}, {mtinfo['size']}"])
|
|
Ind.Decrement()
|
|
else:
|
|
if sharedDriveId:
|
|
row = {'User': user, 'id': sharedDriveId, 'name': sharedDriveName, 'Total': countTotal, 'Item cap': f"{countTotal/SHARED_DRIVE_MAX_FILES_FOLDERS:.2%}"}
|
|
else:
|
|
row = {'User': user, 'Total': countTotal}
|
|
if showSize:
|
|
row['Size'] = sizeTotal
|
|
if showSizeUnits:
|
|
row['SizeUnits'] = _getMain().formatFileSize(sizeTotal)
|
|
if showLastModification:
|
|
_updateLastModificationRow(row, lastModification)
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
for mimeType, mtinfo in sorted(mimeTypeInfo.items()):
|
|
row[f'{mimeType}'] = mtinfo['count']
|
|
if showMimeTypeSize:
|
|
row[f'{mimeType}:Size'] = mtinfo['size']
|
|
csvPF.WriteRowTitles(row)
|
|
|
|
csvPF = _getMain().CSVPrintFile() if Act.csvFormat() else None
|
|
if csvPF:
|
|
csvPF.SetZeroBlankMimeTypeCounts(True)
|
|
fieldsList = ['mimeType']
|
|
DLP = DriveListParameters({'allowChoose': False, 'allowCorpora': True, 'allowQuery': True, 'mimeTypeInQuery': True})
|
|
pathDelimiter = '/'
|
|
sharedDriveId = sharedDriveName = ''
|
|
continueOnInvalidQuery = showSize = showSizeUnits = showLastModification = showMimeTypeSize = False
|
|
sizeField = 'quotaBytesUsed'
|
|
summary = FILECOUNT_SUMMARY_NONE
|
|
summaryUser = FILECOUNT_SUMMARY_USER
|
|
summaryMimeTypeInfo = {}
|
|
fileIdEntity = {}
|
|
addCSVData = {}
|
|
summaryLastModification = _initLastModification()
|
|
while Cmd.ArgumentsRemaining():
|
|
myarg = _getMain().getArgument()
|
|
if csvPF and myarg == 'todrive':
|
|
csvPF.GetTodriveParameters()
|
|
elif DLP.ProcessArgument(myarg, fileIdEntity):
|
|
pass
|
|
elif myarg == 'select':
|
|
if fileIdEntity:
|
|
_getMain().usageErrorExit(Msg.CAN_NOT_BE_SPECIFIED_MORE_THAN_ONCE.format('select'))
|
|
fileIdEntity = getSharedDriveEntity()
|
|
elif myarg == 'showsize':
|
|
showSize = True
|
|
elif myarg == 'showsizeunits':
|
|
showSizeUnits = True
|
|
elif myarg == 'showmimetypesize':
|
|
showMimeTypeSize = showSize = True
|
|
elif myarg == 'sizefield':
|
|
sizeField = _getMain().getChoice(SIZE_FIELD_CHOICE_MAP, mapChoice=True)
|
|
elif myarg == 'showlastmodification':
|
|
showLastModification = True
|
|
elif myarg == 'summary':
|
|
summary = _getMain().getChoice(FILECOUNT_SUMMARY_CHOICE_MAP, mapChoice=True)
|
|
elif myarg == 'summaryuser':
|
|
summaryUser = _getMain().getString(Cmd.OB_STRING)
|
|
elif myarg == 'pathdelimiter':
|
|
pathDelimiter = _getMain().getCharacter()
|
|
elif csvPF and myarg == 'addcsvdata':
|
|
_getMain().getAddCSVData(addCSVData)
|
|
elif myarg == 'continueoninvalidquery':
|
|
continueOnInvalidQuery = _getMain().getBoolean()
|
|
else:
|
|
_getMain().unknownArgumentExit()
|
|
if not fileIdEntity:
|
|
fileIdEntity = DLP.GetFileIdEntity()
|
|
if not fileIdEntity.get('shareddrive'):
|
|
btkwargs = DLP.kwargs
|
|
else:
|
|
btkwargs = fileIdEntity['shareddrive']
|
|
fieldsList.append('driveId')
|
|
DLP.Finalize(fileIdEntity)
|
|
if DLP.PM.permissionMatches:
|
|
getPermissionDetailsForMyDrive = DLP.PM.checkDetails
|
|
getPermissionsForSharedDrives = True
|
|
permissionsFields = f'nextPageToken,permissions({",".join(DLP.PM.permissionFields)})'
|
|
else:
|
|
getPermissionDetailsForMyDrive = getPermissionsForSharedDrives = False
|
|
_setSelectionFields()
|
|
if csvPF:
|
|
sortTitles = ['User', 'id', 'name', 'Total', 'Item cap'] if fileIdEntity.get('shareddrive') else ['User', 'Total']
|
|
if showSizeUnits:
|
|
sortTitles.insert(sortTitles.index('Total')+1, 'SizeUnits')
|
|
if showSize:
|
|
sortTitles.insert(sortTitles.index('Total')+1, 'Size')
|
|
if showLastModification:
|
|
sortTitles.extend(['lastModifiedFileId', 'lastModifiedFileName',
|
|
'lastModifiedFileMimeType', 'lastModifiedFilePath',
|
|
'lastModifyingUser', 'lastModifiedTime'])
|
|
if addCSVData:
|
|
sortTitles.extend(sorted(addCSVData.keys()))
|
|
csvPF.SetTitles(sortTitles)
|
|
csvPF.SetSortAllTitles()
|
|
pagesFields = _getMain().getItemFieldsFromFieldsList('files', fieldsList)
|
|
i, count, users = _getMain().getEntityArgument(users)
|
|
for user in users:
|
|
i += 1
|
|
user, drive = _validateUserSharedDrive(user, i, count, fileIdEntity)
|
|
if not drive:
|
|
continue
|
|
sharedDriveId = fileIdEntity.get('shareddrive', {}).get('driveId', '')
|
|
sharedDriveName = _getSharedDriveNameFromId(drive, sharedDriveId) if sharedDriveId else ''
|
|
mimeTypeInfo = {}
|
|
userLastModification = _initLastModification()
|
|
gettingEntity = _getGettingEntity(user, fileIdEntity)
|
|
_getMain().printGettingAllEntityItemsForWhom(Ent.DRIVE_FILE_OR_FOLDER, gettingEntity, i, count, query=DLP.fileIdEntity['query'])
|
|
try:
|
|
feed = _getMain().yieldGAPIpages(drive.files(), 'list', 'files',
|
|
pageMessage=_getMain().getPageMessageForWhom(),
|
|
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.INVALID_QUERY, GAPI.INVALID,
|
|
GAPI.BAD_REQUEST, GAPI.FILE_NOT_FOUND,
|
|
GAPI.NOT_FOUND, GAPI.TEAMDRIVE_MEMBERSHIP_REQUIRED],
|
|
retryReasons=[GAPI.UNKNOWN_ERROR],
|
|
q=DLP.fileIdEntity['query'],
|
|
fields=pagesFields, pageSize=GC.Values[GC.DRIVE_MAX_RESULTS], **btkwargs)
|
|
for files in feed:
|
|
for f_file in files:
|
|
driveId = f_file.get('driveId')
|
|
getCheckFilePermissions = _setGetCheckFilePermissions(f_file, getPermissionDetailsForMyDrive, getPermissionsForSharedDrives,
|
|
driveId, DLP)
|
|
if (not DLP.CheckShowOwnedBy(f_file) or
|
|
not DLP.CheckShowSharedByMe(f_file) or
|
|
not DLP.CheckExcludeTrashed(f_file) or
|
|
not DLP.CheckFileSize(f_file, sizeField) or
|
|
not DLP.CheckFilenameMatch(f_file) or
|
|
(not getCheckFilePermissions and not DLP.CheckFilePermissionMatches(f_file)) or
|
|
(DLP.onlySharedDrives and not driveId)):
|
|
continue
|
|
if getCheckFilePermissions:
|
|
try:
|
|
f_file['permissions'] = _getMain().callGAPIpages(drive.permissions(), 'list', 'permissions',
|
|
throwReasons=GAPI.DRIVE3_GET_ACL_REASONS+[GAPI.BAD_REQUEST],
|
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
fileId=f_file['id'], fields=permissionsFields, supportsAllDrives=True)
|
|
if not DLP.CheckFilePermissionMatches(f_file):
|
|
continue
|
|
for permission in f_file['permissions']:
|
|
permission.pop('teamDrivePermissionDetails', None)
|
|
except (GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError,
|
|
GAPI.insufficientAdministratorPrivileges, GAPI.insufficientFilePermissions,
|
|
GAPI.unknownError, GAPI.invalid, GAPI.badRequest,
|
|
GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy):
|
|
continue
|
|
mimeTypeInfo.setdefault(f_file['mimeType'], {'count': 0, 'size': 0})
|
|
mimeTypeInfo[f_file['mimeType']]['count'] += 1
|
|
mimeTypeInfo[f_file['mimeType']]['size'] += int(f_file.get(sizeField, '0'))
|
|
if showLastModification:
|
|
_checkUpdateLastModifiction(f_file, userLastModification)
|
|
_getLastModificationPath(drive, userLastModification, pathDelimiter)
|
|
showMimeTypeInfo(user, mimeTypeInfo, sharedDriveId, sharedDriveName, userLastModification, i, count)
|
|
if showLastModification and userLastModification['lastModifiedTime'] > summaryLastModification['lastModifiedTime']:
|
|
summaryLastModification = userLastModification.copy()
|
|
except (GAPI.invalidQuery, GAPI.invalid, GAPI.badRequest):
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER, None], _getMain().invalidQuery(DLP.fileIdEntity['query']), i, count)
|
|
if not continueOnInvalidQuery:
|
|
break
|
|
continue
|
|
except GAPI.fileNotFound:
|
|
_getMain().printGotEntityItemsForWhom(0)
|
|
except (GAPI.notFound, GAPI.teamDriveMembershipRequired) as e:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE_ID, sharedDriveId], str(e), i, count)
|
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
|
_getMain().userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
|
continue
|
|
if summary != FILECOUNT_SUMMARY_NONE:
|
|
showMimeTypeInfo(summaryUser, summaryMimeTypeInfo,
|
|
'' if count > 1 else sharedDriveId,
|
|
'' if count > 1 else sharedDriveName,
|
|
summaryLastModification, 0, 0)
|
|
if csvPF:
|
|
csvPF.writeCSVfile('Drive File Counts')
|
|
|
|
# gam <UserTypeEntity> print drivelastmodification [todrive <ToDriveAttribute>*]
|
|
# [select <SharedDriveEntity>]
|
|
# [pathdelimiter <Character>]
|
|
# (addcsvdata <FieldName> <String>)*
|
|
# gam <UserTypeEntity> show drivelastmodification
|
|
# [select <SharedDriveEntity>]
|
|
# [pathdelimiter <Character>]
|
|
def printShowDrivelastModifications(users):
|
|
def showLastModificationInfo(user, sharedDriveId, sharedDriveName, lastModification, i, count):
|
|
if not csvPF:
|
|
if sharedDriveId:
|
|
kvList = [Ent.USER, user, Ent.SHAREDDRIVE, f'{sharedDriveName} ({sharedDriveId})']
|
|
else:
|
|
kvList = [Ent.USER, user]
|
|
_getMain().printEntity(kvList, i, count)
|
|
Ind.Increment()
|
|
_showLastModification(lastModification)
|
|
Ind.Decrement()
|
|
else:
|
|
if sharedDriveId:
|
|
row = {'User': user, 'id': sharedDriveId, 'name': sharedDriveName}
|
|
else:
|
|
row = {'User': user}
|
|
_updateLastModificationRow(row, lastModification)
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
csvPF.WriteRowTitles(row)
|
|
|
|
csvPF = _getMain().CSVPrintFile() if Act.csvFormat() else None
|
|
fieldsList = ['id', 'driveId', 'name', 'mimeType', 'lastModifyingUser', 'modifiedTime', 'parents']
|
|
DLP = DriveListParameters({'allowChoose': False, 'allowCorpora': False, 'allowQuery': False, 'mimeTypeInQuery': True})
|
|
pathDelimiter = '/'
|
|
sharedDriveId = sharedDriveName = ''
|
|
fileIdEntity = {}
|
|
addCSVData = {}
|
|
while Cmd.ArgumentsRemaining():
|
|
myarg = _getMain().getArgument()
|
|
if csvPF and myarg == 'todrive':
|
|
csvPF.GetTodriveParameters()
|
|
elif myarg == 'select':
|
|
if fileIdEntity:
|
|
_getMain().usageErrorExit(Msg.CAN_NOT_BE_SPECIFIED_MORE_THAN_ONCE.format('select'))
|
|
fileIdEntity = getSharedDriveEntity()
|
|
elif myarg == 'pathdelimiter':
|
|
pathDelimiter = _getMain().getCharacter()
|
|
elif csvPF and myarg == 'addcsvdata':
|
|
_getMain().getAddCSVData(addCSVData)
|
|
else:
|
|
_getMain().unknownArgumentExit()
|
|
if not fileIdEntity:
|
|
fileIdEntity = DLP.GetFileIdEntity()
|
|
if not fileIdEntity.get('shareddrive'):
|
|
btkwargs = DLP.kwargs
|
|
else:
|
|
btkwargs = fileIdEntity['shareddrive']
|
|
fieldsList.append('driveId')
|
|
DLP.Finalize(fileIdEntity)
|
|
if csvPF:
|
|
sortTitles = ['User', 'id', 'name'] if fileIdEntity.get('shareddrive') else ['User']
|
|
if addCSVData:
|
|
sortTitles.extend(sorted(addCSVData.keys()))
|
|
sortTitles.extend(['lastModifiedFileId', 'lastModifiedFileName',
|
|
'lastModifiedFileMimeType', 'lastModifiedFilePath',
|
|
'lastModifyingUser', 'lastModifiedTime'])
|
|
csvPF.SetTitles(sortTitles)
|
|
csvPF.SetSortAllTitles()
|
|
pagesFields = _getMain().getItemFieldsFromFieldsList('files', fieldsList)
|
|
i, count, users = _getMain().getEntityArgument(users)
|
|
for user in users:
|
|
i += 1
|
|
user, drive = _validateUserSharedDrive(user, i, count, fileIdEntity)
|
|
if not drive:
|
|
continue
|
|
sharedDriveId = fileIdEntity.get('shareddrive', {}).get('driveId', '')
|
|
sharedDriveName = _getSharedDriveNameFromId(drive, sharedDriveId) if sharedDriveId else ''
|
|
userLastModification = _initLastModification()
|
|
gettingEntity = _getGettingEntity(user, fileIdEntity)
|
|
_getMain().printGettingAllEntityItemsForWhom(Ent.DRIVE_FILE_OR_FOLDER, gettingEntity, i, count)
|
|
try:
|
|
feed = _getMain().yieldGAPIpages(drive.files(), 'list', 'files',
|
|
pageMessage=_getMain().getPageMessageForWhom(),
|
|
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.INVALID, GAPI.BAD_REQUEST, GAPI.FILE_NOT_FOUND,
|
|
GAPI.NOT_FOUND, GAPI.TEAMDRIVE_MEMBERSHIP_REQUIRED],
|
|
retryReasons=[GAPI.UNKNOWN_ERROR],
|
|
fields=pagesFields, pageSize=GC.Values[GC.DRIVE_MAX_RESULTS], **btkwargs)
|
|
for files in feed:
|
|
for f_file in files:
|
|
_checkUpdateLastModifiction(f_file, userLastModification)
|
|
_getLastModificationPath(drive, userLastModification, pathDelimiter)
|
|
showLastModificationInfo(user, sharedDriveId, sharedDriveName, userLastModification, i, count)
|
|
except (GAPI.invalid, GAPI.badRequest) as e:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER, None], str(e), i, count)
|
|
continue
|
|
except GAPI.fileNotFound:
|
|
_getMain().printGotEntityItemsForWhom(0)
|
|
except (GAPI.notFound, GAPI.teamDriveMembershipRequired) as e:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE_ID, sharedDriveId], str(e), i, count)
|
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
|
_getMain().userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
|
continue
|
|
if csvPF:
|
|
csvPF.writeCSVfile('Drive File Last Modification')
|
|
|
|
DISKUSAGE_SHOW_CHOICES = {'all', 'summary', 'summaryandtrash'}
|
|
|
|
# gam <UserTypeEntity> print diskusage <DriveFileEntity> [todrive <ToDriveAttribute>*]
|
|
# [anyowner|(showownedby any|me|others)]
|
|
# [sizefield quotabytesused|size]
|
|
# [pathdelimiter <Character>] [excludetrashed] [stripcrsfromname]
|
|
# (addcsvdata <FieldName> <String>)*
|
|
# [noprogress] [show all|summary|summaryandtrash]
|
|
def printDiskUsage(users):
|
|
def _getChildDriveFolderInfo(drive, fileEntry, user, i, count, depth):
|
|
fileEntry['depth'] = depth
|
|
q = _getMain().WITH_PARENTS.format(fileEntry['id'])
|
|
try:
|
|
children = _getMain().callGAPIpages(drive.files(), 'list', 'files',
|
|
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.INVALID_QUERY, GAPI.INVALID],
|
|
retryReasons=[GAPI.UNKNOWN_ERROR],
|
|
q=q, orderBy=orderBy, fields=pagesFields,
|
|
pageSize=GC.Values[GC.DRIVE_MAX_RESULTS], supportsAllDrives=True, includeItemsFromAllDrives=True)
|
|
except (GAPI.invalidQuery, GAPI.invalid):
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FOLDER, None], _getMain().invalidQuery(q), i, count)
|
|
return
|
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
|
_getMain().userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
|
return
|
|
Ind.Increment()
|
|
if showProgress:
|
|
_getMain().entityActionPerformed([Ent.USER, user, Ent.DRIVE_FOLDER, fileEntry['path']])
|
|
for childEntryInfo in children:
|
|
trashed = childEntryInfo['trashed']
|
|
if trashed and excludeTrashed:
|
|
continue
|
|
mimeType = childEntryInfo.pop('mimeType')
|
|
if mimeType == MIMETYPE_GA_FOLDER:
|
|
fileEntry['directFolderCount'] += 1
|
|
fileEntry['totalFolderCount'] += 1
|
|
if trashed:
|
|
trashFolder['totalFolderCount'] += 1
|
|
if childEntryInfo['explicitlyTrashed']:
|
|
trashFolder['directFolderCount'] += 1
|
|
childEntryInfo['User'] = user
|
|
if includeOwner:
|
|
owners = childEntryInfo.pop('owners', [])
|
|
if owners:
|
|
childEntryInfo['Owner'] = owners[0].get('emailAddress', 'Unknown')
|
|
childEntryInfo.update(zeroFolderInfo)
|
|
if stripCRsFromName:
|
|
childEntryInfo['name'] = _stripControlCharsFromName(childEntryInfo['name'])
|
|
childEntryInfo['path'] = fileEntry['path']+pathDelimiter+childEntryInfo['name']
|
|
childEntryInfo.pop(sizeField, None)
|
|
foldersList.append(childEntryInfo)
|
|
_getChildDriveFolderInfo(drive, childEntryInfo, user, i, count, depth+1)
|
|
fileEntry['totalFileCount'] += childEntryInfo['totalFileCount']
|
|
fileEntry['totalFileSize'] += childEntryInfo['totalFileSize']
|
|
fileEntry['totalFolderCount'] += childEntryInfo['totalFolderCount']
|
|
elif mimeType != MIMETYPE_GA_SHORTCUT:
|
|
if includeOwner and showOwnedBy is not None and childEntryInfo['ownedByMe'] != showOwnedBy:
|
|
continue
|
|
fsize = int(childEntryInfo.get(sizeField, '0'))
|
|
fileEntry['directFileCount'] += 1
|
|
fileEntry['directFileSize'] += fsize
|
|
fileEntry['totalFileCount'] += 1
|
|
fileEntry['totalFileSize'] += fsize
|
|
if trashed:
|
|
trashFolder['totalFileCount'] += 1
|
|
trashFolder['totalFileSize'] += fsize
|
|
if childEntryInfo['explicitlyTrashed']:
|
|
trashFolder['directFileCount'] += 1
|
|
trashFolder['directFileSize'] += fsize
|
|
Ind.Decrement()
|
|
|
|
csvPF = _getMain().CSVPrintFile(['User', 'Owner', 'id', 'name', 'ownedByMe', 'trashed', 'explicitlyTrashed',
|
|
'directFileCount', 'directFileSize', 'directFolderCount',
|
|
'totalFileCount', 'totalFileSize', 'totalFolderCount', 'depth', 'path'])
|
|
excludeTrashed = stripCRsFromName = False
|
|
includeOwner = True
|
|
orderBy = 'folder,name'
|
|
zeroFolderInfo = {'directFileCount': 0, 'directFileSize': 0, 'directFolderCount': 0,
|
|
'totalFileCount': 0, 'totalFileSize': 0, 'totalFolderCount': 0}
|
|
sizeField = 'quotaBytesUsed'
|
|
showOwnedBy = showProgress = True
|
|
pathDelimiter = '/'
|
|
fileIdEntity = getDriveFileEntity()
|
|
addCSVData = {}
|
|
showResults = 'all'
|
|
while Cmd.ArgumentsRemaining():
|
|
myarg = _getMain().getArgument()
|
|
if myarg == 'todrive':
|
|
csvPF.GetTodriveParameters()
|
|
elif myarg == 'anyowner':
|
|
showOwnedBy = None
|
|
elif myarg == 'showownedby':
|
|
showOwnedBy = _getMain().getChoice(SHOW_OWNED_BY_CHOICE_MAP, mapChoice=True)
|
|
elif myarg == 'sizefield':
|
|
sizeField = _getMain().getChoice(SIZE_FIELD_CHOICE_MAP, mapChoice=True)
|
|
elif myarg == 'pathdelimiter':
|
|
pathDelimiter = _getMain().getCharacter()
|
|
elif myarg == 'excludetrashed':
|
|
excludeTrashed = True
|
|
elif myarg == 'stripcrsfromname':
|
|
stripCRsFromName = True
|
|
elif myarg == 'addcsvdata':
|
|
_getMain().getAddCSVData(addCSVData)
|
|
elif myarg == 'show':
|
|
showResults = _getMain().getChoice(DISKUSAGE_SHOW_CHOICES)
|
|
elif myarg == 'noprogress':
|
|
showProgress = False
|
|
else:
|
|
_getMain().unknownArgumentExit()
|
|
if addCSVData:
|
|
csvPF.AddTitles(sorted(addCSVData.keys()))
|
|
fieldsList = ['id', 'name', 'mimeType', sizeField, 'trashed', 'explicitlyTrashed', 'owners(emailAddress)', 'ownedByMe']
|
|
pagesFields = _getMain().getItemFieldsFromFieldsList('files', fieldsList)
|
|
topFieldsList = fieldsList[:]
|
|
topFieldsList.extend(['driveId', 'parents'])
|
|
topFields = _getMain().getFieldsFromFieldsList(topFieldsList)
|
|
i, count, users = _getMain().getEntityArgument(users)
|
|
i = 0
|
|
for user in users:
|
|
i += 1
|
|
origUser = user
|
|
user, drive, jcount = _validateUserGetFileIDs(origUser, i, count, fileIdEntity, entityType=Ent.DRIVE_DISK_USAGE)
|
|
if jcount == 0:
|
|
continue
|
|
j = 0
|
|
for fileId in fileIdEntity['list']:
|
|
j += 1
|
|
foldersList = []
|
|
trashFolder = {'User': user, 'id': 'Trash', 'name': 'Trash', 'path': 'Trash'}
|
|
trashFolder.update(zeroFolderInfo)
|
|
try:
|
|
topFolder = _getMain().callGAPI(drive.files(), 'get',
|
|
throwReasons=GAPI.DRIVE_GET_THROW_REASONS,
|
|
fileId=fileId, fields=topFields, supportsAllDrives=True)
|
|
if stripCRsFromName:
|
|
topFolder['name'] = _stripControlCharsFromName(topFolder['name'])
|
|
mimeType = topFolder.pop('mimeType')
|
|
if mimeType != MIMETYPE_GA_FOLDER:
|
|
entityValueList = [Ent.USER, user, _getMain()._getEntityMimeType(topFolder), topFolder['name']]
|
|
_getMain().entityActionNotPerformedWarning(entityValueList, Msg.INVALID_MIMETYPE.format(mimeType, MIMETYPE_GA_FOLDER), i, count)
|
|
continue
|
|
if topFolder['trashed']:
|
|
if excludeTrashed:
|
|
entityValueList = [Ent.USER, user, Ent.DRIVE_FOLDER, topFolder['name']]
|
|
_getMain().entityActionNotPerformedWarning(entityValueList, Msg.IN_TRASH_AND_EXCLUDE_TRASHED, i, count)
|
|
continue
|
|
trashFolder['totalFolderCount'] += 1
|
|
if topFolder['explicitlyTrashed']:
|
|
trashFolder['directFolderCount'] += 1
|
|
driveId = topFolder.pop('driveId', None)
|
|
if driveId:
|
|
includeOwner = False
|
|
csvPF.RemoveTitles(['Owner', 'ownedByMe'])
|
|
if topFolder['name'] == TEAM_DRIVE and not topFolder.get('parents'):
|
|
topFolder['name'] = _getSharedDriveNameFromId(drive, driveId)
|
|
topFolder['path'] = f'{SHARED_DRIVES}{pathDelimiter}{topFolder["name"]}'
|
|
else:
|
|
topFolder['path'] = topFolder['name']
|
|
topFolder.pop('ownedByMe', None)
|
|
elif topFolder['name'] == MY_DRIVE and not topFolder.get('parents'):
|
|
topFolder['path'] = _getMain().MY_DRIVE
|
|
else:
|
|
topFolder['path'] = topFolder['name']
|
|
topFolder['User'] = user
|
|
if includeOwner:
|
|
owners = topFolder.pop('owners', [])
|
|
if owners:
|
|
topFolder['Owner'] = owners[0].get('emailAddress', 'Unknown')
|
|
trashFolder['Owner'] = topFolder['Owner']
|
|
topFolder.pop('parents', None)
|
|
topFolder.update(zeroFolderInfo)
|
|
topFolder.pop(sizeField, None)
|
|
foldersList.append(topFolder)
|
|
_getChildDriveFolderInfo(drive, topFolder, user, i, count, -1)
|
|
except GAPI.fileNotFound:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FOLDER, fileId], Msg.NOT_FOUND, j, jcount)
|
|
continue
|
|
except (GAPI.notFound, GAPI.teamDriveMembershipRequired) as e:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE_ID, fileIdEntity['shareddrive']['driveId']], str(e), j, jcount)
|
|
continue
|
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
|
_getMain().userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
|
break
|
|
if showResults == 'all':
|
|
for folder in foldersList:
|
|
if addCSVData:
|
|
folder.update(addCSVData)
|
|
csvPF.WriteRow(folder)
|
|
else:
|
|
folder = foldersList[0]
|
|
if addCSVData:
|
|
folder.update(addCSVData)
|
|
csvPF.WriteRow(folder)
|
|
if showResults != 'summary' and not excludeTrashed:
|
|
trashFolder['trashed'] = trashFolder['totalFileCount']+trashFolder['totalFolderCount'] > 0
|
|
trashFolder['explicitlyTrashed'] = trashFolder['directFileCount']+trashFolder['directFolderCount'] > 0
|
|
if addCSVData:
|
|
trashFolder.update(addCSVData)
|
|
trashFolder['depth'] = -1
|
|
csvPF.WriteRow(trashFolder)
|
|
csvPF.writeCSVfile('Drive Disk Usage')
|
|
|
|
FILESHARECOUNTS_OWNER = 'Owner'
|
|
FILESHARECOUNTS_TOTAL = 'Total'
|
|
FILESHARECOUNTS_SHARED = 'Shared'
|
|
FILESHARECOUNTS_SHARED_EXTERNAL = 'Shared External'
|
|
FILESHARECOUNTS_SHARED_INTERNAL = 'Shared Internal'
|
|
|
|
FILESHARECOUNTS_ZEROCOUNTS = {
|
|
FILESHARECOUNTS_TOTAL: 0,
|
|
FILESHARECOUNTS_SHARED: 0,
|
|
FILESHARECOUNTS_SHARED_EXTERNAL: 0,
|
|
FILESHARECOUNTS_SHARED_INTERNAL: 0,
|
|
'anyone': 0, 'anyoneWithLink': 0,
|
|
'externalDomain': 0, 'externalDomainWithLink': 0,
|
|
'internalDomain': 0, 'internalDomainWithLink': 0,
|
|
'externalGroup': 0, 'internalGroup': 0,
|
|
'externalUser': 0, 'internalUser': 0,
|
|
'deletedGroup': 0, 'deletedUser': 0,
|
|
}
|
|
|
|
FILESHARECOUNTS_CATEGORIES = {
|
|
'anyone': {False: 'anyone', True: 'anyoneWithLink'},
|
|
'domain': {False: {False: 'externalDomain', True: 'externalDomainWithLink'}, True: {False: 'internalDomain', True: 'internalDomainWithLink'}},
|
|
'group': {False: 'externalGroup', True: 'internalGroup'},
|
|
'user': {False: 'externalUser', True: 'internalUser'},
|
|
'deleted': {'group': 'deletedGroup', 'user': 'deletedUser'},
|
|
}
|
|
|
|
# gam <UserTypeEntity> print filesharecounts [todrive <ToDriveAttribute>*]
|
|
# [excludetrashed]
|
|
# [internaldomains all|primary|<DomainNameList>]
|
|
# [summary none|only|plus] [summaryuser <String>]
|
|
# gam <UserTypeEntity> show filesharecounts
|
|
# [excludetrashed]
|
|
# [internaldomains all|primary|<DomainNameList>]
|
|
# [summary none|only|plus] [summaryuser <String>]
|
|
def printShowFileShareCounts(users):
|
|
def incrementCounter(counter):
|
|
if not counterSet[counter]:
|
|
userShareCounts[counter] += 1
|
|
counterSet[counter] = True
|
|
|
|
def showShareCounts(user, shareCounts, i, count):
|
|
if summary != FILECOUNT_SUMMARY_NONE:
|
|
if count != 0:
|
|
for field, shareCount in shareCounts.items():
|
|
summaryShareCounts[field] += shareCount
|
|
if summary == FILECOUNT_SUMMARY_ONLY:
|
|
return
|
|
if not csvPF:
|
|
_getMain().printEntity([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER, shareCounts[FILESHARECOUNTS_TOTAL]], i, count)
|
|
Ind.Increment()
|
|
for field, shareCount in shareCounts.items():
|
|
_getMain().printKeyValueList([field, shareCount])
|
|
Ind.Decrement()
|
|
else:
|
|
row = {FILESHARECOUNTS_OWNER: user}
|
|
row.update(shareCounts)
|
|
csvPF.WriteRow(row)
|
|
|
|
cd = _getMain().buildGAPIObject(API.DIRECTORY)
|
|
csvPF = _getMain().CSVPrintFile([FILESHARECOUNTS_OWNER]+list(FILESHARECOUNTS_ZEROCOUNTS.keys())) if Act.csvFormat() else None
|
|
query = ME_IN_OWNERS
|
|
summary = FILECOUNT_SUMMARY_NONE
|
|
summaryUser = FILECOUNT_SUMMARY_USER
|
|
fileIdEntity = {}
|
|
internalDomains = 'all'
|
|
while Cmd.ArgumentsRemaining():
|
|
myarg = _getMain().getArgument()
|
|
if csvPF and myarg == 'todrive':
|
|
csvPF.GetTodriveParameters()
|
|
elif myarg == 'excludetrashed':
|
|
query += ' and trashed=false'
|
|
elif myarg == 'internaldomains':
|
|
internalDomains = _getMain().getString(Cmd.OB_DOMAIN_NAME_LIST).replace(',', ' ').lower()
|
|
elif myarg == 'summary':
|
|
summary = _getMain().getChoice(FILECOUNT_SUMMARY_CHOICE_MAP, mapChoice=True)
|
|
elif myarg == 'summaryuser':
|
|
summaryUser = _getMain().getString(Cmd.OB_STRING)
|
|
else:
|
|
_getMain().unknownArgumentExit()
|
|
internalDomains = _getMain().finalizeInternalDomains(cd, internalDomains)
|
|
summaryShareCounts = FILESHARECOUNTS_ZEROCOUNTS.copy()
|
|
i, count, users = _getMain().getEntityArgument(users)
|
|
for user in users:
|
|
i += 1
|
|
user, drive = _validateUserSharedDrive(user, i, count, fileIdEntity)
|
|
if not drive:
|
|
continue
|
|
userShareCounts = FILESHARECOUNTS_ZEROCOUNTS.copy()
|
|
gettingEntity = _getGettingEntity(user, fileIdEntity)
|
|
_getMain().printGettingAllEntityItemsForWhom(Ent.DRIVE_FILE_OR_FOLDER, gettingEntity, i, count, query=query)
|
|
try:
|
|
feed = _getMain().yieldGAPIpages(drive.files(), 'list', 'files',
|
|
pageMessage=_getMain().getPageMessageForWhom(),
|
|
throwReasons=GAPI.DRIVE_USER_THROW_REASONS,
|
|
retryReasons=[GAPI.UNKNOWN_ERROR],
|
|
q=query, fields='nextPageToken,files(permissions(type,role,emailAddress,domain,allowFileDiscovery,deleted))',
|
|
pageSize=GC.Values[GC.DRIVE_MAX_RESULTS])
|
|
for files in feed:
|
|
for f_file in files:
|
|
counterSet = {FILESHARECOUNTS_TOTAL: False, FILESHARECOUNTS_SHARED: False,
|
|
FILESHARECOUNTS_SHARED_EXTERNAL: False, FILESHARECOUNTS_SHARED_INTERNAL: False}
|
|
for permission in f_file['permissions']:
|
|
if permission['role'] == 'owner':
|
|
incrementCounter(FILESHARECOUNTS_TOTAL)
|
|
else:
|
|
incrementCounter(FILESHARECOUNTS_SHARED)
|
|
ptype = permission['type']
|
|
if ptype == 'anyone':
|
|
incrementCounter(FILESHARECOUNTS_SHARED_EXTERNAL)
|
|
userShareCounts[FILESHARECOUNTS_CATEGORIES[ptype][not permission['allowFileDiscovery']]] += 1
|
|
else:
|
|
domain = permission.get('domain', '')
|
|
if not domain and ptype in ['user', 'group']:
|
|
if permission.get('deleted', False):
|
|
userShareCounts[FILESHARECOUNTS_CATEGORIES['deleted'][ptype]] += 1
|
|
continue
|
|
emailAddress = permission['emailAddress']
|
|
domain = emailAddress[emailAddress.find('@')+1:]
|
|
internal = domain in internalDomains
|
|
incrementCounter([FILESHARECOUNTS_SHARED_EXTERNAL, FILESHARECOUNTS_SHARED_INTERNAL][internal])
|
|
if ptype == 'domain':
|
|
userShareCounts[FILESHARECOUNTS_CATEGORIES[ptype][internal][not permission['allowFileDiscovery']]] +=1
|
|
else: # group, user
|
|
userShareCounts[FILESHARECOUNTS_CATEGORIES[ptype][internal]] += 1
|
|
showShareCounts(user, userShareCounts, i, count)
|
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
|
_getMain().userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
|
continue
|
|
if summary != FILECOUNT_SUMMARY_NONE:
|
|
showShareCounts(summaryUser, summaryShareCounts, 0, 0)
|
|
if csvPF:
|
|
csvPF.writeCSVfile('Drive File Share Counts')
|
|
|
|
FILETREE_FIELDS_CHOICE_MAP = {
|
|
'explicitlytrashed': 'explicitlyTrashed',
|
|
'filesize': 'size',
|
|
'id': 'id',
|
|
'mime': 'mimeType',
|
|
'mimetype': 'mimeType',
|
|
'owners': 'owners',
|
|
'parents': 'parents',
|
|
'size': 'size',
|
|
'trashed': 'trashed',
|
|
'webviewlink': 'webViewLink',
|
|
}
|
|
|
|
FILETREE_FIELDS_PRINT_ORDER = ['id', 'parents', 'owners', 'mimeType', 'size', 'explicitlyTrashed', 'trashed', 'webViewLink']
|
|
|
|
# gam <UserTypeEntity> print filetree [todrive <ToDriveAttribute>*]
|
|
# [select <DriveFileEntity> [selectsubquery <QueryDriveFile>] [depth <Number>]]
|
|
# [anyowner|(showownedby any|me|others)]
|
|
# [showmimetype [not] <MimeTypeList>] [showmimetype category <MimeTypeNameList>]
|
|
# [sizefield quotabytesused|size] [minimumfilesize <Integer>] [maximumfilesize <Integer>]
|
|
# [filenamematchpattern <REMatchPattern>]
|
|
# <PermissionMatch>* [<PermissionMatchMode>] [<PermissionMatchAction>]
|
|
# [excludetrashed]
|
|
# [fields <FileTreeFieldNameList>]
|
|
# (orderby <DriveFileOrderByFieldName> [ascending|descending])* [delimiter <Character>]
|
|
# [noindent] [stripcrsfromname]
|
|
# gam <UserTypeEntity> show filetree
|
|
# [select <DriveFileEntity> [selectsubquery <QueryDriveFile>] [depth <Number>]]
|
|
# [anyowner|(showownedby any|me|others)]
|
|
# [showmimetype [not] <MimeTypeList>] [showmimetype category <MimeTypeNameList>]
|
|
# [sizefield quotabytesused|size] [minimumfilesize <Integer>] [maximumfilesize <Integer>]
|
|
# [filenamematchpattern <REMatchPattern>]
|
|
# <PermissionMatch>* [<PermissionMatchMode>] [<PermissionMatchAction>]
|
|
# [excludetrashed]
|
|
# [fields <FileTreeFieldNameList>]
|
|
# (orderby <DriveFileOrderByFieldName> [ascending|descending])* [delimiter <Character>]
|
|
# [stripcrsfromname]
|
|
def printShowFileTree(users):
|
|
def _showFileInfo(fileEntry, depth, j=0, jcount=0):
|
|
if not DLP.CheckExcludeTrashed(fileEntry):
|
|
return
|
|
if stripCRsFromName:
|
|
fileEntry['name'] = _stripControlCharsFromName(fileEntry['name'])
|
|
if not csvPF:
|
|
fileInfoList = []
|
|
for field in FILETREE_FIELDS_PRINT_ORDER:
|
|
if showFields[field]:
|
|
if field == 'parents':
|
|
parents = fileEntry.get(field, [])
|
|
fileInfoList.extend([field, f'{len(parents)} [{delimiter.join(parents)}]'])
|
|
elif field == 'owners':
|
|
owners = [owner['emailAddress'] for owner in fileEntry.get(field, [])]
|
|
if owners:
|
|
fileInfoList.extend([field, delimiter.join(owners)])
|
|
elif field in {'explicitlyTrashed', 'trashed'}:
|
|
trashed = fileEntry.get(field, False)
|
|
if trashed:
|
|
fileInfoList.extend([field, trashed])
|
|
elif field == 'size':
|
|
fileInfoList.extend([field, fileEntry.get(sizeField, 0)])
|
|
else:
|
|
fileInfoList.extend([field, fileEntry.get(field, '')])
|
|
if fileInfoList:
|
|
_getMain().printKeyValueListWithCount([fileEntry['name'], formatKeyValueList('(', fileInfoList, ')')], j, jcount)
|
|
else:
|
|
_getMain().printKeyValueList([fileEntry['name']])
|
|
else:
|
|
userInfo['index'] += 1
|
|
row = userInfo.copy()
|
|
row['depth'] = depth
|
|
row['name'] = ('' if noindent else Ind.SpacesSub1())+fileEntry['name']
|
|
for field in FILETREE_FIELDS_PRINT_ORDER:
|
|
if showFields[field]:
|
|
if field == 'parents':
|
|
row[field] = delimiter.join(fileEntry.get(field, []))
|
|
elif field == 'owners':
|
|
row[field] = delimiter.join([owner['emailAddress'] for owner in fileEntry.get(field, [])])
|
|
elif field == 'size':
|
|
row['size'] = fileEntry.get(sizeField, 0)
|
|
else:
|
|
row[field] = fileEntry.get(field, '')
|
|
csvPF.WriteRow(row)
|
|
|
|
def _showDriveFolderContents(fileEntry, depth):
|
|
for childId in fileEntry['children']:
|
|
childEntry = fileTree.get(childId)
|
|
if childEntry:
|
|
if not DLP.CheckExcludeTrashed(childEntry['info']):
|
|
continue
|
|
if (DLP.CheckMimeType(childEntry['info']) and
|
|
DLP.CheckFileSize(childEntry['info'], sizeField) and
|
|
DLP.CheckFilenameMatch(childEntry['info']) and
|
|
DLP.CheckFilePermissionMatches(childEntry['info'])):
|
|
_showFileInfo(childEntry['info'], depth)
|
|
if childEntry['info']['mimeType'] == MIMETYPE_GA_FOLDER and (maxdepth == -1 or depth < maxdepth):
|
|
Ind.Increment()
|
|
_showDriveFolderContents(childEntry, depth+1)
|
|
Ind.Decrement()
|
|
|
|
def _showChildDriveFolderContents(drive, fileEntry, user, i, count, depth):
|
|
if not DLP.CheckExcludeTrashed(fileEntry):
|
|
return
|
|
q = _getMain().WITH_PARENTS.format(fileEntry['id'])
|
|
if selectSubQuery:
|
|
q += ' and ('+selectSubQuery+')'
|
|
try:
|
|
children = _getMain().callGAPIpages(drive.files(), 'list', 'files',
|
|
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.INVALID_QUERY, GAPI.INVALID],
|
|
retryReasons=[GAPI.UNKNOWN_ERROR],
|
|
q=q, orderBy=OBY.orderBy, fields=pagesFields,
|
|
pageSize=GC.Values[GC.DRIVE_MAX_RESULTS], supportsAllDrives=True, includeItemsFromAllDrives=True)
|
|
except (GAPI.invalidQuery, GAPI.invalid):
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE, None], _getMain().invalidQuery(selectSubQuery), i, count)
|
|
return
|
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
|
_getMain().userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
|
return
|
|
for childEntryInfo in children:
|
|
if not DLP.CheckExcludeTrashed(childEntryInfo):
|
|
continue
|
|
if (DLP.CheckShowOwnedBy(childEntryInfo) and
|
|
DLP.CheckMimeType(childEntryInfo) and
|
|
DLP.CheckFileSize(childEntryInfo, sizeField) and
|
|
DLP.CheckFilenameMatch(childEntryInfo) and
|
|
DLP.CheckFilePermissionMatches(childEntryInfo)):
|
|
_showFileInfo(childEntryInfo, depth)
|
|
if childEntryInfo['mimeType'] == MIMETYPE_GA_FOLDER and (maxdepth == -1 or depth < maxdepth):
|
|
Ind.Increment()
|
|
_showChildDriveFolderContents(drive, childEntryInfo, user, i, count, depth+1)
|
|
Ind.Decrement()
|
|
|
|
csvPF = _getMain().CSVPrintFile(['User', 'index', 'depth', 'name']) if Act.csvFormat() else None
|
|
maxdepth = -1
|
|
fileIdEntity = {}
|
|
selectSubQuery = ''
|
|
sizeField = 'quotaBytesUsed'
|
|
showFields = {}
|
|
for mappedField in FILETREE_FIELDS_CHOICE_MAP.values():
|
|
showFields[mappedField] = False
|
|
buildTree = noindent = stripCRsFromName = False
|
|
delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER]
|
|
OBY = _getMain().OrderBy(DRIVEFILE_ORDERBY_CHOICE_MAP)
|
|
DLP = DriveListParameters({'allowChoose': False, 'allowCorpora': False, 'allowQuery': False, 'mimeTypeInQuery': False})
|
|
while Cmd.ArgumentsRemaining():
|
|
myarg = _getMain().getArgument()
|
|
if csvPF and myarg == 'todrive':
|
|
csvPF.GetTodriveParameters()
|
|
elif DLP.ProcessArgument(myarg, fileIdEntity):
|
|
pass
|
|
elif myarg == 'select':
|
|
if fileIdEntity:
|
|
_getMain().usageErrorExit(Msg.CAN_NOT_BE_SPECIFIED_MORE_THAN_ONCE.format('select'))
|
|
fileIdEntity = getDriveFileEntity(DLP=DLP)
|
|
elif myarg == 'selectsubquery':
|
|
selectSubQuery = _getMain().getString(Cmd.OB_QUERY, minLen=0)
|
|
elif myarg == 'orderby':
|
|
OBY.GetChoice()
|
|
elif myarg == 'depth':
|
|
maxdepth = _getMain().getInteger(minVal=-1)
|
|
elif myarg == 'sizefield':
|
|
sizeField = _getMain().getChoice(SIZE_FIELD_CHOICE_MAP, mapChoice=True)
|
|
elif myarg == 'fields':
|
|
for field in _getMain()._getFieldsList():
|
|
if field in FILETREE_FIELDS_CHOICE_MAP:
|
|
showFields[FILETREE_FIELDS_CHOICE_MAP[field]] = True
|
|
if csvPF:
|
|
csvPF.AddTitle(FILETREE_FIELDS_CHOICE_MAP[field])
|
|
else:
|
|
_getMain().invalidChoiceExit(field, FILETREE_FIELDS_CHOICE_MAP, True)
|
|
elif myarg == 'delimiter':
|
|
delimiter = _getMain().getCharacter()
|
|
elif csvPF and myarg == 'noindent':
|
|
noindent = True
|
|
elif myarg == 'stripcrsfromname':
|
|
stripCRsFromName = True
|
|
else:
|
|
_getMain().unknownArgumentExit()
|
|
fieldsList = ['driveId', 'id', 'name', 'parents', 'mimeType', 'ownedByMe', 'owners(emailAddress)',
|
|
'shared', sizeField, 'explicitlyTrashed', 'trashed', 'webViewLink']
|
|
buildTree = (not fileIdEntity
|
|
or (not fileIdEntity['dict']
|
|
and not fileIdEntity['query']
|
|
and not fileIdEntity['shareddrivefilequery']
|
|
and _simpleFileIdEntityList(fileIdEntity['list'])))
|
|
if buildTree:
|
|
if not fileIdEntity:
|
|
fileIdEntity = initDriveFileEntity()
|
|
DLP.GetFileIdEntity()
|
|
if not fileIdEntity.get('shareddrive'):
|
|
btkwargs = DLP.kwargs
|
|
btkwargs['q'] = DLP.fileIdEntity['query']
|
|
cleanFileIDsList(fileIdEntity, [ROOT, ORPHANS])
|
|
else:
|
|
btkwargs = fileIdEntity['shareddrive']
|
|
DLP.Finalize(fileIdEntity)
|
|
elif not fileIdEntity:
|
|
fileIdEntity = initDriveFileEntity()
|
|
if DLP.PM.permissionMatches:
|
|
fieldsList.append('permissions')
|
|
fields = _getMain().getFieldsFromFieldsList(fieldsList)
|
|
pagesFields = _getMain().getItemFieldsFromFieldsList('files', fieldsList)
|
|
shareddriveFields = []
|
|
i, count, users = _getMain().getEntityArgument(users)
|
|
for user in users:
|
|
i += 1
|
|
origUser = user
|
|
user, drive = _validateUserSharedDrive(user, i, count, fileIdEntity)
|
|
if not drive:
|
|
continue
|
|
if buildTree:
|
|
fileTree, status = initFileTree(drive, fileIdEntity.get('shareddrive'), DLP, shareddriveFields, True, user, i, count)
|
|
if not status:
|
|
continue
|
|
gettingEntity = _getGettingEntity(user, fileIdEntity)
|
|
_getMain().printGettingAllEntityItemsForWhom(Ent.DRIVE_FILE_OR_FOLDER, gettingEntity, i, count, query=DLP.fileIdEntity['query'])
|
|
try:
|
|
feed = _getMain().yieldGAPIpages(drive.files(), 'list', 'files',
|
|
pageMessage=_getMain().getPageMessageForWhom(),
|
|
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.TEAMDRIVE_MEMBERSHIP_REQUIRED],
|
|
retryReasons=[GAPI.UNKNOWN_ERROR],
|
|
orderBy=OBY.orderBy,
|
|
fields=pagesFields, pageSize=GC.Values[GC.DRIVE_MAX_RESULTS], **btkwargs)
|
|
for files in feed:
|
|
extendFileTree(fileTree, files, DLP, stripCRsFromName)
|
|
extendFileTreeParents(drive, fileTree, fields)
|
|
DLP.GetLocationFileIdsFromTree(fileTree, fileIdEntity)
|
|
except (GAPI.notFound, GAPI.teamDriveMembershipRequired) as e:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE_ID, fileIdEntity['shareddrive']['driveId']], str(e), i, count)
|
|
continue
|
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
|
_getMain().userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
|
continue
|
|
else:
|
|
fileTree = {}
|
|
user, drive, jcount = _validateUserGetFileIDs(origUser, i, count, fileIdEntity, drive=drive, entityType=Ent.DRIVE_FILE_OR_FOLDER)
|
|
if jcount == 0:
|
|
continue
|
|
if csvPF:
|
|
userInfo = {'User': user, 'index': 0, 'depth': 0, 'name': ''}
|
|
j = 0
|
|
Ind.Increment()
|
|
for fileId in fileIdEntity['list']:
|
|
j += 1
|
|
fileEntry = fileTree.get(fileId)
|
|
if fileEntry:
|
|
fileEntryInfo = fileEntry['info']
|
|
else:
|
|
try:
|
|
fileEntryInfo = _getMain().callGAPI(drive.files(), 'get',
|
|
throwReasons=GAPI.DRIVE_GET_THROW_REASONS,
|
|
fileId=fileId, fields=fields, supportsAllDrives=True)
|
|
if (fileEntryInfo['mimeType'] == MIMETYPE_GA_FOLDER and fileEntryInfo.get('driveId') and
|
|
fileEntryInfo['name'] == TEAM_DRIVE and not fileEntryInfo.get('parents', [])):
|
|
fileEntryInfo['name'] = f"{SHARED_DRIVES}/{_getSharedDriveNameFromId(drive, fileId)}"
|
|
if stripCRsFromName:
|
|
fileEntryInfo['name'] = _stripControlCharsFromName(fileEntryInfo['name'])
|
|
if buildTree:
|
|
fileTree[fileId] = {'info': fileEntryInfo}
|
|
except GAPI.fileNotFound:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER, fileId], Msg.NOT_FOUND, j, jcount)
|
|
continue
|
|
except (GAPI.notFound, GAPI.teamDriveMembershipRequired) as e:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE_ID, fileIdEntity['shareddrive']['driveId']], str(e), j, jcount)
|
|
continue
|
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
|
_getMain().userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
|
break
|
|
_showFileInfo(fileEntryInfo, -1, j, jcount)
|
|
Ind.Increment()
|
|
if buildTree:
|
|
_showDriveFolderContents(fileEntry, 0)
|
|
else:
|
|
_showChildDriveFolderContents(drive, fileEntryInfo, user, i, count, 0)
|
|
Ind.Decrement()
|
|
Ind.Decrement()
|
|
if csvPF:
|
|
csvPF.writeCSVfile('Drive File Tree')
|
|
|
|
def getCreationModificationTimes(path_to_file):
|
|
"""
|
|
Try to get the date that a file was created, falling back to when it was
|
|
last modified if that isn't possible.
|
|
See http://stackoverflow.com/a/39501288/1709587 for explanation.
|
|
"""
|
|
mtime = os.path.getmtime(path_to_file)
|
|
if platform.system() == 'Windows':
|
|
ctime = os.path.getctime(path_to_file)
|
|
else:
|
|
stat = os.stat(path_to_file)
|
|
try:
|
|
ctime = stat.st_birthtime
|
|
except AttributeError:
|
|
# We're probably on Linux. No easy way to get creation dates here,
|
|
# so we'll settle for when its content was last modified.
|
|
ctime = stat.st_mtime
|
|
return (_getMain().formatLocalSecondsTimestamp(ctime), _getMain().formatLocalSecondsTimestamp(mtime))
|
|
|
|
def writeReturnIdLink(returnIdLink, mimeType, result):
|
|
if returnIdLink != 'editLink':
|
|
_getMain().writeStdout(f'{result[returnIdLink]}\n')
|
|
return
|
|
if mimeType is None:
|
|
_getMain().writeStdout(f'{result["webViewLink"]}\n')
|
|
return
|
|
for mt in MICROSOFT_FORMATS_LIST:
|
|
if mimeType == mt['mime']:
|
|
if mt['ext'][1] == 'd':
|
|
_getMain().writeStdout(f'https://docs.google.com/document/d/{result["id"]}/edit\n')
|
|
return
|
|
if mt['ext'][1] == 'x':
|
|
_getMain().writeStdout(f'https://docs.google.com/spreadsheets/d/{result["id"]}/edit\n')
|
|
return
|
|
if mt['ext'][1] == 'p':
|
|
_getMain().writeStdout(f'https://docs.google.com/presentation/d/{result["id"]}/edit\n')
|
|
return
|
|
_getMain().writeStdout(f'https://drive.google.com/file/d/{result["id"]}/edit\n')
|
|
|