mirror of
https://github.com/GAM-team/GAM.git
synced 2026-07-04 04:41:35 +00:00
1566 lines
63 KiB
Python
1566 lines
63 KiB
Python
"""GAM usage reports and activity reports."""
|
|
|
|
import arrow
|
|
import sys
|
|
|
|
import datetime
|
|
import re
|
|
|
|
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()
|
|
|
|
|
|
def _getMain():
|
|
return sys.modules['gam']
|
|
|
|
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}")
|
|
|
|
def doWhatIs():
|
|
def _showPrimaryType(entityType, email):
|
|
_getMain().printEntity([entityType, email])
|
|
|
|
def _showAliasType(entityType, email, primaryEntityType, primaryEmail):
|
|
_getMain().printEntity([entityType, email, primaryEntityType, primaryEmail])
|
|
|
|
cd = _getMain().buildGAPIObject(API.DIRECTORY)
|
|
email = _getMain().getEmailAddress()
|
|
showInfo = invitableCheck = True
|
|
while Cmd.ArgumentsRemaining():
|
|
myarg = _getMain().getArgument()
|
|
if myarg == 'noinfo':
|
|
showInfo = False
|
|
elif myarg == 'noinvitablecheck':
|
|
invitableCheck = False
|
|
else:
|
|
_getMain().unknownArgumentExit()
|
|
try:
|
|
result = _getMain().callGAPI(cd.users(), 'get',
|
|
throwReasons=GAPI.USER_GET_THROW_REASONS,
|
|
userKey=email, fields='id,primaryEmail')
|
|
if (result['primaryEmail'].lower() == email) or (result['id'] == email):
|
|
if showInfo:
|
|
_getMain().infoUsers(entityList=[email])
|
|
else:
|
|
_showPrimaryType(Ent.USER, email)
|
|
_getMain().setSysExitRC(_getMain().ENTITY_IS_A_USER_RC)
|
|
else:
|
|
if showInfo:
|
|
_getMain().infoAliases(entityList=[email])
|
|
else:
|
|
_showAliasType(Ent.USER_ALIAS, email, Ent.USER, result['primaryEmail'])
|
|
_getMain().setSysExitRC(_getMain().ENTITY_IS_A_USER_ALIAS_RC)
|
|
return
|
|
except (GAPI.userNotFound, GAPI.badRequest):
|
|
pass
|
|
except (GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.forbidden,
|
|
GAPI.backendError, GAPI.systemError):
|
|
_getMain().entityUnknownWarning(Ent.EMAIL, email)
|
|
_getMain().setSysExitRC(_getMain().ENTITY_IS_UKNOWN_RC)
|
|
return
|
|
try:
|
|
result = _getMain().callGAPI(cd.groups(), 'get',
|
|
throwReasons=GAPI.GROUP_GET_THROW_REASONS,
|
|
groupKey=email, fields='id,email')
|
|
if (result['email'].lower() == email) or (result['id'] == email):
|
|
if showInfo:
|
|
_getMain().infoGroups([email])
|
|
else:
|
|
_showPrimaryType(Ent.GROUP, email)
|
|
_getMain().setSysExitRC(_getMain().ENTITY_IS_A_GROUP_RC)
|
|
else:
|
|
if showInfo:
|
|
_getMain().infoAliases(entityList=[email])
|
|
else:
|
|
_showAliasType(Ent.GROUP_ALIAS, email, Ent.GROUP, result['email'])
|
|
_getMain().setSysExitRC(_getMain().ENTITY_IS_A_GROUP_ALIAS_RC)
|
|
return
|
|
except (GAPI.groupNotFound, GAPI.forbidden):
|
|
pass
|
|
except (GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.badRequest):
|
|
_getMain().entityUnknownWarning(Ent.EMAIL, email)
|
|
_getMain().setSysExitRC(_getMain().ENTITY_IS_UKNOWN_RC)
|
|
return
|
|
if not invitableCheck:
|
|
isInvitableUser = False
|
|
ci = None
|
|
else:
|
|
isInvitableUser, ci = _getMain()._getIsInvitableUser(None, email)
|
|
if isInvitableUser:
|
|
if showInfo:
|
|
name, user, ci = _getMain()._getCIUserInvitationsEntity(ci, email)
|
|
_getMain().infoCIUserInvitations(name, user, ci, None)
|
|
else:
|
|
_showPrimaryType(Ent.USER_INVITATION, email)
|
|
_getMain().setSysExitRC(_getMain().ENTITY_IS_AN_UNMANAGED_ACCOUNT_RC)
|
|
else:
|
|
_getMain().entityUnknownWarning(Ent.EMAIL, email)
|
|
_getMain().setSysExitRC(_getMain().ENTITY_IS_UKNOWN_RC)
|
|
|
|
def _adjustTryDate(errMsg, numDateChanges, limitDateChanges, prevTryDate):
|
|
match_date = re.match('Data for dates later than (.*) is not yet available. Please check back later', errMsg)
|
|
if match_date:
|
|
tryDate = match_date.group(1)
|
|
else:
|
|
match_date = re.match('Start date can not be later than (.*)', errMsg)
|
|
if match_date:
|
|
tryDate = match_date.group(1)
|
|
else:
|
|
match_date = re.match('End date greater than LastReportedDate.', errMsg)
|
|
if match_date:
|
|
tryDateTime = arrow.Arrow.strptime(prevTryDate, _getMain().YYYYMMDD_FORMAT).shift(days=-1)
|
|
tryDate = tryDateTime.strftime(_getMain().YYYYMMDD_FORMAT)
|
|
if (not match_date) or (numDateChanges > limitDateChanges >= 0):
|
|
_getMain().printWarningMessage(_getMain().DATA_NOT_AVALIABLE_RC, errMsg)
|
|
return None
|
|
return tryDate
|
|
|
|
def _checkDataRequiredServices(result, tryDate, dataRequiredServices, parameterServices=None, checkUserEmail=False):
|
|
# -1: Data not available:
|
|
# 0: Backup to earlier date
|
|
# 1: Data available
|
|
dataWarnings = result.get('warnings', [])
|
|
usageReports = result.get('usageReports', [])
|
|
# move to day before if we don't have at least one usageReport with parameters
|
|
if not usageReports or not usageReports[0].get('parameters', []):
|
|
tryDateTime = arrow.Arrow.strptime(tryDate, _getMain().YYYYMMDD_FORMAT).shift(days=-1)
|
|
return (0, tryDateTime.strftime(_getMain().YYYYMMDD_FORMAT), None)
|
|
for warning in dataWarnings:
|
|
if warning['code'] == 'PARTIAL_DATA_AVAILABLE':
|
|
for app in warning['data']:
|
|
if app['key'] == 'application' and app['value'] != 'docs' and app['value'] in dataRequiredServices:
|
|
tryDateTime = arrow.Arrow.strptime(tryDate, _getMain().YYYYMMDD_FORMAT).shift(days=-1)
|
|
return (0, tryDateTime.strftime(_getMain().YYYYMMDD_FORMAT), None)
|
|
elif warning['code'] == 'DATA_NOT_AVAILABLE':
|
|
for app in warning['data']:
|
|
if app['key'] == 'application' and app['value'] != 'docs' and app['value'] in dataRequiredServices:
|
|
return (-1, tryDate, None)
|
|
if parameterServices:
|
|
requiredServices = parameterServices.copy()
|
|
for item in usageReports[0].get('parameters', []):
|
|
if 'name' not in item:
|
|
continue
|
|
service, _ = item['name'].split(':', 1)
|
|
if service in requiredServices:
|
|
requiredServices.remove(service)
|
|
if not requiredServices:
|
|
break
|
|
else:
|
|
tryDateTime = arrow.Arrow.strptime(tryDate, _getMain().YYYYMMDD_FORMAT).shift(days=-1)
|
|
return (0, tryDateTime.strftime(_getMain().YYYYMMDD_FORMAT), None)
|
|
if checkUserEmail:
|
|
if 'entity' not in usageReports[0] or 'userEmail' not in usageReports[0]['entity']:
|
|
tryDateTime = arrow.Arrow.strptime(tryDate, _getMain().YYYYMMDD_FORMAT).shift(days=-1)
|
|
return (0, tryDateTime.strftime(_getMain().YYYYMMDD_FORMAT), None)
|
|
return (1, tryDate, usageReports)
|
|
|
|
CUSTOMER_REPORT_SERVICES = {
|
|
'accounts',
|
|
'app_maker',
|
|
'apps_scripts',
|
|
'calendar',
|
|
'chat',
|
|
'classroom',
|
|
'cros',
|
|
'device_management',
|
|
'docs',
|
|
'drive',
|
|
'gmail',
|
|
'gplus',
|
|
'meet',
|
|
'sites',
|
|
}
|
|
|
|
USER_REPORT_SERVICES = {
|
|
'accounts',
|
|
'chat',
|
|
'classroom',
|
|
'docs',
|
|
'drive',
|
|
'gmail',
|
|
'gplus',
|
|
}
|
|
|
|
CUSTOMER_USER_CHOICES = {'customer', 'user'}
|
|
|
|
# gam report usageparameters customer|user [todrive <ToDriveAttribute>*]
|
|
def doReportUsageParameters():
|
|
report = _getMain().getChoice(CUSTOMER_USER_CHOICES)
|
|
csvPF = _getMain().CSVPrintFile(['parameter'], 'sortall')
|
|
_getMain().getTodriveOnly(csvPF)
|
|
rep = _getMain().buildGAPIObject(API.REPORTS)
|
|
if report == 'customer':
|
|
service = rep.customerUsageReports()
|
|
dataRequiredServices = CUSTOMER_REPORT_SERVICES
|
|
kwargs = {}
|
|
else: # 'user'
|
|
service = rep.userUsageReport()
|
|
dataRequiredServices = USER_REPORT_SERVICES
|
|
kwargs = {'userKey': _getMain()._getAdminEmail()}
|
|
customerId = GC.Values[GC.CUSTOMER_ID]
|
|
if customerId == GC.MY_CUSTOMER:
|
|
customerId = None
|
|
tryDate = _getMain().todaysDate().strftime(_getMain().YYYYMMDD_FORMAT)
|
|
allParameters = set()
|
|
while True:
|
|
try:
|
|
result = _getMain().callGAPI(service, 'get',
|
|
throwReasons=[GAPI.INVALID, GAPI.BAD_REQUEST],
|
|
date=tryDate, customerId=customerId, fields='warnings,usageReports(parameters(name))', **kwargs)
|
|
fullData, tryDate, usageReports = _checkDataRequiredServices(result, tryDate, dataRequiredServices)
|
|
if fullData < 0:
|
|
_getMain().printWarningMessage(_getMain().DATA_NOT_AVALIABLE_RC, Msg.NO_USAGE_PARAMETERS_DATA_AVAILABLE)
|
|
return
|
|
if usageReports:
|
|
for parameter in usageReports[0]['parameters']:
|
|
name = parameter.get('name')
|
|
if name:
|
|
allParameters.add(name)
|
|
if fullData == 1:
|
|
break
|
|
except GAPI.badRequest:
|
|
_getMain().printErrorMessage(_getMain().BAD_REQUEST_RC, Msg.BAD_REQUEST)
|
|
return
|
|
except GAPI.invalid as e:
|
|
tryDate = _adjustTryDate(str(e), 0, -1, tryDate)
|
|
if not tryDate:
|
|
break
|
|
for parameter in sorted(allParameters):
|
|
csvPF.WriteRow({'parameter': parameter})
|
|
csvPF.writeCSVfile(f'{report.capitalize()} Report Usage Parameters')
|
|
|
|
def getUserOrgUnits(cd, orgUnit, orgUnitId):
|
|
try:
|
|
if orgUnit == orgUnitId:
|
|
orgUnit = _getMain().callGAPI(cd.orgunits(), 'get',
|
|
throwReasons=GAPI.ORGUNIT_GET_THROW_REASONS,
|
|
customerId=GC.Values[GC.CUSTOMER_ID], orgUnitPath=orgUnit, fields='orgUnitPath')['orgUnitPath']
|
|
_getMain().printGettingAllEntityItemsForWhom(Ent.USER, orgUnit, qualifier=Msg.IN_THE.format(Ent.Singular(Ent.ORGANIZATIONAL_UNIT)),
|
|
entityType=Ent.ORGANIZATIONAL_UNIT)
|
|
result = _getMain().callGAPIpages(cd.users(), 'list', 'users',
|
|
pageMessage=_getMain().getPageMessageForWhom(),
|
|
throwReasons=[GAPI.INVALID_ORGUNIT, GAPI.ORGUNIT_NOT_FOUND,
|
|
GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
|
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
customer=GC.Values[GC.CUSTOMER_ID], query=_getMain().orgUnitPathQuery(orgUnit, None, None), orderBy='email',
|
|
fields='nextPageToken,users(primaryEmail,orgUnitPath)', maxResults=GC.Values[GC.USER_MAX_RESULTS])
|
|
userOrgUnits = {}
|
|
for user in result:
|
|
userOrgUnits[user['primaryEmail']] = user['orgUnitPath']
|
|
return userOrgUnits
|
|
except (GAPI.badRequest, GAPI.invalidInput, GAPI.invalidOrgunit, GAPI.orgunitNotFound, GAPI.backendError,
|
|
GAPI.invalidCustomerId, GAPI.loginRequired, GAPI.resourceNotFound, GAPI.forbidden):
|
|
checkEntityDNEorAccessErrorExit(cd, Ent.ORGANIZATIONAL_UNIT, orgUnit)
|
|
|
|
# Convert report mb item to gb
|
|
def convertReportMBtoGB(name, item):
|
|
if item is not None:
|
|
item['intValue'] = f"{int(item['intValue'])/1024:.2f}"
|
|
return name.replace('_in_mb', '_in_gb')
|
|
|
|
REPORTS_PARAMETERS_SIMPLE_TYPES = ['intValue', 'boolValue', 'datetimeValue', 'stringValue']
|
|
|
|
# gam report usage user [todrive <ToDriveAttribute>*]
|
|
# [(user all|<UserItem>)|(orgunit|org|ou <OrgUnitPath> [showorgunit])|(select <UserTypeEntity>)]
|
|
# [([start|startdate <Date>] [end|enddate <Date>])|(range <Date> <Date>)|
|
|
# thismonth|(previousmonths <Integer>)]
|
|
# [skipdates <Date>[:<Date>](,<Date>[:<Date>])*] [skipdaysofweek <DayOfWeek>(,<DayOfWeek>)*]
|
|
# [fields|parameters <String>)]
|
|
# [convertmbtogb]
|
|
# (addcsvdata <FieldName> <String>)*
|
|
# gam report usage customer [todrive <ToDriveAttribute>*]
|
|
# [([start|startdate <Date>] [end|enddate <Date>])|(range <Date> <Date>)|
|
|
# thismonth|(previousmonths <Integer>)]
|
|
# [skipdates <Date>[:<Date>](,<Date>[:<Date>])*] [skipdaysofweek <DayOfWeek>(,<DayOfWeek>)*]
|
|
# [fields|parameters <String>)]
|
|
# [convertmbtogb]
|
|
# (addcsvdata <FieldName> <String>)*
|
|
def doReportUsage():
|
|
def usageEntitySelectors():
|
|
selectorChoices = Cmd.USER_ENTITY_SELECTORS+Cmd.USER_CSVDATA_ENTITY_SELECTORS
|
|
if GC.Values[GC.USER_SERVICE_ACCOUNT_ACCESS_ONLY]:
|
|
selectorChoices += Cmd.SERVICE_ACCOUNT_ONLY_ENTITY_SELECTORS[:]+[Cmd.ENTITY_USER, Cmd.ENTITY_USERS]
|
|
else:
|
|
selectorChoices += Cmd.BASE_ENTITY_SELECTORS[:]+Cmd.USER_ENTITIES[:]
|
|
return selectorChoices
|
|
|
|
def validateYYYYMMDD(argstr):
|
|
if argstr in _getMain().TODAY_NOW or argstr[0] in _getMain().PLUS_MINUS:
|
|
if argstr == 'NOW':
|
|
argstr = 'TODAY'
|
|
deltaDate = _getMain().getDelta(argstr, _getMain().DELTA_DATE_PATTERN)
|
|
if deltaDate is None:
|
|
Cmd.Backup()
|
|
_getMain().invalidArgumentExit(_getMain().DELTA_DATE_FORMAT_REQUIRED)
|
|
return deltaDate
|
|
try:
|
|
argDate = arrow.Arrow.strptime(argstr, _getMain().YYYYMMDD_FORMAT)
|
|
return arrow.Arrow(argDate.year, argDate.month, argDate.day, tzinfo=GC.Values[GC.TIMEZONE])
|
|
except ValueError:
|
|
Cmd.Backup()
|
|
_getMain().invalidArgumentExit(_getMain().YYYYMMDD_FORMAT_REQUIRED)
|
|
|
|
report = _getMain().getChoice(CUSTOMER_USER_CHOICES)
|
|
rep = _getMain().buildGAPIObject(API.REPORTS)
|
|
titles = ['date']
|
|
if report == 'customer':
|
|
fullDataServices = CUSTOMER_REPORT_SERVICES
|
|
userReports = False
|
|
service = rep.customerUsageReports()
|
|
kwargs = [{}]
|
|
else: # 'user'
|
|
fullDataServices = USER_REPORT_SERVICES
|
|
userReports = True
|
|
service = rep.userUsageReport()
|
|
kwargs = [{'userKey': 'all'}]
|
|
titles.append('user')
|
|
csvPF = _getMain().CSVPrintFile()
|
|
customerId = GC.Values[GC.CUSTOMER_ID]
|
|
if customerId == GC.MY_CUSTOMER:
|
|
customerId = None
|
|
parameters = set()
|
|
convertMbToGb = select = showOrgUnit = False
|
|
userKey = 'all'
|
|
cd = orgUnit = orgUnitId = None
|
|
userOrgUnits = {}
|
|
startEndTime = _getMain().StartEndTime('startdate', 'enddate', 'date')
|
|
skipDayNumbers = []
|
|
skipDates = set()
|
|
addCSVData = {}
|
|
while Cmd.ArgumentsRemaining():
|
|
myarg = _getMain().getArgument()
|
|
if csvPF and myarg == 'todrive':
|
|
csvPF.GetTodriveParameters()
|
|
elif myarg in {'start', 'startdate', 'end', 'enddate', 'range', 'thismonth', 'previousmonths'}:
|
|
startEndTime.Get(myarg)
|
|
elif userReports and myarg in {'ou', 'org', 'orgunit'}:
|
|
if cd is None:
|
|
cd = _getMain().buildGAPIObject(API.DIRECTORY)
|
|
orgUnit, orgUnitId = _getMain().getOrgUnitId(cd)
|
|
select = False
|
|
elif userReports and myarg == 'showorgunit':
|
|
showOrgUnit = True
|
|
elif myarg in {'fields', 'parameters'}:
|
|
for field in _getMain().getString(Cmd.OB_STRING).replace(',', ' ').split():
|
|
if ':' in field:
|
|
repsvc, _ = field.split(':', 1)
|
|
if repsvc in fullDataServices:
|
|
parameters.add(field)
|
|
else:
|
|
_getMain().invalidChoiceExit(repsvc, fullDataServices, True)
|
|
else:
|
|
Cmd.Backup()
|
|
_getMain().invalidArgumentExit('service:parameter')
|
|
elif myarg == 'skipdates':
|
|
for skip in _getMain().getString(Cmd.OB_STRING).upper().split(','):
|
|
if skip.find(':') == -1:
|
|
skipDates.add(validateYYYYMMDD(skip))
|
|
else:
|
|
skipStart, skipEnd = skip.split(':', 1)
|
|
skipStartDate = validateYYYYMMDD(skipStart)
|
|
skipEndDate = validateYYYYMMDD(skipEnd)
|
|
if skipEndDate < skipStartDate:
|
|
Cmd.Backup()
|
|
_getMain().usageErrorExit(Msg.INVALID_DATE_TIME_RANGE.format(myarg, skipEnd, myarg, skipStart))
|
|
while skipStartDate <= skipEndDate:
|
|
skipDates.add(skipStartDate)
|
|
skipStartDate = skipStartDate.shift(days=1)
|
|
elif myarg == 'skipdaysofweek':
|
|
skipdaynames = _getMain().getString(Cmd.OB_STRING).lower().split(',')
|
|
dow = [d.lower() for d in _getMain().DAYS_OF_WEEK]
|
|
skipDayNumbers = [dow.index(d) for d in skipdaynames if d in dow]
|
|
elif userReports and myarg == 'user':
|
|
userKey = _getMain().getString(Cmd.OB_EMAIL_ADDRESS)
|
|
orgUnit = orgUnitId = None
|
|
select = False
|
|
elif userReports and (myarg == 'select' or myarg in usageEntitySelectors()):
|
|
if myarg != 'select':
|
|
Cmd.Backup()
|
|
_, users = _getMain().getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)
|
|
orgUnit = orgUnitId = None
|
|
select = True
|
|
elif myarg == 'convertmbtogb':
|
|
convertMbToGb = True
|
|
elif myarg == 'addcsvdata':
|
|
_getMain().getAddCSVData(addCSVData)
|
|
else:
|
|
_getMain().unknownArgumentExit()
|
|
if startEndTime.endDateTime is None:
|
|
startEndTime.endDateTime = _getMain().todaysDate()
|
|
if startEndTime.startDateTime is None:
|
|
startEndTime.startDateTime = startEndTime.endDateTime.shift(days=-30)
|
|
startDateTime = startEndTime.startDateTime
|
|
startDate = startDateTime.strftime(_getMain().YYYYMMDD_FORMAT)
|
|
endDateTime = startEndTime.endDateTime
|
|
endDate = endDateTime.strftime(_getMain().YYYYMMDD_FORMAT)
|
|
startUseDate = endUseDate = None
|
|
if not orgUnitId:
|
|
showOrgUnit = False
|
|
if userReports:
|
|
if select:
|
|
Ent.SetGetting(Ent.REPORT)
|
|
kwargs = [{'userKey': _getMain().normalizeEmailAddressOrUID(user)} for user in users]
|
|
elif userKey == 'all':
|
|
if orgUnitId:
|
|
kwargs[0]['orgUnitID'] = orgUnitId
|
|
userOrgUnits = getUserOrgUnits(cd, orgUnit, orgUnitId)
|
|
forWhom = f'users in orgUnit {orgUnit}'
|
|
else:
|
|
forWhom = 'all users'
|
|
_getMain().printGettingEntityItemForWhom(Ent.REPORT, forWhom)
|
|
else:
|
|
Ent.SetGetting(Ent.REPORT)
|
|
kwargs = [{'userKey': _getMain().normalizeEmailAddressOrUID(userKey)}]
|
|
_getMain().printGettingEntityItemForWhom(Ent.REPORT, kwargs[0]['userKey'])
|
|
if showOrgUnit:
|
|
titles.append('orgUnitPath')
|
|
else:
|
|
pageMessage = None
|
|
if addCSVData:
|
|
titles.extend(sorted(addCSVData.keys()))
|
|
csvPF.SetTitles(titles)
|
|
csvPF.SetSortAllTitles()
|
|
parameters = ','.join(parameters) if parameters else None
|
|
while startDateTime <= endDateTime:
|
|
if startDateTime.weekday() in skipDayNumbers or startDateTime in skipDates:
|
|
startDateTime = startDateTime.shift(days=1)
|
|
continue
|
|
useDate = startDateTime.strftime(_getMain().YYYYMMDD_FORMAT)
|
|
startDateTime = startDateTime.shift(days=1)
|
|
try:
|
|
for kwarg in kwargs:
|
|
if userReports:
|
|
if not select and userKey == 'all':
|
|
pageMessage = _getMain().getPageMessageForWhom(forWhom, showDate=useDate)
|
|
else:
|
|
pageMessage = _getMain().getPageMessageForWhom(kwarg['userKey'], showDate=useDate)
|
|
try:
|
|
usage = _getMain().callGAPIpages(service, 'get', 'usageReports',
|
|
pageMessage=pageMessage,
|
|
throwReasons=[GAPI.INVALID, GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.FORBIDDEN],
|
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
customerId=customerId, date=useDate,
|
|
parameters=parameters, **kwarg)
|
|
except GAPI.badRequest:
|
|
continue
|
|
for entity in usage:
|
|
row = {'date': useDate}
|
|
if userReports:
|
|
if 'userEmail' in entity['entity']:
|
|
row['user'] = entity['entity']['userEmail']
|
|
if showOrgUnit:
|
|
row['orgUnitPath'] = userOrgUnits.get(row['user'], _getMain().UNKNOWN)
|
|
else:
|
|
row['user'] = _getMain().UNKNOWN
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
for item in entity.get('parameters', []):
|
|
if 'name' not in item:
|
|
continue
|
|
name = item['name']
|
|
if name == 'cros:device_version_distribution':
|
|
versions = {}
|
|
for version in item['msgValue']:
|
|
versions[version['version_number']] = version['num_devices']
|
|
for k, v in sorted(versions.items(), reverse=True):
|
|
title = f'cros:num_devices_chrome_{k}'
|
|
row[title] = v
|
|
else:
|
|
for ptype in REPORTS_PARAMETERS_SIMPLE_TYPES:
|
|
if ptype in item:
|
|
if ptype != 'datetimeValue':
|
|
if convertMbToGb and name.endswith('_in_mb'):
|
|
name = convertReportMBtoGB(name, item)
|
|
row[name] = item[ptype]
|
|
else:
|
|
row[name] = _getMain().formatLocalTime(item[ptype])
|
|
break
|
|
else:
|
|
row[name] = ''
|
|
if not startUseDate:
|
|
startUseDate = useDate
|
|
endUseDate = useDate
|
|
csvPF.WriteRowTitles(row)
|
|
except GAPI.invalid as e:
|
|
_getMain().stderrWarningMsg(str(e))
|
|
break
|
|
except GAPI.invalidInput as e:
|
|
_getMain().systemErrorExit(_getMain().GOOGLE_API_ERROR_RC, str(e))
|
|
except GAPI.forbidden as e:
|
|
_getMain().accessErrorExit(None, str(e))
|
|
if startUseDate:
|
|
reportName = f'{report.capitalize()} Usage Report - {startUseDate}:{endUseDate}'
|
|
else:
|
|
reportName = f'{report.capitalize()} Usage Report - {startDate}:{endDate} - No Data'
|
|
csvPF.writeCSVfile(reportName)
|
|
|
|
NL_SPACES_PATTERN = re.compile(r'\n +')
|
|
DISABLED_REASON_TIME_PATTERN = re.compile(r'.*(\d{4}/\d{2}/\d{2}-\d{2}:\d{2}:\d{2})')
|
|
|
|
REPORT_ALIASES_CHOICE_MAP = {
|
|
'access': 'accesstransparency',
|
|
'calendars': 'calendar',
|
|
'cloud': 'gcp',
|
|
'currents': 'gplus',
|
|
'customers': 'customer',
|
|
'domain': 'customer',
|
|
'devices': 'mobile',
|
|
'doc': 'drive',
|
|
'docs': 'drive',
|
|
'enterprisegroups': 'groupsenterprise',
|
|
'gemini': 'geminiinworkspaceapps',
|
|
'geminiforworkspace': 'geminiinworkspaceapps',
|
|
'group': 'groups',
|
|
'google+': 'gplus',
|
|
'hangoutsmeet': 'meet',
|
|
'logins': 'login',
|
|
'lookerstudio': 'datastudio',
|
|
'oauthtoken': 'token',
|
|
'tokens': 'token',
|
|
'users': 'user',
|
|
}
|
|
|
|
REPORT_CHOICE_MAP = {
|
|
'accessevaluation': 'access_evaluation',
|
|
'accesstransparency': 'access_transparency',
|
|
'admin': 'admin',
|
|
'admindataaction': 'admin_data_action',
|
|
'assignments': 'assignments',
|
|
'calendar': 'calendar',
|
|
'chat': 'chat',
|
|
'chrome': 'chrome',
|
|
'classroom': 'classroom',
|
|
'cloudsearch': 'cloud_search',
|
|
'contacts': 'contacts',
|
|
'contextawareaccess': 'context_aware_access',
|
|
'customer': 'customer',
|
|
'datamigration': 'data_migration',
|
|
'datastudio': 'data_studio',
|
|
'directorysync': 'directory_sync',
|
|
'drive': 'drive',
|
|
'gcp': 'gcp',
|
|
'geminiinworkspaceapps': 'gemini_in_workspace_apps',
|
|
'gmail': 'gmail',
|
|
'gplus': 'gplus',
|
|
'graduation':'graduation',
|
|
'groups': 'groups',
|
|
'groupsenterprise': 'groups_enterprise',
|
|
'jamboard': 'jamboard',
|
|
'keep': 'keep',
|
|
'ldap': 'ldap',
|
|
'login': 'login',
|
|
'meet': 'meet',
|
|
'meethardware': 'meet_hardware',
|
|
'mobile': 'mobile',
|
|
'profile': 'profile',
|
|
'rules': 'rules',
|
|
'saml': 'saml',
|
|
'takeout': 'takeout',
|
|
'tasks': 'tasks',
|
|
'token': 'token',
|
|
'usage': 'usage',
|
|
'usageparameters': 'usageparameters',
|
|
'user': 'user',
|
|
'useraccounts': 'user_accounts',
|
|
'vault': 'vault',
|
|
}
|
|
|
|
REPORT_ACTIVITIES_UPPERCASE_EVENTS = {
|
|
'access_transparency',
|
|
'admin',
|
|
'chrome',
|
|
'cloud_search',
|
|
'context_aware_access',
|
|
'data_migration',
|
|
'data_studio',
|
|
'directory_sync',
|
|
'gcp',
|
|
'jamboard',
|
|
'meet_hardware',
|
|
'mobile',
|
|
'profile',
|
|
'takeout',
|
|
}
|
|
|
|
REPORT_ACTIVITIES_FILTER_MAP = {
|
|
'applicationinfofilter': 'applicationInfoFilter',
|
|
'groupidfilter': 'groupIdFilter',
|
|
'networkinfofilter': 'networkInfoFilter',
|
|
'resourcedetailsfilter': 'resourceDetailsFilter',
|
|
'statusfilter': 'statusFilter',
|
|
}
|
|
|
|
REPORT_ACTIVITIES_TIME_OBJECTS = {'time'}
|
|
|
|
# gam report <ActivityApplictionName> [todrive <ToDriveAttribute>*]
|
|
# [(user all|<UserItem>)|(orgunit|org|ou <OrgUnitPath> [showorgunit])|(select <UserTypeEntity>)]
|
|
# [userisactor]
|
|
# [([start <Time>] [end <Time>])|(range <Time> <Time>)|
|
|
# yesterday|today|thismonth|(previousmonths <Integer>)]
|
|
# [filter <String> (filtertime<String> <Time>)*]
|
|
# [event|events <EventNameList>] [ip <String>]
|
|
# [gmaileventtypes <NumberRangeList>]
|
|
# [groupidfilter <String>] [resourcedetailsfilter <String>]
|
|
# [networkinfofilter <String>] [statusfilter <String>]
|
|
# [applicationinfofilter <String>] [includesensitivedata]
|
|
# [notimesort]
|
|
# [maxactivities <Number>] [maxevents <Number>] [maxresults <Number>]
|
|
# [countsonly [bydate|summary] [eventrowfilter]]
|
|
# (addcsvdata <FieldName> <String>)* [shownoactivities]
|
|
# gam report users|user [todrive <ToDriveAttribute>*]
|
|
# [(user all|<UserItem>)|(orgunit|org|ou <OrgUnitPath> [showorgunit])|(select <UserTypeEntity>)]
|
|
# [allverifyuser <UserItem>]
|
|
# [(date <Date>)|(range <Date> <Date>)|
|
|
# yesterday|today|thismonth|(previousmonths <Integer>)]
|
|
# [nodatechange | (fulldatarequired all|<UserServiceNameList>)]
|
|
# [filter <String> (filtertime<String> <Time>)*]
|
|
# [(fields|parameters <String>)|(services <UserServiceNameList>)]
|
|
# [aggregatebydate|aggregatebyuser [Boolean]]
|
|
# [maxresults <Number>]
|
|
# [convertmbtogb]
|
|
# (addcsvdata <FieldName> <String>)*
|
|
# gam report customers|customer|domain [todrive <ToDriveAttribute>*]
|
|
# [(date <Date>)|(range <Date> <Date>)|
|
|
# yesterday|today|thismonth|(previousmonths <Integer>)]
|
|
# [nodatechange | (fulldatarequired all|<CustomerServiceNameList>)]
|
|
# [(fields|parameters <String>)|(services <CustomerServiceNameList>)] [noauthorizedapps]
|
|
# [convertmbtogb]
|
|
# (addcsvdata <FieldName> <String>)*
|
|
def doReport():
|
|
def processUserUsage(usage, lastDate):
|
|
if not usage:
|
|
return (True, lastDate)
|
|
if lastDate == usage[0]['date']:
|
|
return (False, lastDate)
|
|
lastDate = usage[0]['date']
|
|
for user_report in usage:
|
|
if 'entity' not in user_report:
|
|
continue
|
|
row = {'date': user_report['date']}
|
|
if 'userEmail' in user_report['entity']:
|
|
row['email'] = user_report['entity']['userEmail']
|
|
if showOrgUnit:
|
|
row['orgUnitPath'] = userOrgUnits.get(row['email'], _getMain().UNKNOWN)
|
|
else:
|
|
row['email'] = _getMain().UNKNOWN
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
for item in user_report.get('parameters', []):
|
|
if 'name' not in item:
|
|
continue
|
|
name = item['name']
|
|
repsvc, _ = name.split(':', 1)
|
|
if repsvc not in includeServices:
|
|
continue
|
|
if name == 'accounts:disabled_reason' and 'stringValue' in item:
|
|
mg = DISABLED_REASON_TIME_PATTERN.match(item['stringValue'])
|
|
if mg:
|
|
try:
|
|
disabledTime = _getMain().formatLocalTime(arrow.Arrow.strptime(mg.group(1), '%Y/%m/%d-%H:%M:%S').replace(tzinfo='UTC').strftime(_getMain().YYYYMMDDTHHMMSSZ_FORMAT))
|
|
row['accounts:disabled_time'] = disabledTime
|
|
csvPF.AddTitles('accounts:disabled_time')
|
|
except ValueError:
|
|
pass
|
|
elif convertMbToGb and name.endswith('_in_mb'):
|
|
name = convertReportMBtoGB(name, item)
|
|
csvPF.AddTitles(name)
|
|
for ptype in REPORTS_PARAMETERS_SIMPLE_TYPES:
|
|
if ptype in item:
|
|
if ptype != 'datetimeValue':
|
|
row[name] = item[ptype]
|
|
else:
|
|
row[name] = _getMain().formatLocalTime(item[ptype])
|
|
break
|
|
else:
|
|
row[name] = ''
|
|
csvPF.WriteRow(row)
|
|
return (True, lastDate)
|
|
|
|
def processAggregateUserUsageByUser(usage, lastDate):
|
|
if not usage:
|
|
return (True, lastDate)
|
|
if lastDate == usage[0]['date']:
|
|
return (False, lastDate)
|
|
lastDate = usage[0]['date']
|
|
for user_report in usage:
|
|
if 'entity' not in user_report:
|
|
continue
|
|
if 'userEmail' not in user_report['entity']:
|
|
continue
|
|
email = user_report['entity']['userEmail']
|
|
for item in user_report.get('parameters', []):
|
|
if 'name' not in item:
|
|
continue
|
|
name = item['name']
|
|
repsvc, _ = name.split(':', 1)
|
|
if repsvc not in includeServices:
|
|
continue
|
|
if 'intValue' in item:
|
|
if convertMbToGb and name.endswith('_in_mb'):
|
|
name = convertReportMBtoGB(name, None)
|
|
csvPF.AddTitles(name)
|
|
eventCounts.setdefault(email, {})
|
|
eventCounts[email].setdefault(name, 0)
|
|
eventCounts[email][name] += int(item['intValue'])
|
|
return (True, lastDate)
|
|
|
|
def processAggregateUserUsageByDate(usage, lastDate):
|
|
if not usage:
|
|
return (True, lastDate)
|
|
if lastDate == usage[0]['date']:
|
|
return (False, lastDate)
|
|
lastDate = usage[0]['date']
|
|
for user_report in usage:
|
|
if 'entity' not in user_report:
|
|
continue
|
|
for item in user_report.get('parameters', []):
|
|
if 'name' not in item:
|
|
continue
|
|
name = item['name']
|
|
repsvc, _ = name.split(':', 1)
|
|
if repsvc not in includeServices:
|
|
continue
|
|
if 'intValue' in item:
|
|
if convertMbToGb and name.endswith('_in_mb'):
|
|
name = convertReportMBtoGB(name, None)
|
|
csvPF.AddTitles(name)
|
|
eventCounts.setdefault(lastDate, {})
|
|
eventCounts[lastDate].setdefault(name, 0)
|
|
eventCounts[lastDate][name] += int(item['intValue'])
|
|
return (True, lastDate)
|
|
|
|
def processCustomerUsageOneRow(usage, lastDate):
|
|
if not usage:
|
|
return (True, lastDate)
|
|
if lastDate == usage[0]['date']:
|
|
return (False, lastDate)
|
|
lastDate = usage[0]['date']
|
|
row = {'date': lastDate}
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
for item in usage[0].get('parameters', []):
|
|
if 'name' not in item:
|
|
continue
|
|
name = item['name']
|
|
repsvc, _ = name.split(':', 1)
|
|
if repsvc not in includeServices:
|
|
continue
|
|
for ptype in REPORTS_PARAMETERS_SIMPLE_TYPES:
|
|
if ptype in item:
|
|
if convertMbToGb and name.endswith('_in_mb'):
|
|
name = convertReportMBtoGB(name, item)
|
|
csvPF.AddTitles(name)
|
|
if ptype != 'datetimeValue':
|
|
row[name] = item[ptype]
|
|
else:
|
|
row[name] = _getMain().formatLocalTime(item[ptype])
|
|
break
|
|
else:
|
|
if 'msgValue' in item:
|
|
if name == 'accounts:authorized_apps':
|
|
if noAuthorizedApps:
|
|
continue
|
|
for app in item['msgValue']:
|
|
appName = f'App: {_getMain().escapeCRsNLs(app["client_name"])}'
|
|
for key in ['num_users', 'client_id']:
|
|
title = f'{appName}{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}{key}'
|
|
csvPF.AddTitles(title)
|
|
row[title] = app[key]
|
|
elif name == 'cros:device_version_distribution':
|
|
versions = {}
|
|
for version in item['msgValue']:
|
|
versions[version['version_number']] = version['num_devices']
|
|
for k, v in sorted(versions.items(), reverse=True):
|
|
title = f'cros:device_version{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}{k}'
|
|
csvPF.AddTitles(title)
|
|
row[title] = v
|
|
else:
|
|
values = []
|
|
for subitem in item['msgValue']:
|
|
if 'count' in subitem:
|
|
mycount = myvalue = None
|
|
for key, value in subitem.items():
|
|
if key == 'count':
|
|
mycount = value
|
|
else:
|
|
myvalue = value
|
|
if mycount and myvalue:
|
|
values.append(f'{myvalue}:{mycount}')
|
|
value = ' '.join(values)
|
|
elif 'version_number' in subitem and 'num_devices' in subitem:
|
|
values.append(f'{subitem["version_number"]}:{subitem["num_devices"]}')
|
|
else:
|
|
continue
|
|
value = ' '.join(sorted(values, reverse=True))
|
|
csvPF.AddTitles(name)
|
|
row['name'] = value
|
|
csvPF.WriteRow(row)
|
|
return (True, lastDate)
|
|
|
|
def processCustomerUsage(usage, lastDate):
|
|
if not usage:
|
|
return (True, lastDate)
|
|
if lastDate == usage[0]['date']:
|
|
return (False, lastDate)
|
|
lastDate = usage[0]['date']
|
|
for item in usage[0].get('parameters', []):
|
|
if 'name' not in item:
|
|
continue
|
|
name = item['name']
|
|
repsvc, _ = name.split(':', 1)
|
|
if repsvc not in includeServices:
|
|
continue
|
|
for ptype in REPORTS_PARAMETERS_SIMPLE_TYPES:
|
|
if ptype in item:
|
|
row = {'date': lastDate}
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
if ptype != 'datetimeValue':
|
|
if convertMbToGb and name.endswith('_in_mb'):
|
|
name = convertReportMBtoGB(name, item)
|
|
row.update({'name': name, 'value': item[ptype]})
|
|
else:
|
|
row.update({'name': name, 'value': _getMain().formatLocalTime(item[ptype])})
|
|
csvPF.WriteRow(row)
|
|
break
|
|
else:
|
|
if 'msgValue' in item:
|
|
if name == 'accounts:authorized_apps':
|
|
if noAuthorizedApps:
|
|
continue
|
|
for subitem in item['msgValue']:
|
|
app = {'date': lastDate}
|
|
if addCSVData:
|
|
app.update(addCSVData)
|
|
for an_item in subitem:
|
|
if an_item == 'client_name':
|
|
app['name'] = f'App: {_getMain().escapeCRsNLs(subitem[an_item])}'
|
|
elif an_item == 'num_users':
|
|
app['value'] = f'{subitem[an_item]} users'
|
|
elif an_item == 'client_id':
|
|
app['client_id'] = subitem[an_item]
|
|
authorizedApps.append(app)
|
|
elif name == 'cros:device_version_distribution':
|
|
values = []
|
|
for subitem in item['msgValue']:
|
|
values.append(f'{subitem["version_number"]}:{subitem["num_devices"]}')
|
|
row = {'date': lastDate}
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
row.update({'name': name, 'value': ' '.join(sorted(values, reverse=True))})
|
|
csvPF.WriteRow(row)
|
|
else:
|
|
values = []
|
|
for subitem in item['msgValue']:
|
|
if 'count' in subitem:
|
|
mycount = myvalue = None
|
|
for key, value in subitem.items():
|
|
if key == 'count':
|
|
mycount = value
|
|
else:
|
|
myvalue = value
|
|
if mycount and myvalue:
|
|
values.append(f'{myvalue}:{mycount}')
|
|
else:
|
|
continue
|
|
row = {'date': lastDate}
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
row.update({'name': name, 'value': ' '.join(sorted(values, reverse=True))})
|
|
csvPF.WriteRow(row)
|
|
csvPF.SortRowsTwoTitles('date', 'name', False)
|
|
if authorizedApps:
|
|
csvPF.AddTitle('client_id')
|
|
for row in sorted(authorizedApps, key=lambda k: (k['date'], k['name'].lower())):
|
|
csvPF.WriteRow(row)
|
|
return (True, lastDate)
|
|
|
|
def _computeUsedQuotaInPercentage(events):
|
|
if ('accounts:total_quota_in_mb' in events) and (events['accounts:total_quota_in_mb'] > 0) and ('accounts:used_quota_in_mb' in events):
|
|
events['accounts:used_quota_in_percentage'] = int(events['accounts:used_quota_in_mb']/events['accounts:total_quota_in_mb']*100)
|
|
else:
|
|
events['accounts:used_quota_in_percentage'] = 0
|
|
|
|
def _getActivitiesFilters(myarg):
|
|
if myarg in REPORT_ACTIVITIES_FILTER_MAP:
|
|
kwargs[REPORT_ACTIVITIES_FILTER_MAP[myarg]] = _getMain().getString(Cmd.OB_STRING)
|
|
return True
|
|
return False
|
|
|
|
# dynamically extend our choices with other reports Google dynamically adds
|
|
rep = _getMain().buildGAPIObject(API.REPORTS)
|
|
dyn_choices = rep._rootDesc \
|
|
.get('resources', {}) \
|
|
.get('activities', {}) \
|
|
.get('methods', {}) \
|
|
.get('list', {}) \
|
|
.get('parameters', {}) \
|
|
.get('applicationName', {}) \
|
|
.get('enum', [])
|
|
for dyn_choice in dyn_choices:
|
|
if dyn_choice.replace('_', '') not in REPORT_CHOICE_MAP and \
|
|
dyn_choice not in REPORT_CHOICE_MAP.values():
|
|
REPORT_CHOICE_MAP[dyn_choice.replace('_', '')] = dyn_choice
|
|
report = _getMain().getChoice(REPORT_CHOICE_MAP, choiceAliases=REPORT_ALIASES_CHOICE_MAP, mapChoice=True)
|
|
if report == 'usage':
|
|
doReportUsage()
|
|
return
|
|
if report == 'usageparameters':
|
|
doReportUsageParameters()
|
|
return
|
|
customerId = GC.Values[GC.CUSTOMER_ID]
|
|
if customerId == GC.MY_CUSTOMER:
|
|
customerId = None
|
|
csvPF = _getMain().CSVPrintFile()
|
|
filters = actorIpAddress = orgUnit = orgUnitId = None
|
|
showOrgUnit = False
|
|
parameters = set()
|
|
parameterServices = set()
|
|
eventCounts = {}
|
|
eventNames = []
|
|
startEndTime = _getMain().StartEndTime('start', 'end')
|
|
filterTimes = {}
|
|
maxActivities = maxEvents = 0
|
|
maxResults = 1000
|
|
aggregateByDate = aggregateByUser = convertMbToGb = countsOnly = countsByDate = countsSummary = \
|
|
eventRowFilter = exitUserLoop = noAuthorizedApps = normalizeUsers = select = userCustomerRange = False
|
|
sortAllTimes = True
|
|
limitDateChanges = -1
|
|
allVerifyUser = userKey = 'all'
|
|
cd = orgUnit = orgUnitId = None
|
|
userOrgUnits = {}
|
|
gmailEventTypes = set()
|
|
customerReports = userReports = False
|
|
if report == 'customer':
|
|
customerReports = True
|
|
service = rep.customerUsageReports()
|
|
fullDataServices = CUSTOMER_REPORT_SERVICES
|
|
elif report == 'user':
|
|
userReports = True
|
|
service = rep.userUsageReport()
|
|
fullDataServices = USER_REPORT_SERVICES
|
|
else:
|
|
service = rep.activities()
|
|
usageReports = customerReports or userReports
|
|
activityReports = not usageReports
|
|
dataRequiredServices = set()
|
|
addCSVData = {}
|
|
mapAdminUsersToFilter = True
|
|
showNoActivities = False
|
|
if usageReports:
|
|
includeServices = set()
|
|
kwargs = {}
|
|
while Cmd.ArgumentsRemaining():
|
|
myarg = _getMain().getArgument()
|
|
if myarg == 'todrive':
|
|
csvPF.GetTodriveParameters()
|
|
elif myarg in {'range', 'thismonth', 'previousmonths'}:
|
|
startEndTime.Get(myarg)
|
|
userCustomerRange = True
|
|
elif myarg in {'ou', 'org', 'orgunit'}:
|
|
if cd is None:
|
|
cd = _getMain().buildGAPIObject(API.DIRECTORY)
|
|
orgUnit, orgUnitId = _getMain().getOrgUnitId(cd)
|
|
select = False
|
|
elif myarg == 'showorgunit':
|
|
showOrgUnit = True
|
|
elif usageReports and myarg in {'date', 'yesterday', 'today'}:
|
|
startEndTime.Get('start' if myarg == 'date' else myarg)
|
|
startEndTime.endDateTime = startEndTime.startDateTime
|
|
userCustomerRange = False
|
|
elif usageReports and myarg in {'nodatechange', 'limitdatechanges'}:
|
|
if myarg == 'nodatechange':
|
|
limitDateChanges = 0
|
|
else:
|
|
limitDateChanges = _getMain().getInteger(minVal=-1)
|
|
if (limitDateChanges == 0) and (startEndTime.startDateTime is not None) and (startEndTime.endDateTime == startEndTime.startDateTime):
|
|
userCustomerRange = True
|
|
elif usageReports and myarg in {'fields', 'parameters'}:
|
|
for field in _getMain().getString(Cmd.OB_STRING).replace(',', ' ').split():
|
|
if ':' in field:
|
|
repsvc, _ = field.split(':', 1)
|
|
if repsvc in fullDataServices:
|
|
parameters.add(field)
|
|
parameterServices.add(repsvc)
|
|
includeServices.add(repsvc)
|
|
else:
|
|
_getMain().invalidChoiceExit(repsvc, fullDataServices, True)
|
|
else:
|
|
Cmd.Backup()
|
|
_getMain().invalidArgumentExit('service:parameter')
|
|
elif usageReports and myarg == 'fulldatarequired':
|
|
fdr = _getMain().getString(Cmd.OB_SERVICE_NAME_LIST, minLen=0).lower()
|
|
if fdr:
|
|
if fdr != 'all':
|
|
for repsvc in fdr.replace(',', ' ').split():
|
|
if repsvc in fullDataServices:
|
|
dataRequiredServices.add(repsvc)
|
|
else:
|
|
_getMain().invalidChoiceExit(repsvc, fullDataServices, True)
|
|
else:
|
|
dataRequiredServices = fullDataServices
|
|
elif usageReports and myarg in {'service', 'services'}:
|
|
for repsvc in _getMain().getString(Cmd.OB_SERVICE_NAME_LIST).lower().replace(',', ' ').split():
|
|
if repsvc in fullDataServices:
|
|
parameterServices.add(repsvc)
|
|
includeServices.add(repsvc)
|
|
else:
|
|
_getMain().invalidChoiceExit(repsvc, fullDataServices, True)
|
|
elif usageReports and myarg == 'convertmbtogb':
|
|
convertMbToGb = True
|
|
elif customerReports and myarg == 'noauthorizedapps':
|
|
noAuthorizedApps = True
|
|
elif activityReports and myarg == 'maxactivities':
|
|
maxActivities = _getMain().getInteger(minVal=0)
|
|
elif activityReports and myarg == 'maxevents':
|
|
maxEvents = _getMain().getInteger(minVal=0)
|
|
elif activityReports and myarg in {'start', 'starttime', 'end', 'endtime', 'yesterday', 'today'}:
|
|
startEndTime.Get(myarg)
|
|
elif activityReports and myarg in {'event', 'events'}:
|
|
for event in _getMain().getString(Cmd.OB_EVENT_NAME_LIST).replace(',', ' ').split():
|
|
event = event.lower() if report not in REPORT_ACTIVITIES_UPPERCASE_EVENTS else event.upper()
|
|
if event not in eventNames:
|
|
eventNames.append(event)
|
|
elif activityReports and myarg == 'ip':
|
|
actorIpAddress = _getMain().getString(Cmd.OB_STRING)
|
|
elif activityReports and myarg == 'notimesort':
|
|
sortAllTimes = False
|
|
elif activityReports and myarg == 'countsonly':
|
|
countsOnly = True
|
|
elif activityReports and myarg == 'bydate':
|
|
countsByDate = True
|
|
elif activityReports and myarg == 'summary':
|
|
countsSummary = True
|
|
elif activityReports and myarg == 'eventrowfilter':
|
|
eventRowFilter = True
|
|
elif activityReports and _getActivitiesFilters(myarg):
|
|
pass
|
|
elif activityReports and (report == 'gmail') and myarg == 'gmaileventtypes':
|
|
gmailEventTypes = set(_getMain().getNumberRangeList())
|
|
elif activityReports and myarg == 'userisactor':
|
|
mapAdminUsersToFilter = False
|
|
elif activityReports and myarg == 'includesensitivedata':
|
|
kwargs['includeSensitiveData'] = True
|
|
elif myarg == 'addcsvdata':
|
|
_getMain().getAddCSVData(addCSVData)
|
|
elif activityReports and myarg == 'shownoactivities':
|
|
showNoActivities = True
|
|
elif not customerReports and myarg.startswith('filtertime'):
|
|
filterTimes[myarg] = _getMain().getTimeOrDeltaFromNow()
|
|
elif not customerReports and myarg in {'filter', 'filters'}:
|
|
filters = _getMain().getString(Cmd.OB_STRING)
|
|
elif not customerReports and myarg == 'maxresults':
|
|
maxResults = _getMain().getInteger(minVal=1, maxVal=1000)
|
|
elif not customerReports and myarg == 'user':
|
|
userKey = _getMain().getString(Cmd.OB_EMAIL_ADDRESS)
|
|
orgUnit = orgUnitId = None
|
|
select = False
|
|
elif not customerReports and myarg == 'select':
|
|
_, users = _getMain().getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)
|
|
orgUnit = orgUnitId = None
|
|
select = True
|
|
elif userReports and myarg == 'aggregatebydate':
|
|
aggregateByDate = _getMain().getBoolean()
|
|
elif userReports and myarg == 'aggregatebyuser':
|
|
aggregateByUser = _getMain().getBoolean()
|
|
elif userReports and myarg == 'allverifyuser':
|
|
allVerifyUser = _getMain().getEmailAddress()
|
|
else:
|
|
_getMain().unknownArgumentExit()
|
|
if aggregateByDate and aggregateByUser:
|
|
_getMain().usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format('aggregateByDate', 'aggregateByUser'))
|
|
if countsOnly and countsByDate and countsSummary:
|
|
_getMain().usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format('bydate', 'summary'))
|
|
parameters = ','.join(parameters) if parameters else None
|
|
if usageReports and not includeServices:
|
|
includeServices = set(fullDataServices)
|
|
if filterTimes and filters is not None:
|
|
for filterTimeName, filterTimeValue in filterTimes.items():
|
|
filters = filters.replace(f'#{filterTimeName}#', filterTimeValue)
|
|
if not orgUnitId:
|
|
showOrgUnit = False
|
|
if userReports:
|
|
if startEndTime.startDateTime is None:
|
|
startEndTime.startDateTime = startEndTime.endDateTime = _getMain().todaysDate()
|
|
if select:
|
|
normalizeUsers = True
|
|
orgUnitId = None
|
|
Ent.SetGetting(Ent.REPORT)
|
|
elif userKey == 'all':
|
|
if orgUnitId:
|
|
if showOrgUnit:
|
|
userOrgUnits = getUserOrgUnits(cd, orgUnit, orgUnitId)
|
|
forWhom = f'users in orgUnit {orgUnit}'
|
|
else:
|
|
forWhom = 'all users'
|
|
_getMain().printGettingEntityItemForWhom(Ent.REPORT, forWhom)
|
|
users = ['all']
|
|
else:
|
|
Ent.SetGetting(Ent.REPORT)
|
|
users = [_getMain().normalizeEmailAddressOrUID(userKey)]
|
|
orgUnitId = None
|
|
if aggregateByDate:
|
|
titles = ['date']
|
|
elif aggregateByUser:
|
|
titles = ['email'] if not showOrgUnit else ['email', 'orgUnitPath']
|
|
else:
|
|
titles = ['email', 'date'] if not showOrgUnit else ['email', 'orgUnitPath', 'date']
|
|
if addCSVData:
|
|
titles.extend(sorted(addCSVData.keys()))
|
|
csvPF.SetTitles(titles)
|
|
csvPF.SetSortAllTitles()
|
|
i = 0
|
|
count = len(users)
|
|
for user in users:
|
|
i += 1
|
|
if normalizeUsers:
|
|
user = _getMain().normalizeEmailAddressOrUID(user)
|
|
if user != 'all':
|
|
_getMain().printGettingEntityItemForWhom(Ent.REPORT, user, i, count)
|
|
verifyUser = user
|
|
else:
|
|
verifyUser = allVerifyUser
|
|
startDateTime = startEndTime.startDateTime
|
|
endDateTime = startEndTime.endDateTime
|
|
lastDate = None
|
|
numDateChanges = 0
|
|
while startDateTime <= endDateTime:
|
|
tryDate = startDateTime.strftime(_getMain().YYYYMMDD_FORMAT)
|
|
try:
|
|
if not userCustomerRange:
|
|
result = _getMain().callGAPI(service, 'get',
|
|
throwReasons=[GAPI.INVALID, GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.FORBIDDEN],
|
|
userKey=verifyUser, date=tryDate, customerId=customerId,
|
|
orgUnitID=orgUnitId, parameters=parameters,
|
|
fields='warnings,usageReports', maxResults=1)
|
|
prevTryDate = tryDate
|
|
fullData, tryDate, usageReports = _checkDataRequiredServices(result, tryDate,
|
|
dataRequiredServices, parameterServices, True)
|
|
if fullData < 0:
|
|
_getMain().printWarningMessage(_getMain().DATA_NOT_AVALIABLE_RC, Msg.NO_REPORT_AVAILABLE.format(report))
|
|
break
|
|
numDateChanges += 1
|
|
if fullData == 0:
|
|
if numDateChanges > limitDateChanges >= 0:
|
|
break
|
|
startDateTime = endDateTime = arrow.Arrow.strptime(tryDate, _getMain().YYYYMMDD_FORMAT)
|
|
continue
|
|
if not select and userKey == 'all':
|
|
pageMessage = _getMain().getPageMessageForWhom(forWhom, showDate=tryDate)
|
|
else:
|
|
pageMessage = _getMain().getPageMessageForWhom(user, showDate=tryDate)
|
|
prevTryDate = tryDate
|
|
usage = _getMain().callGAPIpages(service, 'get', 'usageReports',
|
|
pageMessage=pageMessage,
|
|
throwReasons=[GAPI.INVALID, GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.FORBIDDEN],
|
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
userKey=user, date=tryDate, customerId=customerId,
|
|
orgUnitID=orgUnitId, filters=filters, parameters=parameters,
|
|
maxResults=maxResults)
|
|
if aggregateByDate:
|
|
status, lastDate = processAggregateUserUsageByDate(usage, lastDate)
|
|
elif aggregateByUser:
|
|
status, lastDate = processAggregateUserUsageByUser(usage, lastDate)
|
|
else:
|
|
status, lastDate = processUserUsage(usage, lastDate)
|
|
if not status:
|
|
break
|
|
except GAPI.invalid as e:
|
|
if userCustomerRange:
|
|
break
|
|
numDateChanges += 1
|
|
tryDate = _adjustTryDate(str(e), numDateChanges, limitDateChanges, tryDate)
|
|
if not tryDate:
|
|
break
|
|
startDateTime = endDateTime = arrow.Arrow.strptime(tryDate, _getMain().YYYYMMDD_FORMAT)
|
|
continue
|
|
except GAPI.invalidInput as e:
|
|
_getMain().systemErrorExit(_getMain().GOOGLE_API_ERROR_RC, str(e))
|
|
except GAPI.badRequest:
|
|
if user != 'all':
|
|
_getMain().entityUnknownWarning(Ent.USER, user, i, count)
|
|
else:
|
|
_getMain().printErrorMessage(_getMain().BAD_REQUEST_RC, Msg.BAD_REQUEST)
|
|
exitUserLoop = True
|
|
break
|
|
except GAPI.forbidden as e:
|
|
_getMain().accessErrorExit(None, str(e))
|
|
startDateTime = startDateTime.shift(days=1)
|
|
if exitUserLoop:
|
|
break
|
|
if user != 'all' and lastDate is None and GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
|
|
csvPF.WriteRowNoFilter({'date': prevTryDate, 'email': user})
|
|
if aggregateByDate:
|
|
for usageDate, events in eventCounts.items():
|
|
row = {'date': usageDate}
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
if 'accounts:used_quota_in_percentage' in events:
|
|
_computeUsedQuotaInPercentage(events)
|
|
for event, count in events.items():
|
|
if convertMbToGb and event.endswith('_in_gb'):
|
|
count = f'{count/1024:.2f}'
|
|
row[event] = count
|
|
csvPF.WriteRow(row)
|
|
csvPF.SortRows('date', False)
|
|
csvPF.writeCSVfile(f'User Reports Aggregate - {tryDate}')
|
|
elif aggregateByUser:
|
|
for email, events in eventCounts.items():
|
|
row = {'email': email}
|
|
if showOrgUnit:
|
|
row['orgUnitPath'] = userOrgUnits.get(email, _getMain().UNKNOWN)
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
if 'accounts:used_quota_in_percentage' in events:
|
|
_computeUsedQuotaInPercentage(events)
|
|
for event, count in events.items():
|
|
if convertMbToGb and event.endswith('_in_gb'):
|
|
count = f'{count/1024:.2f}'
|
|
row[event] = count
|
|
csvPF.WriteRow(row)
|
|
csvPF.SortRows('email', False)
|
|
csvPF.writeCSVfile('User Reports Aggregate - User')
|
|
else:
|
|
csvPF.SortRowsTwoTitles('email', 'date', False)
|
|
csvPF.writeCSVfile(f'User Reports - {tryDate}')
|
|
elif customerReports:
|
|
if startEndTime.startDateTime is None:
|
|
startEndTime.startDateTime = startEndTime.endDateTime = _getMain().todaysDate()
|
|
csvPF.SetTitles('date')
|
|
if addCSVData:
|
|
csvPF.AddTitles(sorted(addCSVData.keys()))
|
|
if not userCustomerRange or (startEndTime.startDateTime == startEndTime.endDateTime):
|
|
csvPF.AddTitles(['name', 'value'])
|
|
authorizedApps = []
|
|
startDateTime = startEndTime.startDateTime
|
|
endDateTime = startEndTime.endDateTime
|
|
lastDate = None
|
|
numDateChanges = 0
|
|
while startDateTime <= endDateTime:
|
|
tryDate = startDateTime.strftime(_getMain().YYYYMMDD_FORMAT)
|
|
try:
|
|
if not userCustomerRange:
|
|
result = _getMain().callGAPI(service, 'get',
|
|
throwReasons=[GAPI.INVALID, GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.FORBIDDEN],
|
|
date=tryDate, customerId=customerId, parameters=parameters, fields='warnings,usageReports')
|
|
fullData, tryDate, usageReports = _checkDataRequiredServices(result, tryDate,
|
|
dataRequiredServices, parameterServices)
|
|
if fullData < 0:
|
|
_getMain().printWarningMessage(_getMain().DATA_NOT_AVALIABLE_RC, Msg.NO_REPORT_AVAILABLE.format(report))
|
|
break
|
|
numDateChanges += 1
|
|
if fullData == 0:
|
|
if numDateChanges > limitDateChanges >= 0:
|
|
break
|
|
startDateTime = endDateTime = arrow.Arrow.strptime(tryDate, _getMain().YYYYMMDD_FORMAT)
|
|
continue
|
|
usage = _getMain().callGAPIpages(service, 'get', 'usageReports',
|
|
throwReasons=[GAPI.INVALID, GAPI.INVALID_INPUT, GAPI.FORBIDDEN],
|
|
date=tryDate, customerId=customerId, parameters=parameters)
|
|
if not userCustomerRange or (startEndTime.startDateTime == startEndTime.endDateTime):
|
|
status, lastDate = processCustomerUsage(usage, lastDate)
|
|
else:
|
|
status, lastDate = processCustomerUsageOneRow(usage, lastDate)
|
|
if not status:
|
|
break
|
|
except GAPI.invalid as e:
|
|
if userCustomerRange:
|
|
break
|
|
numDateChanges += 1
|
|
tryDate = _adjustTryDate(str(e), numDateChanges, limitDateChanges, tryDate)
|
|
if not tryDate:
|
|
break
|
|
startDateTime = endDateTime = arrow.Arrow.strptime(tryDate, _getMain().YYYYMMDD_FORMAT)
|
|
continue
|
|
except GAPI.invalidInput as e:
|
|
_getMain().systemErrorExit(_getMain().GOOGLE_API_ERROR_RC, str(e))
|
|
except GAPI.forbidden as e:
|
|
_getMain().accessErrorExit(None, str(e))
|
|
startDateTime = startDateTime.shift(days=1)
|
|
csvPF.writeCSVfile(f'Customer Report - {tryDate}')
|
|
else: # activityReports
|
|
csvPF.SetTitles('name')
|
|
if addCSVData:
|
|
csvPF.AddTitles(sorted(addCSVData.keys()))
|
|
csvPF.SetSortAllTitles()
|
|
if select:
|
|
pageMessage = _getMain().getPageMessage()
|
|
normalizeUsers = True
|
|
orgUnitId = None
|
|
elif userKey == 'all':
|
|
if orgUnitId:
|
|
if showOrgUnit:
|
|
userOrgUnits = getUserOrgUnits(cd, orgUnit, orgUnitId)
|
|
_getMain().printGettingAllEntityItemsForWhom(Ent.ACTIVITY, f'users in orgUnit {orgUnit}', query=filters)
|
|
else:
|
|
_getMain().printGettingAllEntityItemsForWhom(Ent.ACTIVITY, 'all users', query=filters)
|
|
pageMessage = _getMain().getPageMessage()
|
|
users = ['all']
|
|
else:
|
|
Ent.SetGetting(Ent.ACTIVITY)
|
|
pageMessage = _getMain().getPageMessage()
|
|
users = [_getMain().normalizeEmailAddressOrUID(userKey)]
|
|
orgUnitId = None
|
|
zeroEventCounts = {}
|
|
if not eventNames:
|
|
eventNames.append(None)
|
|
else:
|
|
for eventName in eventNames:
|
|
zeroEventCounts[eventName] = 0
|
|
mapUsersToFilter = False
|
|
count = len(users)
|
|
# 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 = _getMain().todaysDate()
|
|
startEndTime.endTime = _getMain().ISOformatTimeStamp(startEndTime.endDateTime)
|
|
startEndTime.startDateTime = startEndTime.endDateTime.shift(days=-30)
|
|
startEndTime.startTime = _getMain().ISOformatTimeStamp(startEndTime.startDateTime)
|
|
elif startEndTime.endTime is None:
|
|
startEndTime.endDateTime = startEndTime.startDateTime.shift(days=30)
|
|
startEndTime.endTime = _getMain().ISOformatTimeStamp(startEndTime.endDateTime)
|
|
# admin uses userKey for who executed the command, map user to filter EMAIL_USER==user
|
|
# unless userisactor was specified
|
|
elif report == 'admin':
|
|
if mapAdminUsersToFilter:
|
|
if select or count != 1 or users[0] != 'all':
|
|
if filters is None:
|
|
mapUsersToFilter = True
|
|
filters = 'USER_EMAIL==#user#'
|
|
elif 'USER_EMAIL==' not in filters:
|
|
mapUsersToFilter = True
|
|
filters = filters+',USER_EMAIL==#user#'
|
|
# chrome does not use userKey, map user to filter DEVICE_USER==user
|
|
elif report == 'chrome':
|
|
if select or count != 1 or users[0] != 'all':
|
|
if filters is None:
|
|
mapUsersToFilter = True
|
|
filters = 'DEVICE_USER==#user#'
|
|
elif 'DEVICE_USER==' not in filters:
|
|
mapUsersToFilter = True
|
|
filters = filters+',DEVICE_USER==#user#'
|
|
i = 0
|
|
for user in users:
|
|
i += 1
|
|
if normalizeUsers:
|
|
user = _getMain().normalizeEmailAddressOrUID(user)
|
|
pfilters = filters
|
|
if select or user != 'all':
|
|
puser = user
|
|
if mapUsersToFilter:
|
|
pfilters = filters.replace('#user#', user)
|
|
user = 'all'
|
|
_getMain().printGettingAllEntityItemsForWhom(Ent.ACTIVITY, puser, i, count, query=pfilters)
|
|
for eventName in eventNames:
|
|
try:
|
|
feed = _getMain().callGAPIpages(service, 'list', 'items',
|
|
pageMessage=pageMessage, maxItems=maxActivities,
|
|
throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID, GAPI.INVALID_INPUT, GAPI.AUTH_ERROR, GAPI.SERVICE_NOT_AVAILABLE],
|
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
applicationName=report, userKey=user, customerId=customerId,
|
|
actorIpAddress=actorIpAddress, orgUnitID=orgUnitId,
|
|
startTime=startEndTime.startTime, endTime=startEndTime.endTime,
|
|
eventName=eventName, filters=pfilters, maxResults=maxResults, **kwargs)
|
|
except GAPI.badRequest:
|
|
if user != 'all':
|
|
_getMain().entityUnknownWarning(Ent.USER, user, i, count)
|
|
continue
|
|
_getMain().printErrorMessage(_getMain().BAD_REQUEST_RC, Msg.BAD_REQUEST)
|
|
break
|
|
except (GAPI.invalid, GAPI.invalidInput, GAPI.serviceNotAvailable) as e:
|
|
_getMain().systemErrorExit(_getMain().GOOGLE_API_ERROR_RC, str(e))
|
|
except GAPI.authError:
|
|
_getMain().accessErrorExit(None)
|
|
for activity in feed:
|
|
events = activity.pop('events')
|
|
actor = activity['actor'].get('email')
|
|
if not actor:
|
|
actor = 'id:'+activity['actor'].get('profileId', _getMain().UNKNOWN)
|
|
if showOrgUnit:
|
|
activity['actor']['orgUnitPath'] = userOrgUnits.get(actor, _getMain().UNKNOWN)
|
|
if countsOnly and countsByDate:
|
|
eventTime = activity.get('id', {}).get('time', _getMain().UNKNOWN)
|
|
if eventTime != _getMain().UNKNOWN:
|
|
try:
|
|
eventTime = arrow.get(eventTime)
|
|
except (arrow.parser.ParserError, OverflowError):
|
|
eventTime = _getMain().UNKNOWN
|
|
if eventTime != _getMain().UNKNOWN:
|
|
eventDate = eventTime.strftime(_getMain().YYYYMMDD_FORMAT)
|
|
else:
|
|
eventDate = _getMain().UNKNOWN
|
|
if not countsOnly or eventRowFilter:
|
|
activity_row = _getMain().flattenJSON(activity, timeObjects=REPORT_ACTIVITIES_TIME_OBJECTS)
|
|
purge_parameters = True
|
|
numEvents = 0
|
|
for event in events:
|
|
# filter gmail event types
|
|
if gmailEventTypes:
|
|
keepEvent = True
|
|
for item in event.get('parameters', []):
|
|
if item['name'] == 'event_info':
|
|
for parm in item.get('messageValue', {}).get('parameter', []):
|
|
if parm['name'] == 'mail_event_type':
|
|
if int(parm['intValue']) not in gmailEventTypes:
|
|
keepEvent = False
|
|
break
|
|
break
|
|
if not keepEvent:
|
|
continue
|
|
numEvents += 1
|
|
for item in event.get('parameters', []):
|
|
itemSet = set(item)
|
|
if not itemSet.symmetric_difference({'name'}):
|
|
event[item['name']] = ''
|
|
elif not itemSet.symmetric_difference({'value', 'name'}):
|
|
event[item['name']] = NL_SPACES_PATTERN.sub('', item['value'])
|
|
elif not itemSet.symmetric_difference({'intValue', 'name'}):
|
|
if item['name'] in {'start_time', 'end_time'}:
|
|
val = item.get('intValue')
|
|
if val is not None:
|
|
val = int(val)
|
|
if val >= 62135683200:
|
|
event[item['name']] = _getMain().ISOformatTimeStamp(arrow.Arrow.fromtimestamp(val-62135683200, GC.Values[GC.TIMEZONE]))
|
|
else:
|
|
event[item['name']] = val
|
|
else:
|
|
event[item['name']] = item['intValue']
|
|
elif not itemSet.symmetric_difference({'boolValue', 'name'}):
|
|
event[item['name']] = item['boolValue']
|
|
elif not itemSet.symmetric_difference({'multiValue', 'name'}):
|
|
event[item['name']] = ' '.join(item['multiValue'])
|
|
elif item['name'] == 'scope_data':
|
|
parts = {}
|
|
for message in item['multiMessageValue']:
|
|
for mess in message['parameter']:
|
|
value = mess.get('value', ' '.join(mess.get('multiValue', [])))
|
|
parts[mess['name']] = parts.get(mess['name'], [])+[value]
|
|
for part, v in parts.items():
|
|
if part == 'scope_name':
|
|
part = 'scope'
|
|
event[part] = ' '.join(v)
|
|
else:
|
|
purge_parameters = False
|
|
if purge_parameters:
|
|
event.pop('parameters', None)
|
|
row = _getMain().flattenJSON(event)
|
|
row.update(activity_row)
|
|
if not countsOnly:
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
csvPF.WriteRowTitles(row)
|
|
if numEvents >= maxEvents > 0:
|
|
break
|
|
elif csvPF.CheckRowTitles(row):
|
|
eventName = event['name']
|
|
if not countsSummary:
|
|
eventCounts.setdefault(actor, {})
|
|
if not countsByDate:
|
|
eventCounts[actor].setdefault(eventName, 0)
|
|
eventCounts[actor][eventName] += 1
|
|
else:
|
|
eventCounts[actor].setdefault(eventDate, {})
|
|
eventCounts[actor][eventDate].setdefault(eventName, 0)
|
|
eventCounts[actor][eventDate][eventName] += 1
|
|
else:
|
|
eventCounts.setdefault(eventName, 0)
|
|
eventCounts[eventName] += 1
|
|
elif not countsSummary:
|
|
eventCounts.setdefault(actor, {})
|
|
if not countsByDate:
|
|
for event in events:
|
|
eventName = event['name']
|
|
eventCounts[actor].setdefault(eventName, 0)
|
|
eventCounts[actor][eventName] += 1
|
|
else:
|
|
for event in events:
|
|
eventName = event['name']
|
|
eventCounts[actor].setdefault(eventDate, {})
|
|
eventCounts[actor][eventDate].setdefault(eventName, 0)
|
|
eventCounts[actor][eventDate][eventName] += 1
|
|
else:
|
|
for event in events:
|
|
eventCounts.setdefault(event['name'], 0)
|
|
eventCounts[event['name']] += 1
|
|
if not countsOnly:
|
|
if not csvPF.rows and showNoActivities:
|
|
row = {'name': 'NoActivities'}
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
csvPF.WriteRowTitles(row)
|
|
elif sortAllTimes:
|
|
csvPF.SortRows('id.time', True)
|
|
else:
|
|
if eventRowFilter:
|
|
csvPF.SetRowFilter([], GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE])
|
|
csvPF.SetRowDropFilter([], GC.Values[GC.CSV_OUTPUT_ROW_DROP_FILTER_MODE])
|
|
if not countsSummary:
|
|
titles = ['emailAddress']
|
|
if countsOnly and countsByDate:
|
|
titles.append('date')
|
|
csvPF.SetTitles(titles)
|
|
csvPF.SetSortTitles(titles)
|
|
if addCSVData:
|
|
csvPF.AddTitles(sorted(addCSVData.keys()))
|
|
if eventCounts:
|
|
if not countsByDate:
|
|
for actor, events in eventCounts.items():
|
|
row = {'emailAddress': actor}
|
|
row.update(zeroEventCounts)
|
|
for event, count in events.items():
|
|
row[event] = count
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
csvPF.WriteRowTitles(row)
|
|
else:
|
|
for actor, eventDates in eventCounts.items():
|
|
for eventDate, events in eventDates.items():
|
|
row = {'emailAddress': actor, 'date': eventDate}
|
|
row.update(zeroEventCounts)
|
|
for event, count in events.items():
|
|
row[event] = count
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
csvPF.WriteRowTitles(row)
|
|
elif showNoActivities:
|
|
row = {'emailAddress': 'NoActivities'}
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
csvPF.WriteRow(row)
|
|
else:
|
|
csvPF.SetTitles(['event', 'count'])
|
|
if addCSVData:
|
|
csvPF.AddTitles(sorted(addCSVData.keys()))
|
|
if eventCounts:
|
|
for event, count in sorted(eventCounts.items()):
|
|
row = {'event': event, 'count': count}
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
csvPF.WriteRow(row)
|
|
elif showNoActivities:
|
|
row = {'event': 'NoActivities', 'count': 0}
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
csvPF.WriteRow(row)
|
|
csvPF.writeCSVfile(f'{report.capitalize()} Activity Report', eventRowFilter)
|
|
|
|
# Substitute for #user#, #email#, #usernamne#
|