Compare commits

...

4 Commits

Author SHA1 Message Date
Ross Scroggs
677f683377 Updated gam report gmail start/end time defaults 2025-12-23 14:28:54 -08:00
Ross Scroggs
85e90ec56c Updated gam report <ActivityApplicationName>. #1869
Some checks failed
Build and test GAM / build (false, build, 1, Build Intel Ubuntu Jammy, ubuntu-22.04) (push) Has been cancelled
Build and test GAM / build (false, build, 10, Build x86_64 macOS 15, macos-15-intel) (push) Has been cancelled
Build and test GAM / build (false, build, 11, Build Arm MacOS 26, macos-26) (push) Has been cancelled
Build and test GAM / build (false, build, 12, Build Intel Windows, windows-2025) (push) Has been cancelled
Build and test GAM / build (false, build, 13, Build Arm Windows, windows-11-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 2, Build Intel Ubuntu Noble, ubuntu-24.04) (push) Has been cancelled
Build and test GAM / build (false, build, 3, Build Arm Ubuntu Noble, ubuntu-24.04-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 4, Build Arm Ubuntu Jammy, ubuntu-22.04-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 5, Build Intel StaticX Legacy, ubuntu-22.04, yes) (push) Has been cancelled
Build and test GAM / build (false, build, 6, Build Arm StaticX Legacy, ubuntu-22.04-arm, yes) (push) Has been cancelled
Build and test GAM / build (false, build, 8, Build Arm MacOS 14, macos-14) (push) Has been cancelled
Build and test GAM / build (false, build, 9, Build Arm MacOS 15, macos-15) (push) Has been cancelled
Build and test GAM / build (false, test, 14, Test Python 3.10, ubuntu-24.04, 3.10) (push) Has been cancelled
Build and test GAM / build (false, test, 15, Test Python 3.11, ubuntu-24.04, 3.11) (push) Has been cancelled
Build and test GAM / build (false, test, 16, Test Python 3.12, ubuntu-24.04, 3.12) (push) Has been cancelled
Build and test GAM / build (false, test, 17, Test Python 3.15-dev, ubuntu-24.04, 3.15-dev) (push) Has been cancelled
Build and test GAM / build (true, test, 18, Test Python 3.14 freethread, ubuntu-24.04, 3.14) (push) Has been cancelled
Build and test GAM / merge (push) Has been cancelled
Build and test GAM / publish (push) Has been cancelled
Check for Google Root CA Updates / check-certs (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Push wiki / pushwiki (push) Has been cancelled
2025-12-20 11:31:05 -08:00
Ross Scroggs
53c9544456 Updated gam report <ActivityApplicationName> #1869 2025-12-20 11:30:33 -08:00
Ross Scroggs
be8ae5957b Added option conferencedata meet <MeetID> to <EventAttribute>
Some checks failed
Build and test GAM / build (false, build, 1, Build Intel Ubuntu Jammy, ubuntu-22.04) (push) Has been cancelled
Build and test GAM / build (false, build, 10, Build x86_64 macOS 15, macos-15-intel) (push) Has been cancelled
Build and test GAM / build (false, build, 11, Build Arm MacOS 26, macos-26) (push) Has been cancelled
Build and test GAM / build (false, build, 12, Build Intel Windows, windows-2025) (push) Has been cancelled
Build and test GAM / build (false, build, 13, Build Arm Windows, windows-11-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 2, Build Intel Ubuntu Noble, ubuntu-24.04) (push) Has been cancelled
Build and test GAM / build (false, build, 3, Build Arm Ubuntu Noble, ubuntu-24.04-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 4, Build Arm Ubuntu Jammy, ubuntu-22.04-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 5, Build Intel StaticX Legacy, ubuntu-22.04, yes) (push) Has been cancelled
Build and test GAM / build (false, build, 6, Build Arm StaticX Legacy, ubuntu-22.04-arm, yes) (push) Has been cancelled
Build and test GAM / build (false, build, 8, Build Arm MacOS 14, macos-14) (push) Has been cancelled
Build and test GAM / build (false, build, 9, Build Arm MacOS 15, macos-15) (push) Has been cancelled
Build and test GAM / build (false, test, 14, Test Python 3.10, ubuntu-24.04, 3.10) (push) Has been cancelled
Build and test GAM / build (false, test, 15, Test Python 3.11, ubuntu-24.04, 3.11) (push) Has been cancelled
Build and test GAM / build (false, test, 16, Test Python 3.12, ubuntu-24.04, 3.12) (push) Has been cancelled
Build and test GAM / build (false, test, 17, Test Python 3.15-dev, ubuntu-24.04, 3.15-dev) (push) Has been cancelled
Build and test GAM / build (true, test, 18, Test Python 3.14 freethread, ubuntu-24.04, 3.14) (push) Has been cancelled
Build and test GAM / merge (push) Has been cancelled
Build and test GAM / publish (push) Has been cancelled
Check for Google Root CA Updates / check-certs (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Push wiki / pushwiki (push) Has been cancelled
2025-12-18 10:26:59 -08:00
11 changed files with 338 additions and 190 deletions

View File

@@ -4585,28 +4585,41 @@ gam report usage customer [todrive <ToDriveAttribute>*]
[convertmbtogb] [convertmbtogb]
<ActivityApplicationName> ::= <ActivityApplicationName> ::=
accessevaluation|
accesstransparency|access| accesstransparency|access|
admin| admin|
admindataaction|
assignments|
calendar|calendars| calendar|calendars|
chat| chat|
chrome| chrome|
classroom| classroom|
cloudsearch|
contacts|
contextawareaccess| contextawareaccess|
datamigration|
datastudio| datastudio|
directorysync|
drive|doc|docs| drive|doc|docs|
gcp|cloud| gcp|cloud|
geminiinworkspaceapps|gemini|geminiforworkspace| geminiinworkspaceapps|gemini|geminiforworkspace|
gmail| gmail|
gplus|currents|google+| gplus|currents|google+|
graduation|
groups|group| groups|group|
groupsenterprise|enterprisegroups| groupsenterprise|enterprisegroups|
jamboard| jamboard|
keep| keep|
ldap|
login|logins| login|logins|
meet|hangoutsmeet| meet|hangoutsmeet|
meethardware|
mobile|devices| mobile|devices|
profile|
rules| rules|
saml| saml|
takeout|
tasks|
token|tokens|oauthtoken| token|tokens|oauthtoken|
useraccounts| useraccounts|
vault vault
@@ -4617,7 +4630,7 @@ gam report <ActivityApplicationName> [todrive <ToDriveAttribute>*]
yesterday|today|thismonth|(previousmonths <Integer>)] yesterday|today|thismonth|(previousmonths <Integer>)]
[filter <String> (filtertime<String> <Time>)*] [filter <String> (filtertime<String> <Time>)*]
[event|events <EventNameList>] [ip <String>] [event|events <EventNameList>] [ip <String>]
[groupidfilter <String>] [groupidfilter <String>] [resourcedetailsfilter <String>]
[maxactivities <Number>] [maxevents <Number>] [maxresults <Number>] [maxactivities <Number>] [maxevents <Number>] [maxresults <Number>]
[countsonly [bydate|summary] [eventrowfilter]] [countsonly [bydate|summary] [eventrowfilter]]
(addcsvdata <FieldName> <String>)* [shownoactivities] (addcsvdata <FieldName> <String>)* [shownoactivities]
@@ -5425,7 +5438,7 @@ gam print vaultcounts [todrive <ToDriveAttributes>*]
[wait <Integer>] [wait <Integer>]
gam print vaultcounts [todrive <ToDriveAttributes>*] gam print vaultcounts [todrive <ToDriveAttributes>*]
matter <MatterItem> matter <MatterItem>
corpus mail|groups corpus mail|groups
[scope [all_data|held_data|unprocessed_data]] [scope [all_data|held_data|unprocessed_data]]
[(accounts <EmailAddressEntity>) | (orgunit|org|ou <OrgUnitPath>) | everyone] [(accounts <EmailAddressEntity>) | (orgunit|org|ou <OrgUnitPath>) | everyone]
[terms <String>] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>] [timezone <TimeZone>] [terms <String>] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>] [timezone <TimeZone>]
@@ -5587,7 +5600,7 @@ gam create vaultquery <MatterItem> [name <String>]
[locationquery <StringList>] [peoplequery <StringList>] [minuswords <StringList>] [locationquery <StringList>] [peoplequery <StringList>] [minuswords <StringList>]
[responsestatuses <AttendeeStatus>(,<AttendeeStatus>)*] [calendarversiondate <Date>|<Time>] [responsestatuses <AttendeeStatus>(,<AttendeeStatus>)*] [calendarversiondate <Date>|<Time>]
(covereddata calllogs|textmessages|voicemails)* (covereddata calllogs|textmessages|voicemails)*
[<JSONData>] [<JSONData>]
[shownames] [shownames]
[showdetails|returnidonly|formatjson] [showdetails|returnidonly|formatjson]
@@ -8286,7 +8299,7 @@ gam <UserItem> print meettranscripts <MeetConferenceName> [todrive <ToDriveAttri
<BiographyContent> ::= <BiographyContent> ::=
<String>| <String>|
(file|textfile <FileName> [charset <Charset>])| (file|textfile <FileName> [charset <Charset>])|
(gdoc <UserGoogleDoc>) (gdoc <UserGoogleDoc>)
<PeopleContactAttribute> ::= <PeopleContactAttribute> ::=

View File

@@ -1,3 +1,23 @@
7.30.04
Updated `gam report gmail` to avoid the following error when incomplete start/end time information is provided.
```
ERROR: Invalid request: Start time and end time should both be provided, and the scan duration should not be greater than 30 days.
```
* No time information provided - GAM sets `range -30d today`
* Only `start <Time>` provided - GAM sets `end <Time>+30d`
* Only `end <Time>` provided - GAM sets `start <Time>-30d`
7.30.03
Updated `gam report <ActivityApplicationName>` to reflect the changes described here:
* See: https://workspaceupdates.googleblog.com/2025/12/google-workspace-audit-log-api.html
Added option `resourcedetailsfilter <String>` to `gam report <ActivityApplicationName>` described here:
* See: https://developers.google.com/workspace/admin/reports/reference/rest/v1/activities/list#query-parameters
For `gam <UserTypeEntity> print <Objects>`, expanded the list of `<Objects>` covered by `gam.cfg csv_output_users_audit = True`.
7.30.02 7.30.02
Added option `conferencedata meet <MeetID>` to `<EventAttribute>` that allows specifying Added option `conferencedata meet <MeetID>` to `<EventAttribute>` that allows specifying

View File

@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
""" """
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>' __author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
__version__ = '7.30.02' __version__ = '7.30.04'
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)' __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
#pylint: disable=wrong-import-position #pylint: disable=wrong-import-position
@@ -12645,8 +12645,8 @@ def doPrintShowSvcAccts():
svcAccts = callGAPIpages(iam.projects().serviceAccounts(), 'list', 'accounts', svcAccts = callGAPIpages(iam.projects().serviceAccounts(), 'list', 'accounts',
throwReasons=[GAPI.NOT_FOUND, GAPI.PERMISSION_DENIED], throwReasons=[GAPI.NOT_FOUND, GAPI.PERMISSION_DENIED],
name=f'projects/{projectId}') name=f'projects/{projectId}')
jcount = len(svcAccts)
if not csvPF: if not csvPF:
jcount = len(svcAccts)
entityPerformActionNumItems([Ent.PROJECT, projectId], jcount, Ent.SVCACCT, i, count) entityPerformActionNumItems([Ent.PROJECT, projectId], jcount, Ent.SVCACCT, i, count)
Ind.Increment() Ind.Increment()
j = 0 j = 0
@@ -13247,6 +13247,7 @@ def doWhatIs():
return return
if not invitableCheck: if not invitableCheck:
isInvitableUser = False isInvitableUser = False
ci = None
else: else:
isInvitableUser, ci = _getIsInvitableUser(None, email) isInvitableUser, ci = _getIsInvitableUser(None, email)
if isInvitableUser: if isInvitableUser:
@@ -13685,28 +13686,42 @@ REPORT_ALIASES_CHOICE_MAP = {
} }
REPORT_CHOICE_MAP = { REPORT_CHOICE_MAP = {
'accessevaluation': 'access_evaluation',
'accesstransparency': 'access_transparency', 'accesstransparency': 'access_transparency',
'admin': 'admin', 'admin': 'admin',
'admindataaction': 'admin_data_action',
'assignments': 'assignments',
'calendar': 'calendar', 'calendar': 'calendar',
'chat': 'chat', 'chat': 'chat',
'chrome': 'chrome', 'chrome': 'chrome',
'classroom': 'classroom',
'cloudsearch': 'cloud_search',
'contacts': 'contacts',
'contextawareaccess': 'context_aware_access', 'contextawareaccess': 'context_aware_access',
'customer': 'customer', 'customer': 'customer',
'datamigration': 'data_migration',
'datastudio': 'data_studio', 'datastudio': 'data_studio',
'directorysync': 'directory_sync',
'drive': 'drive', 'drive': 'drive',
'gcp': 'gcp', 'gcp': 'gcp',
'geminiinworkspaceapps': 'gemini_in_workspace_apps', 'geminiinworkspaceapps': 'gemini_in_workspace_apps',
'gmail': 'gmail', 'gmail': 'gmail',
'gplus': 'gplus', 'gplus': 'gplus',
'graduation':'graduation',
'groups': 'groups', 'groups': 'groups',
'groupsenterprise': 'groups_enterprise', 'groupsenterprise': 'groups_enterprise',
'jamboard': 'jamboard', 'jamboard': 'jamboard',
'keep': 'keep', 'keep': 'keep',
'ldap': 'ldap',
'login': 'login', 'login': 'login',
'meet': 'meet', 'meet': 'meet',
'meethardware': 'meet_hardware',
'mobile': 'mobile', 'mobile': 'mobile',
'profile': 'profile',
'rules': 'rules', 'rules': 'rules',
'saml': 'saml', 'saml': 'saml',
'takeout': 'takeout',
'tasks': 'tasks',
'token': 'token', 'token': 'token',
'usage': 'usage', 'usage': 'usage',
'usageparameters': 'usageparameters', 'usageparameters': 'usageparameters',
@@ -13719,11 +13734,17 @@ REPORT_ACTIVITIES_UPPERCASE_EVENTS = {
'access_transparency', 'access_transparency',
'admin', 'admin',
'chrome', 'chrome',
'cloud_search',
'context_aware_access', 'context_aware_access',
'data_migration',
'data_studio', 'data_studio',
'directory_sync',
'gcp', 'gcp',
'jamboard', 'jamboard',
'mobile' 'meet_hardware',
'mobile',
'profile',
'takeout',
} }
REPORT_ACTIVITIES_TIME_OBJECTS = {'time'} REPORT_ACTIVITIES_TIME_OBJECTS = {'time'}
@@ -14037,7 +14058,7 @@ def doReport():
if customerId == GC.MY_CUSTOMER: if customerId == GC.MY_CUSTOMER:
customerId = None customerId = None
csvPF = CSVPrintFile() csvPF = CSVPrintFile()
filters = actorIpAddress = groupIdFilter = orgUnit = orgUnitId = None filters = actorIpAddress = groupIdFilter = orgUnit = orgUnitId = resourceDetailsFilter = None
showOrgUnit = False showOrgUnit = False
parameters = set() parameters = set()
parameterServices = set() parameterServices = set()
@@ -14154,6 +14175,8 @@ def doReport():
eventRowFilter = True eventRowFilter = True
elif activityReports and myarg == 'groupidfilter': elif activityReports and myarg == 'groupidfilter':
groupIdFilter = getString(Cmd.OB_STRING) groupIdFilter = getString(Cmd.OB_STRING)
elif activityReports and myarg == 'resourcedetailsfilter':
resourceDetailsFilter = getString(Cmd.OB_STRING)
elif myarg == 'addcsvdata': elif myarg == 'addcsvdata':
k = getString(Cmd.OB_STRING) k = getString(Cmd.OB_STRING)
addCSVData[k] = getString(Cmd.OB_STRING, minLen=0) addCSVData[k] = getString(Cmd.OB_STRING, minLen=0)
@@ -14420,6 +14443,17 @@ def doReport():
else: else:
for eventName in eventNames: for eventName in eventNames:
zeroEventCounts[eventName] = 0 zeroEventCounts[eventName] = 0
# gmail requires a start time and an end time no more than 30 days apart
if report == 'gmail':
if startEndTime.startTime is None:
if startEndTime.endTime is None:
startEndTime.endDateTime = todaysDate()
startEndTime.endTime = ISOformatTimeStamp(startEndTime.endDateTime)
startEndTime.startDateTime = startEndTime.endDateTime.shift(days=-30)
startEndTime.startTime = ISOformatTimeStamp(startEndTime.startDateTime)
elif startEndTime.endTime is None:
startEndTime.endDateTime = startEndTime.startDateTime.shift(days=30)
startEndTime.endTime = ISOformatTimeStamp(startEndTime.endDateTime)
i = 0 i = 0
count = len(users) count = len(users)
for user in users: for user in users:
@@ -14438,7 +14472,7 @@ def doReport():
actorIpAddress=actorIpAddress, orgUnitID=orgUnitId, actorIpAddress=actorIpAddress, orgUnitID=orgUnitId,
startTime=startEndTime.startTime, endTime=startEndTime.endTime, startTime=startEndTime.startTime, endTime=startEndTime.endTime,
eventName=eventName, filters=filters, groupIdFilter=groupIdFilter, eventName=eventName, filters=filters, groupIdFilter=groupIdFilter,
maxResults=maxResults) resourceDetailsFilter=resourceDetailsFilter, maxResults=maxResults)
except GAPI.badRequest: except GAPI.badRequest:
if user != 'all': if user != 'all':
entityUnknownWarning(Ent.USER, user, i, count) entityUnknownWarning(Ent.USER, user, i, count)
@@ -16074,8 +16108,8 @@ def printShowAnalyticItems(users, entityType):
except GAPI.serviceNotAvailable: except GAPI.serviceNotAvailable:
userAnalyticsServiceNotEnabledWarning(user, i, count) userAnalyticsServiceNotEnabledWarning(user, i, count)
continue continue
jcount = len(results)
if not csvPF: if not csvPF:
jcount = len(results)
if not FJQC.formatJSON: if not FJQC.formatJSON:
entityPerformActionNumItems([Ent.USER, user], jcount, entityType) entityPerformActionNumItems([Ent.USER, user], jcount, entityType)
Ind.Increment() Ind.Increment()
@@ -16091,7 +16125,7 @@ def printShowAnalyticItems(users, entityType):
printLine(json.dumps(cleanJSON(item, timeObjects=analyticEntityMap['timeObjects']), printLine(json.dumps(cleanJSON(item, timeObjects=analyticEntityMap['timeObjects']),
ensure_ascii=False, sort_keys=False)) ensure_ascii=False, sort_keys=False))
Ind.Decrement() Ind.Decrement()
else: elif results:
for item in results: for item in results:
row = flattenJSON(item, flattened={'User': user}, timeObjects=analyticEntityMap['timeObjects']) row = flattenJSON(item, flattened={'User': user}, timeObjects=analyticEntityMap['timeObjects'])
if not FJQC.formatJSON: if not FJQC.formatJSON:
@@ -16103,6 +16137,8 @@ def printShowAnalyticItems(users, entityType):
row['JSON'] = json.dumps(cleanJSON(item, timeObjects=analyticEntityMap['timeObjects']), row['JSON'] = json.dumps(cleanJSON(item, timeObjects=analyticEntityMap['timeObjects']),
ensure_ascii=False, sort_keys=True) ensure_ascii=False, sort_keys=True)
csvPF.WriteRowNoFilter(row) csvPF.WriteRowNoFilter(row)
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'User': user})
if csvPF: if csvPF:
csvPF.writeCSVfile(Ent.Plural(entityType)) csvPF.writeCSVfile(Ent.Plural(entityType))
@@ -22381,11 +22417,13 @@ def _printPersonEntityList(entityType, entityList, userEntityType, user, i, coun
if not contactQuery or localPeopleContactSelects(contactQuery, person): if not contactQuery or localPeopleContactSelects(contactQuery, person):
_showPerson(userEntityType, user, entityType, person, j, jcount, FJQC, parameters) _showPerson(userEntityType, user, entityType, person, j, jcount, FJQC, parameters)
Ind.Decrement() Ind.Decrement()
else: elif entityList:
entityTypeName = Ent.Singular(userEntityType) entityTypeName = Ent.Singular(userEntityType)
for person in entityList: for person in entityList:
if not contactQuery or localPeopleContactSelects(contactQuery, person): if not contactQuery or localPeopleContactSelects(contactQuery, person):
_printPerson(entityTypeName, user, person, csvPF, FJQC, parameters) _printPerson(entityTypeName, user, person, csvPF, FJQC, parameters)
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({userEntityType: user})
PEOPLE_FIELDS_CHOICE_MAP = { PEOPLE_FIELDS_CHOICE_MAP = {
'additionalname': PEOPLE_NAMES, 'additionalname': PEOPLE_NAMES,
@@ -23701,9 +23739,9 @@ def printShowContactDelegates(users):
except (GAPI.serviceNotAvailable, GAPI.badRequest): except (GAPI.serviceNotAvailable, GAPI.badRequest):
userContactDelegateServiceNotEnabledWarning(user, i, count) userContactDelegateServiceNotEnabledWarning(user, i, count)
continue continue
jcount = len(delegates)
if not csvPF: if not csvPF:
if not csvStyle: if not csvStyle:
jcount = len(delegates)
entityPerformActionNumItems([Ent.USER, user], jcount, Ent.CONTACT_DELEGATE, i, count) entityPerformActionNumItems([Ent.USER, user], jcount, Ent.CONTACT_DELEGATE, i, count)
Ind.Increment() Ind.Increment()
j = 0 j = 0
@@ -23727,17 +23765,16 @@ def printShowContactDelegates(users):
writeStdout(f'{user},{_getDelegateName(cd, delegateEmail, delegateNames)},{delegateEmail}\n') writeStdout(f'{user},{_getDelegateName(cd, delegateEmail, delegateNames)},{delegateEmail}\n')
else: else:
writeStdout(f'{user},{delegateEmail}\n') writeStdout(f'{user},{delegateEmail}\n')
else: elif delegates:
if delegates: if showNames:
if showNames: for delegate in delegates:
for delegate in delegates: csvPF.WriteRow({'User': user, 'delegateName': _getDelegateName(cd, delegate['email'], delegateNames),
csvPF.WriteRow({'User': user, 'delegateName': _getDelegateName(cd, delegate['email'], delegateNames), 'delegateAddress': delegate['email']})
'delegateAddress': delegate['email']}) else:
else: for delegate in delegates:
for delegate in delegates: csvPF.WriteRow({'User': user, 'delegateAddress': delegate['email']})
csvPF.WriteRow({'User': user, 'delegateAddress': delegate['email']}) elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]: csvPF.WriteRowNoFilter({'User': user})
csvPF.WriteRowNoFilter({'User': user})
if csvPF: if csvPF:
csvPF.writeCSVfile('Contact Delegates') csvPF.writeCSVfile('Contact Delegates')
@@ -26749,10 +26786,11 @@ def printShowChatEmojis(users):
try: try:
emojis = callGAPIpages(chat.customEmojis(), 'list', 'customEmojis', emojis = callGAPIpages(chat.customEmojis(), 'list', 'customEmojis',
pageMessage=_getChatPageMessage(Ent.CHAT_EMOJI, user, i, count, pfilter), pageMessage=_getChatPageMessage(Ent.CHAT_EMOJI, user, i, count, pfilter),
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED], throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT,
GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
pageSize=GC.Values[GC.CHAT_MAX_RESULTS], filter=pfilter) pageSize=GC.Values[GC.CHAT_MAX_RESULTS], filter=pfilter)
except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied) as e: except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.failedPrecondition) as e:
exitIfChatNotConfigured(chat, kvList, str(e), i, count) exitIfChatNotConfigured(chat, kvList, str(e), i, count)
continue continue
except GAPI.failedPrecondition: except GAPI.failedPrecondition:
@@ -26768,9 +26806,11 @@ def printShowChatEmojis(users):
j += 1 j += 1
_showChatItem(emoji, Ent.CHAT_EMOJI, FJQC, j, jcount) _showChatItem(emoji, Ent.CHAT_EMOJI, FJQC, j, jcount)
Ind.Decrement() Ind.Decrement()
else: elif emojis:
for emoji in sorted(emojis, key=lambda k: k['emojiName']): for emoji in sorted(emojis, key=lambda k: k['emojiName']):
_printChatItem(user, emoji, '', Ent.CHAT_EMOJI, csvPF, FJQC) _printChatItem(user, emoji, '', Ent.CHAT_EMOJI, csvPF, FJQC)
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'User': user})
if csvPF: if csvPF:
csvPF.writeCSVfile('Chat Custom Emojis') csvPF.writeCSVfile('Chat Custom Emojis')
@@ -28369,9 +28409,11 @@ def printShowChatMessages(users):
message['space']['displayName'] = parent['displayName'] message['space']['displayName'] = parent['displayName']
_showChatItem(message, Ent.CHAT_MESSAGE, FJQC, k, kcount) _showChatItem(message, Ent.CHAT_MESSAGE, FJQC, k, kcount)
Ind.Decrement() Ind.Decrement()
else: elif messages:
for message in messages: for message in messages:
_printChatItem(user, message, parent, Ent.CHAT_MESSAGE, csvPF, FJQC) _printChatItem(user, message, parent, Ent.CHAT_MESSAGE, csvPF, FJQC)
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'User': user})
if csvPF: if csvPF:
csvPF.writeCSVfile('Chat Messages') csvPF.writeCSVfile('Chat Messages')
@@ -28482,9 +28524,11 @@ def printShowChatEvents(users):
event['space'] = {'name': parentName, 'displayName': parent['displayName']} event['space'] = {'name': parentName, 'displayName': parent['displayName']}
_showChatItem(event, Ent.CHAT_EVENT, FJQC, k, kcount) _showChatItem(event, Ent.CHAT_EVENT, FJQC, k, kcount)
Ind.Decrement() Ind.Decrement()
else: elif events:
for event in events: for event in events:
_printChatItem(user, event, parent, Ent.CHAT_EVENT, csvPF, FJQC) _printChatItem(user, event, parent, Ent.CHAT_EVENT, csvPF, FJQC)
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'User': user})
if csvPF: if csvPF:
csvPF.writeCSVfile('Chat Events') csvPF.writeCSVfile('Chat Events')
@@ -31557,8 +31601,8 @@ def doPrintShowChromeApps():
except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.serviceNotAvailable) as e: except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.serviceNotAvailable) as e:
entityActionFailedWarning([Ent.CHROME_APP, None], str(e)) entityActionFailedWarning([Ent.CHROME_APP, None], str(e))
return return
jcount = len(apps)
if not csvPF: if not csvPF:
jcount = len(apps)
if not FJQC.formatJSON: if not FJQC.formatJSON:
entityPerformActionNumItems([Ent.ORGANIZATIONAL_UNIT, orgUnitPath], jcount, Ent.CHROME_APP) entityPerformActionNumItems([Ent.ORGANIZATIONAL_UNIT, orgUnitPath], jcount, Ent.CHROME_APP)
Ind.Increment() Ind.Increment()
@@ -31706,8 +31750,8 @@ def doPrintShowChromeAppDevices():
except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.serviceNotAvailable) as e: except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.serviceNotAvailable) as e:
entityActionFailedWarning([Ent.CHROME_APP_DEVICE, None], str(e)) entityActionFailedWarning([Ent.CHROME_APP_DEVICE, None], str(e))
return return
jcount = len(devices)
if not csvPF: if not csvPF:
jcount = len(devices)
if not FJQC.formatJSON: if not FJQC.formatJSON:
entityPerformActionNumItems([Ent.ORGANIZATIONAL_UNIT, orgUnitPath], jcount, Ent.CHROME_APP_DEVICE) entityPerformActionNumItems([Ent.ORGANIZATIONAL_UNIT, orgUnitPath], jcount, Ent.CHROME_APP_DEVICE)
Ind.Increment() Ind.Increment()
@@ -32135,8 +32179,8 @@ def doPrintShowChromeVersions():
except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.serviceNotAvailable) as e: except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.serviceNotAvailable) as e:
entityActionFailedWarning([Ent.CHROME_VERSION, None], str(e)) entityActionFailedWarning([Ent.CHROME_VERSION, None], str(e))
return return
jcount = len(versions)
if not csvPF: if not csvPF:
jcount = len(versions)
if not FJQC.formatJSON: if not FJQC.formatJSON:
entityPerformActionNumItems([Ent.ORGANIZATIONAL_UNIT, orgUnitPath], jcount, Ent.CHROME_VERSION) entityPerformActionNumItems([Ent.ORGANIZATIONAL_UNIT, orgUnitPath], jcount, Ent.CHROME_VERSION)
Ind.Increment() Ind.Increment()
@@ -42725,8 +42769,8 @@ def doPrintShowVaultExports():
else: else:
warnMatterNotOpen(None, matter, matterNameId, j, jcount) warnMatterNotOpen(None, matter, matterNameId, j, jcount)
continue continue
kcount = len(exports)
if not csvPF: if not csvPF:
kcount = len(exports)
if not FJQC.formatJSON: if not FJQC.formatJSON:
entityPerformActionNumItems([Ent.VAULT_MATTER, matterNameId], kcount, Ent.VAULT_EXPORT, j, jcount) entityPerformActionNumItems([Ent.VAULT_MATTER, matterNameId], kcount, Ent.VAULT_EXPORT, j, jcount)
Ind.Increment() Ind.Increment()
@@ -43420,8 +43464,8 @@ def doPrintShowVaultHolds():
else: else:
warnMatterNotOpen(None, matter, matterNameId, j, jcount) warnMatterNotOpen(None, matter, matterNameId, j, jcount)
continue continue
kcount = len(holds)
if not csvPF: if not csvPF:
kcount = len(holds)
if not FJQC.formatJSON: if not FJQC.formatJSON:
entityPerformActionNumItems([Ent.VAULT_MATTER, matterNameId], kcount, Ent.VAULT_HOLD, j, jcount) entityPerformActionNumItems([Ent.VAULT_MATTER, matterNameId], kcount, Ent.VAULT_HOLD, j, jcount)
Ind.Increment() Ind.Increment()
@@ -43784,8 +43828,8 @@ def doPrintShowVaultQueries():
else: else:
warnMatterNotOpen(None, matter, matterNameId, j, jcount) warnMatterNotOpen(None, matter, matterNameId, j, jcount)
continue continue
kcount = len(queries)
if not csvPF: if not csvPF:
kcount = len(queries)
if not FJQC.formatJSON: if not FJQC.formatJSON:
entityPerformActionNumItems([Ent.VAULT_MATTER, matterNameId], kcount, Ent.VAULT_QUERY, j, jcount) entityPerformActionNumItems([Ent.VAULT_MATTER, matterNameId], kcount, Ent.VAULT_QUERY, j, jcount)
Ind.Increment() Ind.Increment()
@@ -48197,8 +48241,8 @@ def printShowWebResources(users):
if not verif: if not verif:
continue continue
sites = callGAPIitems(verif.webResource(), 'list', 'items') sites = callGAPIitems(verif.webResource(), 'list', 'items')
jcount = len(sites)
if not csvPF: if not csvPF:
jcount = len(sites)
entityPerformActionNumItems([Ent.USER, user], jcount, Ent.WEB_RESOURCE, i, count) entityPerformActionNumItems([Ent.USER, user], jcount, Ent.WEB_RESOURCE, i, count)
Ind.Increment() Ind.Increment()
j = 0 j = 0
@@ -48206,10 +48250,12 @@ def printShowWebResources(users):
j += 1 j += 1
_showSiteVerificationInfo(site, j, jcount) _showSiteVerificationInfo(site, j, jcount)
Ind.Decrement() Ind.Decrement()
else: elif sites:
for site in sites: for site in sites:
row = flattenJSON(site, flattened={'User': user}) row = flattenJSON(site, flattened={'User': user})
csvPF.WriteRowTitles(row) csvPF.WriteRowTitles(row)
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'User': user})
if csvPF: if csvPF:
csvPF.writeCSVfile('Web Resources') csvPF.writeCSVfile('Web Resources')
@@ -48234,8 +48280,8 @@ def printShowWebMasterSites(users):
throwReasons=[GAPI.PERMISSION_DENIED]) throwReasons=[GAPI.PERMISSION_DENIED])
except GAPI.permissionDenied as e: except GAPI.permissionDenied as e:
accessErrorExitNonDirectory(API.SEARCHCONSOLE, str(e)) accessErrorExitNonDirectory(API.SEARCHCONSOLE, str(e))
jcount = len(sites)
if not csvPF: if not csvPF:
jcount = len(sites)
entityPerformActionNumItems([Ent.USER, user], jcount, Ent.WEB_MASTERSITE, i, count) entityPerformActionNumItems([Ent.USER, user], jcount, Ent.WEB_MASTERSITE, i, count)
Ind.Increment() Ind.Increment()
j = 0 j = 0
@@ -48246,10 +48292,12 @@ def printShowWebMasterSites(users):
printKeyValueList(['permissionLevel', site['permissionLevel']]) printKeyValueList(['permissionLevel', site['permissionLevel']])
Ind.Decrement() Ind.Decrement()
Ind.Decrement() Ind.Decrement()
else: elif sites:
for site in sites: for site in sites:
row = flattenJSON(site, flattened={'User': user}) row = flattenJSON(site, flattened={'User': user})
csvPF.WriteRowTitles(row) csvPF.WriteRowTitles(row)
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'User': user})
if csvPF: if csvPF:
csvPF.writeCSVfile('Web Master Sites') csvPF.writeCSVfile('Web Master Sites')
@@ -51513,9 +51561,9 @@ def _printShowGuardians(entityList=None):
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.SERVICE_NOT_AVAILABLE], GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.SERVICE_NOT_AVAILABLE],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
studentId=studentId, invitedEmailAddress=invitedEmailAddress, states=states) studentId=studentId, invitedEmailAddress=invitedEmailAddress, states=states)
jcount = len(invitations)
if not csvPF: if not csvPF:
if not FJQC.formatJSON: if not FJQC.formatJSON:
jcount = len(invitations)
entityPerformActionNumItems([Ent.STUDENT, studentId if not allStudents else 'All'], jcount, Ent.GUARDIAN_INVITATION, i, count) entityPerformActionNumItems([Ent.STUDENT, studentId if not allStudents else 'All'], jcount, Ent.GUARDIAN_INVITATION, i, count)
Ind.Increment() Ind.Increment()
j = 0 j = 0
@@ -51530,7 +51578,7 @@ def _printShowGuardians(entityList=None):
Ind.Decrement() Ind.Decrement()
else: else:
printLine(json.dumps(cleanJSON(invitations, timeObjects=GUARDIAN_TIME_OBJECTS), ensure_ascii=False, sort_keys=True)) printLine(json.dumps(cleanJSON(invitations, timeObjects=GUARDIAN_TIME_OBJECTS), ensure_ascii=False, sort_keys=True))
else: elif invitations:
if not FJQC.formatJSON: if not FJQC.formatJSON:
for invitation in invitations: for invitation in invitations:
if showStudentEmails: if showStudentEmails:
@@ -51541,6 +51589,8 @@ def _printShowGuardians(entityList=None):
else: else:
csvPF.WriteRow({'studentId': studentId, 'studentEmail': _getClassroomEmail(croom, classroomEmails, studentId, studentId), csvPF.WriteRow({'studentId': studentId, 'studentEmail': _getClassroomEmail(croom, classroomEmails, studentId, studentId),
'JSON': json.dumps(cleanJSON(invitations, timeObjects=GUARDIAN_TIME_OBJECTS), ensure_ascii=False, sort_keys=True)}) 'JSON': json.dumps(cleanJSON(invitations, timeObjects=GUARDIAN_TIME_OBJECTS), ensure_ascii=False, sort_keys=True)})
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'studentEmail': studentId})
if guardianClass != GUARDIAN_CLASS_INVITATIONS: if guardianClass != GUARDIAN_CLASS_INVITATIONS:
if csvPF: if csvPF:
if not allStudents: if not allStudents:
@@ -51556,9 +51606,9 @@ def _printShowGuardians(entityList=None):
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.SERVICE_NOT_AVAILABLE], GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.SERVICE_NOT_AVAILABLE],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
studentId=studentId, invitedEmailAddress=invitedEmailAddress) studentId=studentId, invitedEmailAddress=invitedEmailAddress)
jcount = len(guardians)
if not csvPF: if not csvPF:
if not FJQC.formatJSON: if not FJQC.formatJSON:
jcount = len(guardians)
entityPerformActionNumItems([Ent.STUDENT, studentId if not allStudents else 'All'], jcount, Ent.GUARDIAN, i, count) entityPerformActionNumItems([Ent.STUDENT, studentId if not allStudents else 'All'], jcount, Ent.GUARDIAN, i, count)
Ind.Increment() Ind.Increment()
j = 0 j = 0
@@ -51573,7 +51623,7 @@ def _printShowGuardians(entityList=None):
Ind.Decrement() Ind.Decrement()
else: else:
printLine(json.dumps(cleanJSON(guardians), ensure_ascii=False, sort_keys=True)) printLine(json.dumps(cleanJSON(guardians), ensure_ascii=False, sort_keys=True))
else: elif guardians:
if not FJQC.formatJSON: if not FJQC.formatJSON:
for guardian in guardians: for guardian in guardians:
if showStudentEmails: if showStudentEmails:
@@ -51584,6 +51634,8 @@ def _printShowGuardians(entityList=None):
else: else:
csvPF.WriteRowNoFilter({'studentId': studentId, 'studentEmail': _getClassroomEmail(croom, classroomEmails, studentId, studentId), csvPF.WriteRowNoFilter({'studentId': studentId, 'studentEmail': _getClassroomEmail(croom, classroomEmails, studentId, studentId),
'JSON': json.dumps(cleanJSON(guardians), ensure_ascii=False, sort_keys=True)}) 'JSON': json.dumps(cleanJSON(guardians), ensure_ascii=False, sort_keys=True)})
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'studentEmail': studentId})
except GAPI.notFound: except GAPI.notFound:
entityUnknownWarning(Ent.STUDENT, studentId, i, count) entityUnknownWarning(Ent.STUDENT, studentId, i, count)
except (GAPI.invalidArgument, GAPI.badRequest, GAPI.forbidden, GAPI.permissionDenied) as e: except (GAPI.invalidArgument, GAPI.badRequest, GAPI.forbidden, GAPI.permissionDenied) as e:
@@ -51857,11 +51909,10 @@ def printShowClassroomInvitations(users):
printGettingAllEntityItemsForWhom(entityType, userId, i, count) printGettingAllEntityItemsForWhom(entityType, userId, i, count)
status, invitations = _getClassroomInvitations(croom, userId, None, role, i, count) status, invitations = _getClassroomInvitations(croom, userId, None, role, i, count)
if status > 0: if status > 0:
jcount = len(invitations)
if not FJQC.formatJSON:
entityPerformActionNumItems([Ent.USER, userId], jcount, entityType, i, count)
if not csvPF: if not csvPF:
if not FJQC.formatJSON: if not FJQC.formatJSON:
jcount = len(invitations)
entityPerformActionNumItems([Ent.USER, userId], jcount, entityType, i, count)
Ind.Increment() Ind.Increment()
j = 0 j = 0
for invitation in invitations: for invitation in invitations:
@@ -51878,7 +51929,7 @@ def printShowClassroomInvitations(users):
Ind.Decrement() Ind.Decrement()
else: else:
printLine(json.dumps(cleanJSON(invitations), ensure_ascii=False, sort_keys=True)) printLine(json.dumps(cleanJSON(invitations), ensure_ascii=False, sort_keys=True))
else: elif invitations:
if not FJQC.formatJSON: if not FJQC.formatJSON:
for invitation in invitations: for invitation in invitations:
invitation['courseName'] = _getCourseName(croom, courseNames, invitation['courseId']) invitation['courseName'] = _getCourseName(croom, courseNames, invitation['courseId'])
@@ -51887,6 +51938,8 @@ def printShowClassroomInvitations(users):
else: else:
csvPF.WriteRowNoFilter({'userEmail': userEmail, csvPF.WriteRowNoFilter({'userEmail': userEmail,
'JSON': json.dumps(cleanJSON(invitations), ensure_ascii=False, sort_keys=True)}) 'JSON': json.dumps(cleanJSON(invitations), ensure_ascii=False, sort_keys=True)})
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'userId' if not FJQC.formatJSON else 'userEmail': userEmail})
if csvPF: if csvPF:
csvPF.writeCSVfile('ClassroomInvitations') csvPF.writeCSVfile('ClassroomInvitations')
@@ -53334,8 +53387,8 @@ def printShowCalendars(users):
if domain in excludeDomains: if domain in excludeDomains:
continue continue
calendars.append(calendar) calendars.append(calendar)
jcount = len(calendars)
if not csvPF: if not csvPF:
jcount = len(calendars)
if not FJQC.formatJSON: if not FJQC.formatJSON:
entityPerformActionNumItems([Ent.USER, user], jcount, Ent.CALENDAR, i, count) entityPerformActionNumItems([Ent.USER, user], jcount, Ent.CALENDAR, i, count)
Ind.Increment() Ind.Increment()
@@ -53346,39 +53399,38 @@ def printShowCalendars(users):
acls = _getCalendarPermissions(cal, calendar) acls = _getCalendarPermissions(cal, calendar)
_showCalendar(calendar, j, jcount, FJQC, acls) _showCalendar(calendar, j, jcount, FJQC, acls)
Ind.Decrement() Ind.Decrement()
else: elif calendars:
if calendars: if not getCalPermissions or not oneItemPerRow:
if not getCalPermissions or not oneItemPerRow: for calendar in calendars:
for calendar in calendars: row = {'primaryEmail': user, 'calendarId': calendar['id']}
row = {'primaryEmail': user, 'calendarId': calendar['id']} if getCalPermissions:
calPerms = _getCalendarPermissions(cal, calendar)
flattenJSON({'permissions': calPerms}, flattened=row)
flattenJSON(calendar, flattened=row, simpleLists=CALENDAR_SIMPLE_LISTS, delimiter=delimiter)
if not FJQC.formatJSON:
row.pop('id')
csvPF.WriteRowTitles(row)
elif csvPF.CheckRowTitles(row):
if getCalPermissions: if getCalPermissions:
calPerms = _getCalendarPermissions(cal, calendar) calendar.update({'permissions': calPerms})
flattenJSON({'permissions': calPerms}, flattened=row) csvPF.WriteRowNoFilter({'primaryEmail': user, 'calendarId': calendar['id'],
flattenJSON(calendar, flattened=row, simpleLists=CALENDAR_SIMPLE_LISTS, delimiter=delimiter) 'JSON': json.dumps(cleanJSON(calendar), ensure_ascii=False, sort_keys=True)})
else:
for calendar in calendars:
baserow = {'primaryEmail': user, 'calendarId': calendar['id']}
flattenJSON(calendar, flattened=baserow, simpleLists=CALENDAR_SIMPLE_LISTS, delimiter=delimiter)
for permission in _getCalendarPermissions(cal, calendar):
row = baserow.copy()
flattenJSON({'permission': permission}, flattened=row)
if not FJQC.formatJSON: if not FJQC.formatJSON:
row.pop('id') row.pop('id')
csvPF.WriteRowTitles(row) csvPF.WriteRowTitles(row)
elif csvPF.CheckRowTitles(row): elif csvPF.CheckRowTitles(row):
if getCalPermissions: calendar.update({'permission': permission})
calendar.update({'permissions': calPerms})
csvPF.WriteRowNoFilter({'primaryEmail': user, 'calendarId': calendar['id'], csvPF.WriteRowNoFilter({'primaryEmail': user, 'calendarId': calendar['id'],
'JSON': json.dumps(cleanJSON(calendar), ensure_ascii=False, sort_keys=True)}) 'JSON': json.dumps(cleanJSON(calendar), ensure_ascii=False, sort_keys=True)})
else: elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
for calendar in calendars: csvPF.WriteRowNoFilter({'primaryEmail': user})
baserow = {'primaryEmail': user, 'calendarId': calendar['id']}
flattenJSON(calendar, flattened=baserow, simpleLists=CALENDAR_SIMPLE_LISTS, delimiter=delimiter)
for permission in _getCalendarPermissions(cal, calendar):
row = baserow.copy()
flattenJSON({'permission': permission}, flattened=row)
if not FJQC.formatJSON:
row.pop('id')
csvPF.WriteRowTitles(row)
elif csvPF.CheckRowTitles(row):
calendar.update({'permission': permission})
csvPF.WriteRowNoFilter({'primaryEmail': user, 'calendarId': calendar['id'],
'JSON': json.dumps(cleanJSON(calendar), ensure_ascii=False, sort_keys=True)})
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'primaryEmail': user})
if csvPF: if csvPF:
csvPF.writeCSVfile('Calendars') csvPF.writeCSVfile('Calendars')
@@ -54610,11 +54662,13 @@ def printShowStatusEvent(users, eventType):
_getEventDaysOfWeek(event) _getEventDaysOfWeek(event)
_showCalendarStatusEvent(user, calId, Ent.EVENT, event, k, kcount, FJQC) _showCalendarStatusEvent(user, calId, Ent.EVENT, event, k, kcount, FJQC)
Ind.Decrement() Ind.Decrement()
else: elif events:
for event in events: for event in events:
if showDayOfWeek: if showDayOfWeek:
_getEventDaysOfWeek(event) _getEventDaysOfWeek(event)
_printCalendarEvent(user, calId, event, csvPF, FJQC, {}) _printCalendarEvent(user, calId, event, csvPF, FJQC, {})
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'primaryEmail': user})
first = first.shift(**wlDate['udelta']) first = first.shift(**wlDate['udelta'])
last = last.shift(**wlDate['udelta']) last = last.shift(**wlDate['udelta'])
if csvPF: if csvPF:
@@ -54728,7 +54782,7 @@ def printShowYouTubeChannel(users):
showJSON(None, channel, skipObjects={'id'}, timeObjects=YOUTUBE_CHANNEL_TIME_OBJECTS) showJSON(None, channel, skipObjects={'id'}, timeObjects=YOUTUBE_CHANNEL_TIME_OBJECTS)
Ind.Decrement() Ind.Decrement()
Ind.Decrement() Ind.Decrement()
else: elif channels:
for channel in channels: for channel in channels:
row = {'User': user, 'id': channel['id']} row = {'User': user, 'id': channel['id']}
flattenJSON(channel, flattened=row, timeObjects=YOUTUBE_CHANNEL_TIME_OBJECTS) flattenJSON(channel, flattened=row, timeObjects=YOUTUBE_CHANNEL_TIME_OBJECTS)
@@ -54739,6 +54793,8 @@ def printShowYouTubeChannel(users):
'JSON': json.dumps(cleanJSON(channel, timeObjects=YOUTUBE_CHANNEL_TIME_OBJECTS), 'JSON': json.dumps(cleanJSON(channel, timeObjects=YOUTUBE_CHANNEL_TIME_OBJECTS),
ensure_ascii=False, sort_keys=True)} ensure_ascii=False, sort_keys=True)}
csvPF.WriteRowNoFilter(row) csvPF.WriteRowNoFilter(row)
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'User': user})
if csvPF: if csvPF:
csvPF.writeCSVfile('YouTube Channels') csvPF.writeCSVfile('YouTube Channels')
@@ -66775,6 +66831,7 @@ def infoDriveFileACLs(users, useDomainAdminAccess=False):
useDomainAdminAccess=useDomainAdminAccess, useDomainAdminAccess=useDomainAdminAccess,
fileId=fileId, permissionId=permissionId, fields='*', supportsAllDrives=True) fileId=fileId, permissionId=permissionId, fields='*', supportsAllDrives=True)
if not FJQC.formatJSON: if not FJQC.formatJSON:
jcount = len(permission)
entityPerformActionNumItems([entityType, fileName], jcount, Ent.PERMITTEE) entityPerformActionNumItems([entityType, fileName], jcount, Ent.PERMITTEE)
Ind.Increment() Ind.Increment()
_showDriveFilePermission(permission, printKeys, timeObjects, j, jcount) _showDriveFilePermission(permission, printKeys, timeObjects, j, jcount)
@@ -68347,7 +68404,7 @@ def printShowSharedDrives(users, useDomainAdminAccess=False):
shareddrive = stripNonShowFields(shareddrive) shareddrive = stripNonShowFields(shareddrive)
_showSharedDrive(user, shareddrive, j, jcount, FJQC) _showSharedDrive(user, shareddrive, j, jcount, FJQC)
Ind.Decrement() Ind.Decrement()
else: elif matchedFeed:
for shareddrive in sorted(matchedFeed, key=lambda k: k['name']): for shareddrive in sorted(matchedFeed, key=lambda k: k['name']):
shareddrive = stripNonShowFields(shareddrive) shareddrive = stripNonShowFields(shareddrive)
if FJQC.formatJSON: if FJQC.formatJSON:
@@ -68360,6 +68417,8 @@ def printShowSharedDrives(users, useDomainAdminAccess=False):
csvPF.WriteRow(row) csvPF.WriteRow(row)
else: else:
csvPF.WriteRowTitles(flattenJSON(shareddrive, flattened={'User': user}, timeObjects=SHAREDDRIVE_TIME_OBJECTS)) csvPF.WriteRowTitles(flattenJSON(shareddrive, flattened={'User': user}, timeObjects=SHAREDDRIVE_TIME_OBJECTS))
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'User': user})
if csvPF: if csvPF:
csvPF.writeCSVfile('SharedDrives') csvPF.writeCSVfile('SharedDrives')
@@ -69135,12 +69194,14 @@ def printShowLookerStudioAssets(users):
if asset: if asset:
_showAsset(asset) _showAsset(asset)
Ind.Decrement() Ind.Decrement()
else: elif assets:
for asset in assets: for asset in assets:
if assetIdEntity: if assetIdEntity:
asset = _getLookerStudioAssetByID(ds, user, i, count, asset['name']) asset = _getLookerStudioAssetByID(ds, user, i, count, asset['name'])
if asset: if asset:
_printAsset(asset, user) _printAsset(asset, user)
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'User': user})
if csvPF: if csvPF:
csvPF.writeCSVfile('Looker Studio Assets') csvPF.writeCSVfile('Looker Studio Assets')
@@ -69329,8 +69390,12 @@ def printShowLookerStudioPermissions(users):
assets, jcount = _getLookerStudioAssets(ds, user, i, count, parameters, assetTypes, 'nextPageToken,assets(name,title)', None) assets, jcount = _getLookerStudioAssets(ds, user, i, count, parameters, assetTypes, 'nextPageToken,assets(name,title)', None)
if assets is None: if assets is None:
continue continue
if not csvPF and not FJQC.formatJSON: if not csvPF:
entityPerformActionNumItems([Ent.USER, user], jcount, Ent.LOOKERSTUDIO_ASSET, i, count) if not FJQC.formatJSON:
entityPerformActionNumItems([Ent.USER, user], jcount, Ent.LOOKERSTUDIO_ASSET, i, count)
elif jcount == 0 and GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'User': user})
continue
j = 0 j = 0
for asset in assets: for asset in assets:
j += 1 j += 1
@@ -69941,6 +70006,9 @@ def printShowUserGroups(users):
entityPerformActionNumItems([Ent.USER, user], jcount, Ent.GROUP, i, count) entityPerformActionNumItems([Ent.USER, user], jcount, Ent.GROUP, i, count)
else: else:
entityPerformActionModifierNumItems([Ent.USER, user], Msg.MAXIMUM_OF, jcount, Ent.GROUP, i, count) entityPerformActionModifierNumItems([Ent.USER, user], Msg.MAXIMUM_OF, jcount, Ent.GROUP, i, count)
elif jcount == 0 and GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'User': user})
continue
if noDetails: if noDetails:
Ind.Increment() Ind.Increment()
j = 0 j = 0
@@ -71281,7 +71349,6 @@ def printShowSheetRanges(users):
throwReasons=GAPI.SHEETS_ACCESS_THROW_REASONS, throwReasons=GAPI.SHEETS_ACCESS_THROW_REASONS,
spreadsheetId=spreadsheetId, ranges=spreadsheetRanges, fields='valueRanges', **kwargs) spreadsheetId=spreadsheetId, ranges=spreadsheetRanges, fields='valueRanges', **kwargs)
valueRanges = result.get('valueRanges', []) valueRanges = result.get('valueRanges', [])
kcount = len(valueRanges)
if not csvPF: if not csvPF:
if FJQC.formatJSON: if FJQC.formatJSON:
if not valueRangesOnly: if not valueRangesOnly:
@@ -71289,6 +71356,7 @@ def printShowSheetRanges(users):
else: else:
printLine(json.dumps(result.get('valueRanges', []), ensure_ascii=False, sort_keys=False)) printLine(json.dumps(result.get('valueRanges', []), ensure_ascii=False, sort_keys=False))
continue continue
kcount = len(valueRanges)
entityPerformActionNumItems([Ent.USER, user, Ent.SPREADSHEET, spreadsheetId], kcount, Ent.SPREADSHEET_RANGE, j, jcount) entityPerformActionNumItems([Ent.USER, user, Ent.SPREADSHEET, spreadsheetId], kcount, Ent.SPREADSHEET_RANGE, j, jcount)
Ind.Increment() Ind.Increment()
k = 0 k = 0
@@ -71297,19 +71365,18 @@ def printShowSheetRanges(users):
printKeyValueListWithCount(['range', valueRange['range']], k, kcount) printKeyValueListWithCount(['range', valueRange['range']], k, kcount)
_showValueRange(valueRange) _showValueRange(valueRange)
Ind.Decrement() Ind.Decrement()
else: elif valueRanges:
if kcount: row = flattenJSON(result, flattened={'User': user, 'spreadsheetId': spreadsheetId})
row = flattenJSON(result, flattened={'User': user, 'spreadsheetId': spreadsheetId}) if not FJQC.formatJSON:
if not FJQC.formatJSON: csvPF.WriteRowTitles(row)
csvPF.WriteRowTitles(row) elif csvPF.CheckRowTitles(row):
elif csvPF.CheckRowTitles(row): if not valueRangesOnly:
if not valueRangesOnly: csvPF.WriteRowNoFilter({'User': user, 'spreadsheetId': spreadsheetId,
csvPF.WriteRowNoFilter({'User': user, 'spreadsheetId': spreadsheetId, 'JSON': json.dumps(result, ensure_ascii=False, sort_keys=False)})
'JSON': json.dumps(result, ensure_ascii=False, sort_keys=False)}) else:
else: csvPF.WriteRowNoFilter({'JSON': json.dumps(result.get('valueRanges', []), ensure_ascii=False, sort_keys=False)})
csvPF.WriteRowNoFilter({'JSON': json.dumps(result.get('valueRanges', []), ensure_ascii=False, sort_keys=False)}) elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]: csvPF.WriteRowNoFilter({'User': user})
csvPF.WriteRowNoFilter({'User': user})
except (GAPI.notFound, GAPI.forbidden, GAPI.permissionDenied, except (GAPI.notFound, GAPI.forbidden, GAPI.permissionDenied,
GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.badRequest, GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.badRequest,
GAPI.invalid, GAPI.invalidArgument, GAPI.failedPrecondition) as e: GAPI.invalid, GAPI.invalidArgument, GAPI.failedPrecondition) as e:
@@ -71460,9 +71527,9 @@ def _printShowTokens(entityType, users):
GAPI.DOMAIN_CANNOT_USE_APIS, GAPI.BAD_REQUEST, GAPI.DOMAIN_CANNOT_USE_APIS, GAPI.BAD_REQUEST,
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED], GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
userKey=user, fields=f'items({fields})') userKey=user, fields=f'items({fields})')
jcount = len(results)
if not aggregateUsersBy: if not aggregateUsersBy:
if not csvPF: if not csvPF:
jcount = len(results)
entityPerformActionNumItems([Ent.USER, user], jcount, Ent.ACCESS_TOKEN, i, count) entityPerformActionNumItems([Ent.USER, user], jcount, Ent.ACCESS_TOKEN, i, count)
Ind.Increment() Ind.Increment()
j = 0 j = 0
@@ -71470,16 +71537,15 @@ def _printShowTokens(entityType, users):
j += 1 j += 1
_showToken(token, tokenTitle, orderBy, j, jcount) _showToken(token, tokenTitle, orderBy, j, jcount)
Ind.Decrement() Ind.Decrement()
else: elif results:
if results: for token in sorted(results, key=lambda k: k[orderBy]):
for token in sorted(results, key=lambda k: k[orderBy]): row = {'user': user, 'scopes': delimiter.join(token.get('scopes', []))}
row = {'user': user, 'scopes': delimiter.join(token.get('scopes', []))} for item in token:
for item in token: if item != 'scopes':
if item != 'scopes': row[item] = token.get(item, '')
row[item] = token.get(item, '') csvPF.WriteRow(row)
csvPF.WriteRow(row) elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]: csvPF.WriteRowNoFilter({'user': user})
csvPF.WriteRowNoFilter({'user': user})
elif aggregateUsersBy != 'user': elif aggregateUsersBy != 'user':
if results: if results:
for token in results: for token in results:
@@ -74638,8 +74704,8 @@ def printShowDelegates(users):
throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION], throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
userId='me') userId='me')
delegates = result.get('delegates', []) if result is not None else [] delegates = result.get('delegates', []) if result is not None else []
jcount = len(delegates)
if not csvPF: if not csvPF:
jcount = len(delegates)
if not csvStyle: if not csvStyle:
entityPerformActionNumItems([Ent.USER, user], jcount, Ent.DELEGATE, i, count) entityPerformActionNumItems([Ent.USER, user], jcount, Ent.DELEGATE, i, count)
Ind.Increment() Ind.Increment()
@@ -74670,18 +74736,17 @@ def printShowDelegates(users):
writeStdout(f'{user},{_getDelegateName(cd, delegateEmail, delegateNames)},{status},{delegateEmail}\n') writeStdout(f'{user},{_getDelegateName(cd, delegateEmail, delegateNames)},{status},{delegateEmail}\n')
else: else:
writeStdout(f'{user},{status},{delegateEmail}\n') writeStdout(f'{user},{status},{delegateEmail}\n')
else: elif delegates:
if delegates: if showNames:
if showNames: for delegate in delegates:
for delegate in delegates: csvPF.WriteRow({'User': user, 'delegateName': _getDelegateName(cd, delegate['delegateEmail'], delegateNames),
csvPF.WriteRow({'User': user, 'delegateName': _getDelegateName(cd, delegate['delegateEmail'], delegateNames), 'delegateAddress': delegate['delegateEmail'], 'delegationStatus': delegate['verificationStatus']})
'delegateAddress': delegate['delegateEmail'], 'delegationStatus': delegate['verificationStatus']}) else:
else: for delegate in delegates:
for delegate in delegates: csvPF.WriteRow({'User': user, 'delegateAddress': delegate['delegateEmail'],
csvPF.WriteRow({'User': user, 'delegateAddress': delegate['delegateEmail'], 'delegationStatus': delegate['verificationStatus']})
'delegationStatus': delegate['verificationStatus']}) elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]: csvPF.WriteRowNoFilter({'User': user})
csvPF.WriteRowNoFilter({'User': user})
except (GAPI.permissionDenied, GAPI.failedPrecondition) as e: except (GAPI.permissionDenied, GAPI.failedPrecondition) as e:
entityActionFailedWarning([Ent.USER, user, Ent.DELEGATE, None], str(e), i, count) entityActionFailedWarning([Ent.USER, user, Ent.DELEGATE, None], str(e), i, count)
except GAPI.serviceNotAvailable: except GAPI.serviceNotAvailable:
@@ -75031,7 +75096,7 @@ def infoFilters(users):
# gam <UserTypeEntity> show filters [labelidsonly] [formatjson] # gam <UserTypeEntity> show filters [labelidsonly] [formatjson]
def printShowFilters(users): def printShowFilters(users):
labelIdsOnly = False labelIdsOnly = False
csvPF = CSVPrintFile() if Act.csvFormat() else None csvPF = CSVPrintFile(['User', 'id']) if Act.csvFormat() else None
FJQC = FormatJSONQuoteChar(csvPF) FJQC = FormatJSONQuoteChar(csvPF)
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
@@ -75061,8 +75126,8 @@ def printShowFilters(users):
results = callGAPIitems(gmail.users().settings().filters(), 'list', 'filter', results = callGAPIitems(gmail.users().settings().filters(), 'list', 'filter',
throwReasons=GAPI.GMAIL_THROW_REASONS, throwReasons=GAPI.GMAIL_THROW_REASONS,
userId='me') userId='me')
jcount = len(results)
if not csvPF: if not csvPF:
jcount = len(results)
if not FJQC.formatJSON: if not FJQC.formatJSON:
entityPerformActionNumItems([Ent.USER, user], jcount, Ent.FILTER, i, count) entityPerformActionNumItems([Ent.USER, user], jcount, Ent.FILTER, i, count)
Ind.Increment() Ind.Increment()
@@ -75071,17 +75136,16 @@ def printShowFilters(users):
j += 1 j += 1
_showFilter(userFilter, j, jcount, labels, FJQC) _showFilter(userFilter, j, jcount, labels, FJQC)
Ind.Decrement() Ind.Decrement()
else: elif results:
if results: for userFilter in results:
for userFilter in results: row = _printFilter(user, userFilter, labels)
row = _printFilter(user, userFilter, labels) if FJQC.formatJSON:
if FJQC.formatJSON: if labels['labels']:
if labels['labels']: _mapFilterLabelIdsToNames(userFilter, labels)
_mapFilterLabelIdsToNames(userFilter, labels) row['JSON'] = json.dumps(userFilter, ensure_ascii=False, sort_keys=False)
row['JSON'] = json.dumps(userFilter, ensure_ascii=False, sort_keys=False) csvPF.WriteRowTitles(row)
csvPF.WriteRowTitles(row) elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]: csvPF.WriteRowNoFilter({'User': user})
csvPF.WriteRowNoFilter({'User': user})
except GAPI.serviceNotAvailable: except GAPI.serviceNotAvailable:
userGmailServiceNotEnabledWarning(user, i, count) userGmailServiceNotEnabledWarning(user, i, count)
if csvPF: if csvPF:
@@ -75690,8 +75754,8 @@ def printShowForwardingAddresses(users):
results = callGAPIitems(gmail.users().settings().forwardingAddresses(), 'list', 'forwardingAddresses', results = callGAPIitems(gmail.users().settings().forwardingAddresses(), 'list', 'forwardingAddresses',
throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.FAILED_PRECONDITION], throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.FAILED_PRECONDITION],
userId='me') userId='me')
jcount = len(results)
if not csvPF: if not csvPF:
jcount = len(results)
entityPerformActionNumItems([Ent.USER, user], jcount, Ent.FORWARDING_ADDRESS, i, count) entityPerformActionNumItems([Ent.USER, user], jcount, Ent.FORWARDING_ADDRESS, i, count)
Ind.Increment() Ind.Increment()
j = 0 j = 0
@@ -75699,12 +75763,11 @@ def printShowForwardingAddresses(users):
j += 1 j += 1
_showForwardingAddress(j, jcount, forward) _showForwardingAddress(j, jcount, forward)
Ind.Decrement() Ind.Decrement()
else: elif results:
if results: for forward in results:
for forward in results: csvPF.WriteRow({'User': user, 'forwardingEmail': forward['forwardingEmail'], 'verificationStatus': forward['verificationStatus']})
csvPF.WriteRow({'User': user, 'forwardingEmail': forward['forwardingEmail'], 'verificationStatus': forward['verificationStatus']}) elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]: csvPF.WriteRowNoFilter({'User': user})
csvPF.WriteRowNoFilter({'User': user})
except GAPI.failedPrecondition as e: except GAPI.failedPrecondition as e:
entityActionFailedWarning([Ent.USER, user], str(e), i, count) entityActionFailedWarning([Ent.USER, user], str(e), i, count)
except GAPI.serviceNotAvailable: except GAPI.serviceNotAvailable:
@@ -76134,8 +76197,8 @@ def printShowSendAs(users):
results = callGAPIitems(gmail.users().settings().sendAs(), 'list', 'sendAs', results = callGAPIitems(gmail.users().settings().sendAs(), 'list', 'sendAs',
throwReasons=GAPI.GMAIL_THROW_REASONS, throwReasons=GAPI.GMAIL_THROW_REASONS,
userId='me') userId='me')
jcount = len(results)
if not csvPF: if not csvPF:
jcount = len(results)
entityPerformActionNumItems([Ent.USER, user], jcount if selection is None else 1, Ent.SENDAS_ADDRESS, i, count) entityPerformActionNumItems([Ent.USER, user], jcount if selection is None else 1, Ent.SENDAS_ADDRESS, i, count)
Ind.Increment() Ind.Increment()
j = 0 j = 0
@@ -76144,29 +76207,28 @@ def printShowSendAs(users):
if (selection is None) or (sendas.get(selection, False)): if (selection is None) or (sendas.get(selection, False)):
_showSendAs(sendas, j, jcount, sigReplyFormat, verifyOnly) _showSendAs(sendas, j, jcount, sigReplyFormat, verifyOnly)
Ind.Decrement() Ind.Decrement()
else: elif results:
if results: for sendas in results:
for sendas in results: if (selection is None) or (sendas.get(selection, False)):
if (selection is None) or (sendas.get(selection, False)): row = {'User': user, 'isPrimary': False}
row = {'User': user, 'isPrimary': False} for item in sendas:
for item in sendas: if item != 'smtpMsa':
if item != 'smtpMsa': if item == 'signature':
if item == 'signature': if verifyOnly:
if verifyOnly: row[item] = bool(sendas[item])
row[item] = bool(sendas[item]) elif sigReplyFormat != SIG_REPLY_COMPACT:
elif sigReplyFormat != SIG_REPLY_COMPACT:
row[item] = sendas[item]
else:
row[item] = sendas[item].replace('\r', '').replace('\n', '')
else:
row[item] = sendas[item] row[item] = sendas[item]
else:
row[item] = sendas[item].replace('\r', '').replace('\n', '')
else: else:
for field in SMTPMSA_DISPLAY_FIELDS: row[item] = sendas[item]
if field in sendas[item]: else:
row[f'smtpMsa{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}{field}'] = sendas[item][field] for field in SMTPMSA_DISPLAY_FIELDS:
csvPF.WriteRowTitles(row) if field in sendas[item]:
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]: row[f'smtpMsa{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}{field}'] = sendas[item][field]
csvPF.WriteRowNoFilter({'User': user}) csvPF.WriteRowTitles(row)
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'User': user})
except GAPI.serviceNotAvailable: except GAPI.serviceNotAvailable:
userGmailServiceNotEnabledWarning(user, i, count) userGmailServiceNotEnabledWarning(user, i, count)
if csvPF: if csvPF:
@@ -76554,8 +76616,8 @@ def _printShowCSEItems(users, entityType, keyField, timeObjects):
except GAPI.serviceNotAvailable: except GAPI.serviceNotAvailable:
userGmailServiceNotEnabledWarning(user, i, count) userGmailServiceNotEnabledWarning(user, i, count)
continue continue
jcount = len(results)
if not csvPF: if not csvPF:
jcount = len(results)
if not FJQC.formatJSON: if not FJQC.formatJSON:
entityPerformActionNumItems([Ent.USER, user], jcount, entityType, i, count) entityPerformActionNumItems([Ent.USER, user], jcount, entityType, i, count)
j = 0 j = 0
@@ -77510,7 +77572,7 @@ def printShowNotes(users):
note.pop('permissions', None) note.pop('permissions', None)
_showNote(note, j, jcount, FJQC, compact) _showNote(note, j, jcount, FJQC, compact)
Ind.Decrement() Ind.Decrement()
else: elif notes:
for note in notes: for note in notes:
if showPermissions: if showPermissions:
_assignNoteOwner(note, user) _assignNoteOwner(note, user)
@@ -77527,6 +77589,8 @@ def printShowNotes(users):
row['JSON'] = json.dumps(cleanJSON(note, timeObjects=NOTES_TIME_OBJECTS), row['JSON'] = json.dumps(cleanJSON(note, timeObjects=NOTES_TIME_OBJECTS),
ensure_ascii=False, sort_keys=True) ensure_ascii=False, sort_keys=True)
csvPF.WriteRowNoFilter(row) csvPF.WriteRowNoFilter(row)
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'User': user})
except (GAPI.badRequest, GAPI.invalidArgument, GAPI.notFound) as e: except (GAPI.badRequest, GAPI.invalidArgument, GAPI.notFound) as e:
entityActionFailedWarning([Ent.USER, user, Ent.NOTE, None], str(e), i, count) entityActionFailedWarning([Ent.USER, user, Ent.NOTE, None], str(e), i, count)
except GAPI.authError: except GAPI.authError:
@@ -78316,7 +78380,7 @@ def printShowTasklists(users):
j += 1 j += 1
_showTasklist(tasklist, j, jcount, FJQC) _showTasklist(tasklist, j, jcount, FJQC)
Ind.Decrement() Ind.Decrement()
else: elif tasklists:
for tasklist in tasklists: for tasklist in tasklists:
row = flattenJSON(tasklist, flattened={'User': user}, skipObjects=TASKLIST_SKIP_OBJECTS, timeObjects=TASKLIST_TIME_OBJECTS) row = flattenJSON(tasklist, flattened={'User': user}, skipObjects=TASKLIST_SKIP_OBJECTS, timeObjects=TASKLIST_TIME_OBJECTS)
if not FJQC.formatJSON: if not FJQC.formatJSON:
@@ -78326,6 +78390,8 @@ def printShowTasklists(users):
row['JSON'] = json.dumps(cleanJSON(tasklist, skipObjects=TASKLIST_SKIP_OBJECTS, timeObjects=TASKLIST_TIME_OBJECTS), row['JSON'] = json.dumps(cleanJSON(tasklist, skipObjects=TASKLIST_SKIP_OBJECTS, timeObjects=TASKLIST_TIME_OBJECTS),
ensure_ascii=False, sort_keys=True) ensure_ascii=False, sort_keys=True)
csvPF.WriteRowNoFilter(row) csvPF.WriteRowNoFilter(row)
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'User': user})
if csvPF: if csvPF:
csvPF.writeCSVfile(CSVTitle) csvPF.writeCSVfile(CSVTitle)
@@ -78451,8 +78517,8 @@ def printShowTagManagerObjects(users, entityType):
except (GAPI.badRequest, GAPI.invalid, GAPI.notFound) as e: except (GAPI.badRequest, GAPI.invalid, GAPI.notFound) as e:
entityActionFailedWarning([Ent.USER, user, entityType, kwargs['parent']], str(e), j, jcount) entityActionFailedWarning([Ent.USER, user, entityType, kwargs['parent']], str(e), j, jcount)
continue continue
kcount = len(results)
if not csvPF: if not csvPF:
kcount = len(results)
if not FJQC.formatJSON: if not FJQC.formatJSON:
entityPerformActionNumItems([Ent.USER, user], kcount, entityType, j, jcount) entityPerformActionNumItems([Ent.USER, user], kcount, entityType, j, jcount)
Ind.Increment() Ind.Increment()
@@ -78470,7 +78536,7 @@ def printShowTagManagerObjects(users, entityType):
else: else:
printLine(json.dumps(cleanJSON(result), ensure_ascii=False, sort_keys=True)) printLine(json.dumps(cleanJSON(result), ensure_ascii=False, sort_keys=True))
Ind.Decrement() Ind.Decrement()
else: elif results:
for result in results: for result in results:
baseRow = {'User': user} baseRow = {'User': user}
for tmid in parameters['idList']: for tmid in parameters['idList']:
@@ -78482,6 +78548,8 @@ def printShowTagManagerObjects(users, entityType):
row = {'User': user, parameters['name']: result[parameters['name']], 'path': result['path']} row = {'User': user, parameters['name']: result[parameters['name']], 'path': result['path']}
row['JSON'] = json.dumps(cleanJSON(result), ensure_ascii=False, sort_keys=True) row['JSON'] = json.dumps(cleanJSON(result), ensure_ascii=False, sort_keys=True)
csvPF.WriteRowNoFilter(row) csvPF.WriteRowNoFilter(row)
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'User': user})
if csvPF: if csvPF:
csvPF.writeCSVfile(Ent.Plural(entityType)) csvPF.writeCSVfile(Ent.Plural(entityType))

View File

@@ -409,6 +409,8 @@
<MatterItem> ::= <UniqueID>|<String> <MatterItem> ::= <UniqueID>|<String>
<MatterState> ::= open|closed|deleted <MatterState> ::= open|closed|deleted
<MeetConferenceName> ::= conferenceRecords/<String> <MeetConferenceName> ::= conferenceRecords/<String>
<MeetID> ::= <String>
Must match this Python Regular Expression: [a-z]{3}-[a-z]{4}-[a-z]{3}
<MeetSpaceName> ::= spaces/<String> | <String> <MeetSpaceName> ::= spaces/<String> | <String>
<MessageContent> ::= <MessageContent> ::=
(message|textmessage|htmlmessage <String>)| (message|textmessage|htmlmessage <String>)|

View File

@@ -78,6 +78,8 @@ Client access works when accessing Resource calendars.
<CSVkmdSelector> | <CSVDataSelector> <CSVkmdSelector> | <CSVDataSelector>
See: https://github.com/GAM-team/GAM/wiki/Collections-of-Items See: https://github.com/GAM-team/GAM/wiki/Collections-of-Items
<iCalUID> ::= <String> <iCalUID> ::= <String>
<MeetID> ::= <String>
Must match this Python Regular Expression: [a-z]{3}-[a-z]{4}-[a-z]{3}
<EventAttachmentsSubfieldName> ::= <EventAttachmentsSubfieldName> ::=
attachments.fileid| attachments.fileid|
@@ -150,7 +152,8 @@ Client access works when accessing Resource calendars.
endtimeunspecified| endtimeunspecified|
extendedproperties| extendedproperties|
eventtype| eventtype|
<EventFocusTimePropertiesSubfieldName> focustimeproperties|
<EventFocusTimePropertiesSubfieldName>|
gadget| gadget|
guestscaninviteothers| guestscaninviteothers|
guestscanmodify| guestscanmodify|
@@ -164,7 +167,8 @@ Client access works when accessing Resource calendars.
organizer| organizer|
<EventOrganizerSubfieldName>| <EventOrganizerSubfieldName>|
originalstart|originalstarttime| originalstart|originalstarttime|
<EventOutOfOfficePropertiesSubfieldName> outofofficeproperties|
<EventOutOfOfficePropertiesSubfieldName>|
privatecopy| privatecopy|
recurrence| recurrence|
recurringeventid| recurringeventid|
@@ -254,6 +258,7 @@ Client access works when accessing Resource calendars.
(birthday <Date>)| (birthday <Date>)|
(color <EventColorName>)| (color <EventColorName>)|
(colorindex|colorid <EventColorIndex>)| (colorindex|colorid <EventColorIndex>)|
(conferencedata meet <MeetID>)|
(description <String>)| (description <String>)|
(end|endtime (allday <Date>)|<Time>)| (end|endtime (allday <Date>)|<Time>)|
(guestscaninviteothers <Boolean>)| (guestscaninviteothers <Boolean>)|

View File

@@ -10,6 +10,23 @@ Add the `-s` option to the end of the above commands to suppress creating the `g
See [Downloads-Installs-GAM7](https://github.com/GAM-team/GAM/wiki/Downloads-Installs) for Windows or other options, including manual installation See [Downloads-Installs-GAM7](https://github.com/GAM-team/GAM/wiki/Downloads-Installs) for Windows or other options, including manual installation
### 7.30.03
Updated `gam report <ActivityApplicationName>` to reflect the changes described here:
* See: https://workspaceupdates.googleblog.com/2025/12/google-workspace-audit-log-api.html
Added option `resourcedetailsfilter <String>` to `gam report <ActivityApplicationName>` described here:
* See: https://developers.google.com/workspace/admin/reports/reference/rest/v1/activities/list#query-parameters
For `gam <UserTypeEntity> print <Objects>`, expanded the list of `<Objects>` covered by `gam.cfg csv_output_users_audit = True`.
### 7.30.02
Added option `conferencedata meet <MeetID>` to `<EventAttribute>` that allows specifying
a reference to an existing Google Meet when creating/updating a calendar event.
Upgraded to Python 3.14.2.
### 7.30.01 ### 7.30.01
Fixed bug introduced in 7.30.00 that caused errors when reading CSV files. Fixed bug introduced in 7.30.00 that caused errors when reading CSV files.

View File

@@ -252,9 +252,9 @@ writes the credentials into the file oauth2.txt.
admin@server:/Users/admin$ rm -f /Users/admin/GAMConfig/oauth2.txt admin@server:/Users/admin$ rm -f /Users/admin/GAMConfig/oauth2.txt
admin@server:/Users/admin$ gam version admin@server:/Users/admin$ gam version
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
GAM 7.30.01 - https://github.com/GAM-team/GAM - pyinstaller GAM 7.30.03 - https://github.com/GAM-team/GAM - pyinstaller
GAM Team <google-apps-manager@googlegroups.com> GAM Team <google-apps-manager@googlegroups.com>
Python 3.14.1 64-bit final Python 3.14.2 64-bit final
macOS Tahoe 26.1 x86_64 macOS Tahoe 26.1 x86_64
Path: /Users/admin/bin/gam7 Path: /Users/admin/bin/gam7
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
@@ -990,9 +990,9 @@ writes the credentials into the file oauth2.txt.
C:\>del C:\GAMConfig\oauth2.txt C:\>del C:\GAMConfig\oauth2.txt
C:\>gam version C:\>gam version
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
GAM 7.30.01 - https://github.com/GAM-team/GAM - pythonsource GAM 7.30.03 - https://github.com/GAM-team/GAM - pythonsource
GAM Team <google-apps-manager@googlegroups.com> GAM Team <google-apps-manager@googlegroups.com>
Python 3.14.1 64-bit final Python 3.14.2 64-bit final
Windows 11 10.0.26200 AMD64 Windows 11 10.0.26200 AMD64
Path: C:\GAM7 Path: C:\GAM7
Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com

View File

@@ -12,10 +12,14 @@
- [User reports](#user-reports) - [User reports](#user-reports)
## API documentation ## API documentation
Changes starting 2025-10-29. * [Activity Data Sources](https://support.google.com/a/answer/11482175)
Changes starting 2025-10-29.
* [Reports API - Admin log event changes](https://support.google.com/a/answer/16601511) * [Reports API - Admin log event changes](https://support.google.com/a/answer/16601511)
Changes starting 2025-12-20
* [Reports API - Admin log enhancements](https://workspaceupdates.googleblog.com/2025/12/google-workspace-audit-log-api.html)
These pages show event/parameter names; scroll down in the left column to: Reports. These pages show event/parameter names; scroll down in the left column to: Reports.
* [Reports API - Activities](https://developers.google.com/admin-sdk/reports/v1/reference/activities) * [Reports API - Activities](https://developers.google.com/admin-sdk/reports/v1/reference/activities)
@@ -45,28 +49,41 @@ config csv_output_row_filter "'\"accounts:used_quota_in_mb\":count>15000'"
## Activity reports ## Activity reports
``` ```
<ActivityApplicationName> ::= <ActivityApplicationName> ::=
accessevaluation|
accesstransparency|access| accesstransparency|access|
admin| admin|
admindataaction|
assignments|
calendar|calendars| calendar|calendars|
chat| chat|
chrome| chrome|
classroom| classroom|
cloudsearch|
contacts|
contextawareaccess| contextawareaccess|
datamigration|
datastudio| datastudio|
directorysync|
drive|doc|docs| drive|doc|docs|
gcp|cloud| gcp|cloud|
geminiinworkspaceapps|gemini|geminiforworkspace| geminiinworkspaceapps|gemini|geminiforworkspace|
gmail| gmail|
gplus|currents|google+| gplus|currents|google+|
graduation|
groups|group| groups|group|
groupsenterprise|enterprisegroups| groupsenterprise|enterprisegroups|
jamboard| jamboard|
keep| keep|
ldap|
login|logins| login|logins|
meet|hangoutsmeet| meet|hangoutsmeet|
meethardware|
mobile|devices| mobile|devices|
profile|
rules| rules|
saml| saml|
takeout|
tasks|
token|tokens|oauthtoken| token|tokens|oauthtoken|
useraccounts| useraccounts|
vault vault
@@ -77,7 +94,7 @@ gam report <ActivityApplicationName> [todrive <ToDriveAttribute>*]
yesterday|today|thismonth|(previousmonths <Integer>)] yesterday|today|thismonth|(previousmonths <Integer>)]
[filter <String> (filtertime<String> <Time>)*] [filter <String> (filtertime<String> <Time>)*]
[event|events <EventNameList>] [ip <String>] [event|events <EventNameList>] [ip <String>]
[groupidfilter <String>] [groupidfilter <String>] [resourcedetailsfilter <String>]
[maxactivities <Number>] [maxevents <Number>] [maxresults <Number>] [maxactivities <Number>] [maxevents <Number>] [maxresults <Number>]
[countsonly [bydate|summary] [eventrowfilter]] [countsonly [bydate|summary] [eventrowfilter]]
(addcsvdata <FieldName> <String>)* [shownoactivities] (addcsvdata <FieldName> <String>)* [shownoactivities]
@@ -111,6 +128,12 @@ The `filtertime<String> <Time>` value replaces the string `#filtertime<String>#`
The characters following `filtertime` can be any combination of lowercase letters and numbers. This is most useful in scripts The characters following `filtertime` can be any combination of lowercase letters and numbers. This is most useful in scripts
where you can specify a relative date without having to change the script. where you can specify a relative date without having to change the script.
Limit to those users that are a member of at least one of a list of groups.
* `groupidfilter <String>` - Format: "id:abc123,id:xyz456"
Limit based on resource details.
* `resourcedetailsfilter <String>` - See: https://developers.google.com/workspace/admin/reports/reference/rest/v1/activities/list#query-parameters
You can use `config csv_output_row_filter` to filter the events if the API filter can't produce the results you want. You can use `config csv_output_row_filter` to filter the events if the API filter can't produce the results you want.
Limit to a list of specific events. Limit to a list of specific events.
@@ -119,9 +142,6 @@ Limit to a list of specific events.
Limit to a specific IP address. Limit to a specific IP address.
* `ip <String>` * `ip <String>`
Limit to those users that are a member of at least one of a list of groups.
* `groupidfilter <String>` - Format: "id:abc123,id:xyz456"
Limit the total number of activites. Limit the total number of activites.
* `maxactivities <Number>` * `maxactivities <Number>`

View File

@@ -114,6 +114,8 @@
<CourseState> ::= active|archived|provisioned|declined <CourseState> ::= active|archived|provisioned|declined
<CourseStateList> ::= all|"<CourseState>(,<CourseState>)*" <CourseStateList> ::= all|"<CourseState>(,<CourseState>)*"
<iCalUID> ::= <String> <iCalUID> ::= <String>
<MeetID> ::= <String>
Must match this Python Regular Expression: [a-z]{3}-[a-z]{4}-[a-z]{3}
<ResourceID> ::= <String> <ResourceID> ::= <String>
<ResourceIDList> ::= "<ResourceID>(,<ResourceID>)*" <ResourceIDList> ::= "<ResourceID>(,<ResourceID>)*"
<UniqueID> ::= id:<String> <UniqueID> ::= id:<String>
@@ -215,7 +217,8 @@
endtimeunspecified| endtimeunspecified|
extendedproperties| extendedproperties|
eventtype| eventtype|
<EventFocusTimePropertiesSubfieldName> focustimeproperties|
<EventFocusTimePropertiesSubfieldName>|
gadget| gadget|
guestscaninviteothers| guestscaninviteothers|
guestscanmodify| guestscanmodify|
@@ -229,7 +232,8 @@
organizer| organizer|
<EventOrganizerSubfieldName>| <EventOrganizerSubfieldName>|
originalstart|originalstarttime| originalstart|originalstarttime|
<EventOutOfOfficePropertiesSubfieldName> outofofficeproperties|
<EventOutOfOfficePropertiesSubfieldName>|
privatecopy| privatecopy|
recurrence| recurrence|
recurringeventid| recurringeventid|
@@ -320,6 +324,7 @@
(birthday <Date>)| (birthday <Date>)|
(color <EventColorName>)| (color <EventColorName>)|
(colorindex|colorid <EventColorIndex>)| (colorindex|colorid <EventColorIndex>)|
(conferencedata meet <MeetID>)|
(description <String>)| (description <String>)|
(end|endtime (allday <Date>)|<Time>)| (end|endtime (allday <Date>)|<Time>)|
(guestscaninviteothers <Boolean>)| (guestscaninviteothers <Boolean>)|

View File

@@ -3,9 +3,9 @@
Print the current version of Gam with details Print the current version of Gam with details
``` ```
gam version gam version
GAM 7.30.01 - https://github.com/GAM-team/GAM - pyinstaller GAM 7.30.03 - https://github.com/GAM-team/GAM - pyinstaller
GAM Team <google-apps-manager@googlegroups.com> GAM Team <google-apps-manager@googlegroups.com>
Python 3.14.1 64-bit final Python 3.14.2 64-bit final
macOS Tahoe 26.1 x86_64 macOS Tahoe 26.1 x86_64
Path: /Users/Admin/bin/gam7 Path: /Users/Admin/bin/gam7
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
@@ -15,9 +15,9 @@ Time: 2023-06-02T21:10:00-07:00
Print the current version of Gam with details and time offset information Print the current version of Gam with details and time offset information
``` ```
gam version timeoffset gam version timeoffset
GAM 7.30.01 - https://github.com/GAM-team/GAM - pyinstaller GAM 7.30.03 - https://github.com/GAM-team/GAM - pyinstaller
GAM Team <google-apps-manager@googlegroups.com> GAM Team <google-apps-manager@googlegroups.com>
Python 3.14.1 64-bit final Python 3.14.2 64-bit final
macOS Tahoe 26.1 x86_64 macOS Tahoe 26.1 x86_64
Path: /Users/Admin/bin/gam7 Path: /Users/Admin/bin/gam7
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
@@ -27,9 +27,9 @@ Your system time differs from www.googleapis.com by less than 1 second
Print the current version of Gam with extended details and SSL information Print the current version of Gam with extended details and SSL information
``` ```
gam version extended gam version extended
GAM 7.30.01 - https://github.com/GAM-team/GAM - pyinstaller GAM 7.30.03 - https://github.com/GAM-team/GAM - pyinstaller
GAM Team <google-apps-manager@googlegroups.com> GAM Team <google-apps-manager@googlegroups.com>
Python 3.14.1 64-bit final Python 3.14.2 64-bit final
macOS Tahoe 26.1 x86_64 macOS Tahoe 26.1 x86_64
Path: /Users/Admin/bin/gam7 Path: /Users/Admin/bin/gam7
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
@@ -68,7 +68,7 @@ MacOS High Sierra 10.13.6 x86_64
Path: /Users/Admin/bin/gam7 Path: /Users/Admin/bin/gam7
Version Check: Version Check:
Current: 5.35.08 Current: 5.35.08
Latest: 7.30.01 Latest: 7.30.03
echo $? echo $?
1 1
``` ```
@@ -76,7 +76,7 @@ echo $?
Print the current version number without details Print the current version number without details
``` ```
gam version simple gam version simple
7.30.01 7.30.03
``` ```
In Linux/MacOS you can do: In Linux/MacOS you can do:
``` ```
@@ -86,9 +86,9 @@ echo $VER
Print the current version of Gam and address of this Wiki Print the current version of Gam and address of this Wiki
``` ```
gam help gam help
GAM 7.30.01 - https://github.com/GAM-team/GAM GAM 7.30.03 - https://github.com/GAM-team/GAM
GAM Team <google-apps-manager@googlegroups.com> GAM Team <google-apps-manager@googlegroups.com>
Python 3.14.1 64-bit final Python 3.14.2 64-bit final
macOS Tahoe 26.1 x86_64 macOS Tahoe 26.1 x86_64
Path: /Users/Admin/bin/gam7 Path: /Users/Admin/bin/gam7
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com

View File

@@ -290,8 +290,6 @@ csv_output_timestamp_column
csv_output_users_audit csv_output_users_audit
Gam print commands that print objects belonging to users Gam print commands that print objects belonging to users
don't print rows for users that don't have any of the objects. don't print rows for users that don't have any of the objects.
The objects are: calendars, calendar ACLs, calendar events, delegates, filters,
forwarding addresses, sendas addresses, S/MIME certificates and tokens.
When csv_output_users_audit is true, a placeholder row will be output with the When csv_output_users_audit is true, a placeholder row will be output with the
user's email address; these rows will useful for auditing purposes only, user's email address; these rows will useful for auditing purposes only,
they can not be successfuly used in a gam csv command. they can not be successfuly used in a gam csv command.