def showUsageParameters(): rep = buildGAPIObject() throw_reasons = [gapi.errors.ErrorReason.INVALID, gapi.errors.ErrorReason.BAD_REQUEST] report = sys.argv[3].lower() if report == 'customer': endpoint = rep.customerUsageReports() kwargs = {} elif report == 'user': endpoint = rep.userUsageReport() kwargs = {'userKey': __main__._getValueFromOAuth('email')} else: controlflow.expected_argument_exit( 'usageparameters', ['user', 'customer'], report) customerId = GC_Values[GC_CUSTOMER_ID] if customerId == MY_CUSTOMER: customerId = None tryDate = datetime.date.today().strftime(YYYYMMDD_FORMAT) partial_apps = False all_parameters = [] one_day = datetime.timedelta(days=1) while True: try: response = gapi.call(endpoint, 'get', throw_reasons=throw_reasons, date=tryDate, customerId=customerId, **kwargs) partial_on_thisday = [] for warning in response.get('warnings', []): for data in warning.get('data', []): if data.get('key') == 'application': partial_on_thisday.append(data['value']) if partial_apps: partial_apps = [app for app in partial_apps if app in partial_on_thisday] else: partial_apps = partial_on_thisday for parameter in response['usageReports'][0]['parameters']: name = parameter.get('name') if name and name not in all_parameters: all_parameters.append(name) if not partial_apps: break tryDate = (utils.get_yyyymmdd(tryDate, returnDateTime=True) - \ one_day).strftime(YYYYMMDD_FORMAT) except gapi.errors.GapiInvalidError as e: tryDate = _adjust_date(str(e)) all_parameters.sort() for parameter in all_parameters: print(parameter)
def showReport(): rep = buildGAPIObject() throw_reasons = [gapi.errors.ErrorReason.INVALID] report = sys.argv[2].lower() report = REPORT_CHOICE_MAP.get(report.replace('_', ''), report) valid_apps = gapi.get_enum_values_minus_unspecified( rep._rootDesc['resources']['activities']['methods']['list'] ['parameters']['applicationName']['enum']) + ['customer', 'user'] if report not in valid_apps: controlflow.expected_argument_exit("report", ", ".join(sorted(valid_apps)), report) customerId = GC_Values[GC_CUSTOMER_ID] if customerId == MY_CUSTOMER: customerId = None filters = parameters = actorIpAddress = startTime = endTime = eventName = orgUnitId = None tryDate = datetime.date.today().strftime(YYYYMMDD_FORMAT) to_drive = False userKey = 'all' fullDataRequired = None i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower() if myarg == 'date': tryDate = utils.get_yyyymmdd(sys.argv[i + 1]) i += 2 elif myarg in ['orgunit', 'org', 'ou']: _, orgUnitId = __main__.getOrgUnitId(sys.argv[i + 1]) i += 2 elif myarg == 'fulldatarequired': fullDataRequired = [] fdr = sys.argv[i + 1].lower() if fdr and fdr != 'all': fullDataRequired = fdr.replace(',', ' ').split() i += 2 elif myarg == 'start': startTime = utils.get_time_or_delta_from_now(sys.argv[i + 1]) i += 2 elif myarg == 'end': endTime = utils.get_time_or_delta_from_now(sys.argv[i + 1]) i += 2 elif myarg == 'event': eventName = sys.argv[i + 1] i += 2 elif myarg == 'user': userKey = __main__.normalizeEmailAddressOrUID(sys.argv[i + 1]) i += 2 elif myarg in ['filter', 'filters']: filters = sys.argv[i + 1] i += 2 elif myarg in ['fields', 'parameters']: parameters = sys.argv[i + 1] i += 2 elif myarg == 'ip': actorIpAddress = sys.argv[i + 1] i += 2 elif myarg == 'todrive': to_drive = True i += 1 else: controlflow.invalid_argument_exit(sys.argv[i], "gam report") if report == 'user': while True: try: if fullDataRequired is not None: warnings = gapi.get_items(rep.userUsageReport(), 'get', 'warnings', throw_reasons=throw_reasons, date=tryDate, userKey=userKey, customerId=customerId, orgUnitID=orgUnitId, fields='warnings') fullData, tryDate = _check_full_data_available( warnings, tryDate, fullDataRequired) if fullData < 0: print('No user report available.') sys.exit(1) if fullData == 0: continue page_message = gapi.got_total_items_msg('Users', '...\n') usage = gapi.get_all_pages(rep.userUsageReport(), 'get', 'usageReports', page_message=page_message, throw_reasons=throw_reasons, date=tryDate, userKey=userKey, customerId=customerId, orgUnitID=orgUnitId, filters=filters, parameters=parameters) break except gapi.errors.GapiInvalidError as e: tryDate = _adjust_date(str(e)) if not usage: print('No user report available.') sys.exit(1) titles = ['email', 'date'] csvRows = [] ptypes = ['intValue', 'boolValue', 'datetimeValue', 'stringValue'] for user_report in usage: if 'entity' not in user_report: continue row = { 'email': user_report['entity']['userEmail'], 'date': tryDate } for item in user_report.get('parameters', []): if 'name' not in item: continue name = item['name'] if not name in titles: titles.append(name) for ptype in ptypes: if ptype in item: row[name] = item[ptype] break else: row[name] = '' csvRows.append(row) display.write_csv_file(csvRows, titles, f'User Reports - {tryDate}', to_drive) elif report == 'customer': while True: try: if fullDataRequired is not None: warnings = gapi.get_items(rep.customerUsageReports(), 'get', 'warnings', throw_reasons=throw_reasons, customerId=customerId, date=tryDate, fields='warnings') fullData, tryDate = _check_full_data_available( warnings, tryDate, fullDataRequired) if fullData < 0: print('No customer report available.') sys.exit(1) if fullData == 0: continue usage = gapi.get_all_pages(rep.customerUsageReports(), 'get', 'usageReports', throw_reasons=throw_reasons, customerId=customerId, date=tryDate, parameters=parameters) break except gapi.errors.GapiInvalidError as e: tryDate = _adjust_date(str(e)) if not usage: print('No customer report available.') sys.exit(1) titles = ['name', 'value', 'client_id'] csvRows = [] auth_apps = list() for item in usage[0]['parameters']: if 'name' not in item: continue name = item['name'] if 'intValue' in item: value = item['intValue'] elif 'msgValue' in item: if name == 'accounts:authorized_apps': for subitem in item['msgValue']: app = {} for an_item in subitem: if an_item == 'client_name': app['name'] = 'App: ' + \ subitem[an_item].replace('\n', '\\n') elif an_item == 'num_users': app['value'] = f'{subitem[an_item]} users' elif an_item == 'client_id': app['client_id'] = subitem[an_item] auth_apps.append(app) continue values = [] for subitem in item['msgValue']: if 'count' in subitem: mycount = myvalue = None for key, value in list(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"]}:' f'{subitem["num_devices"]}') else: continue value = ' '.join(sorted(values, reverse=True)) csvRows.append({'name': name, 'value': value}) for app in auth_apps: # put apps at bottom csvRows.append(app) display.write_csv_file(csvRows, titles, f'Customer Report - {tryDate}', todrive=to_drive) else: page_message = gapi.got_total_items_msg('Activities', '...\n') activities = gapi.get_all_pages(rep.activities(), 'list', 'items', page_message=page_message, applicationName=report, userKey=userKey, customerId=customerId, actorIpAddress=actorIpAddress, startTime=startTime, endTime=endTime, eventName=eventName, filters=filters, orgUnitID=orgUnitId) if activities: titles = ['name'] csvRows = [] for activity in activities: events = activity['events'] del activity['events'] activity_row = utils.flatten_json(activity) purge_parameters = True for event in events: for item in event.get('parameters', []): if set(item) == set(['value', 'name']): event[item['name']] = item['value'] elif set(item) == set(['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']] = \ datetime.datetime.fromtimestamp( val-62135683200).isoformat() else: event[item['name']] = item['intValue'] elif set(item) == set(['boolValue', 'name']): event[item['name']] = item['boolValue'] elif set(item) == set(['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 = utils.flatten_json(event) row.update(activity_row) for item in row: if item not in titles: titles.append(item) csvRows.append(row) display.sort_csv_titles([ 'name', ], titles) display.write_csv_file(csvRows, titles, f'{report.capitalize()} Activity Report', to_drive)
def getEventAttributes(i, calendarId, cal, body, action): # Default to external only so non-Google # calendars are notified of changes sendUpdates = 'externalOnly' action = 'update' if body else 'add' while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg in ['notifyattendees', 'sendnotifications', 'sendupdates']: sendUpdates, i = getSendUpdates(myarg, i, cal) elif myarg == 'attendee': body.setdefault('attendees', []) body['attendees'].append({'email': sys.argv[i + 1]}) i += 2 elif myarg == 'removeattendee' and action == 'update': remove_email = sys.argv[i + 1].lower() if 'attendees' in body: body['attendees'] = _remove_attendee(body['attendees'], remove_email) i += 2 elif myarg == 'optionalattendee': body.setdefault('attendees', []) body['attendees'].append({ 'email': sys.argv[i + 1], 'optional': True }) i += 2 elif myarg == 'anyonecanaddself': body['anyoneCanAddSelf'] = True i += 1 elif myarg == 'description': body['description'] = sys.argv[i + 1].replace('\\n', '\n') i += 2 elif myarg == 'replacedescription' and action == 'update': search = sys.argv[i + 1] replace = sys.argv[i + 2] if 'description' in body: body['description'] = re.sub(search, replace, body['description']) i += 3 elif myarg == 'start': if sys.argv[i + 1].lower() == 'allday': body['start'] = {'date': utils.get_yyyymmdd(sys.argv[i + 2])} i += 3 else: start_time = utils.get_time_or_delta_from_now(sys.argv[i + 1]) body['start'] = {'dateTime': start_time} i += 2 elif myarg == 'end': if sys.argv[i + 1].lower() == 'allday': body['end'] = {'date': utils.get_yyyymmdd(sys.argv[i + 2])} i += 3 else: end_time = utils.get_time_or_delta_from_now(sys.argv[i + 1]) body['end'] = {'dateTime': end_time} i += 2 elif myarg == 'guestscantinviteothers': body['guestsCanInviteOthers'] = False i += 1 elif myarg == 'guestscaninviteothers': body['guestsCanInviteTohters'] = __main__.getBoolean( sys.argv[i + 1], 'guestscaninviteothers') i += 2 elif myarg == 'guestscantseeothers': body['guestsCanSeeOtherGuests'] = False i += 1 elif myarg == 'guestscanseeothers': body['guestsCanSeeOtherGuests'] = __main__.getBoolean( sys.argv[i + 1], 'guestscanseeothers') i += 2 elif myarg == 'guestscanmodify': body['guestsCanModify'] = __main__.getBoolean( sys.argv[i + 1], 'guestscanmodify') i += 2 elif myarg == 'id': if action == 'update': controlflow.invalid_argument_exit( 'id', 'gam calendar <calendar> updateevent') body['id'] = sys.argv[i + 1] i += 2 elif myarg == 'summary': body['summary'] = sys.argv[i + 1] i += 2 elif myarg == 'location': body['location'] = sys.argv[i + 1] i += 2 elif myarg == 'available': body['transparency'] = 'transparent' i += 1 elif myarg == 'transparency': validTransparency = ['opaque', 'transparent'] if sys.argv[i + 1].lower() in validTransparency: body['transparency'] = sys.argv[i + 1].lower() else: controlflow.expected_argument_exit( 'transparency', ", ".join(validTransparency), sys.argv[i + 1]) i += 2 elif myarg == 'visibility': validVisibility = ['default', 'public', 'private'] if sys.argv[i + 1].lower() in validVisibility: body['visibility'] = sys.argv[i + 1].lower() else: controlflow.expected_argument_exit("visibility", ", ".join(validVisibility), sys.argv[i + 1]) i += 2 elif myarg == 'tentative': body['status'] = 'tentative' i += 1 elif myarg == 'status': validStatus = ['confirmed', 'tentative', 'cancelled'] if sys.argv[i + 1].lower() in validStatus: body['status'] = sys.argv[i + 1].lower() else: controlflow.expected_argument_exit('visibility', ', '.join(validStatus), sys.argv[i + 1]) i += 2 elif myarg == 'source': body['source'] = {'title': sys.argv[i + 1], 'url': sys.argv[i + 2]} i += 3 elif myarg == 'noreminders': body['reminders'] = {'useDefault': False} i += 1 elif myarg == 'reminder': minutes = \ __main__.getInteger(sys.argv[i+1], myarg, minVal=0, maxVal=CALENDAR_REMINDER_MAX_MINUTES) reminder = {'minutes': minutes, 'method': sys.argv[i + 2]} body.setdefault('reminders', { 'overrides': [], 'useDefault': False }) body['reminders']['overrides'].append(reminder) i += 3 elif myarg == 'recurrence': body.setdefault('recurrence', []) body['recurrence'].append(sys.argv[i + 1]) i += 2 elif myarg == 'timezone': timeZone = sys.argv[i + 1] i += 2 elif myarg == 'privateproperty': if 'extendedProperties' not in body: body['extendedProperties'] = {'private': {}, 'shared': {}} body['extendedProperties']['private'][sys.argv[i + 1]] = sys.argv[i + 2] i += 3 elif myarg == 'sharedproperty': if 'extendedProperties' not in body: body['extendedProperties'] = {'private': {}, 'shared': {}} body['extendedProperties']['shared'][sys.argv[i + 1]] = sys.argv[i + 2] i += 3 elif myarg == 'colorindex': body['colorId'] = __main__.getInteger( sys.argv[i + 1], myarg, CALENDAR_EVENT_MIN_COLOR_INDEX, CALENDAR_EVENT_MAX_COLOR_INDEX) i += 2 elif myarg == 'hangoutsmeet': body['conferenceData'] = { 'createRequest': { 'requestId': f'{str(uuid.uuid4())}' } } i += 1 else: controlflow.invalid_argument_exit( sys.argv[i], f'gam calendar <email> {action}event') if ('recurrence' in body) and (('start' in body) or ('end' in body)): if not timeZone: timeZone = gapi.call(cal.calendars(), 'get', calendarId=calendarId, fields='timeZone')['timeZone'] if 'start' in body: body['start']['timeZone'] = timeZone if 'end' in body: body['end']['timeZone'] = timeZone return (sendUpdates, body)
from run_function import FunctionRunner engine = FunctionRunner(LOGGER, dbset, schemaname=ARGS.schemaname, function=ARGS.function) if ARGS.aggregate or ARGS.movedata: con, cursor = try_connection(LOGGER, dbset, autocommit=True) for year in YEARS: for month in YEARS[year]: yyyymm = get_yyyymm(year, month) if ARGS.aggregate: execute_function(function, LOGGER, cursor, dbset, autocommit=True, yyyymm=yyyymm, **kwargs) elif ARGS.movedata: kwargs['startdate'] = get_yyyymmdd(year, month) move_data(yyyymm, LOGGER, cursor, dbset, **kwargs) else: engine.run(year, month, table=ARGS.tablename) #con.close() LOGGER.info('Processing complete, connection to %s database %s closed', dbset['host'], dbset['database'])
def showUsage(): rep = buildGAPIObject() throw_reasons = [gapi.errors.ErrorReason.INVALID, gapi.errors.ErrorReason.BAD_REQUEST] todrive = False report = sys.argv[3].lower() titles = ['date'] if report == 'customer': endpoint = rep.customerUsageReports() kwargs = [{}] elif report == 'user': endpoint = rep.userUsageReport() kwargs = [{'userKey': 'all'}] titles.append('user') else: controlflow.expected_argument_exit( 'usage', ['user', 'customer'], report) customerId = GC_Values[GC_CUSTOMER_ID] if customerId == MY_CUSTOMER: customerId = None parameters = [] filters = None start_date = end_date = orgUnitId = None skip_day_numbers = [] skip_dates = [] i = 4 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'startdate': start_date = parse(sys.argv[i+1]) i += 2 elif myarg == 'enddate': end_date = parse(sys.argv[i+1]) i += 2 elif myarg == 'todrive': todrive = True i += 1 elif myarg in ['orgunit', 'org', 'ou']: if report != 'user': controlflow.invalid_argument_exit(myarg, f'gam usage {report}') _, orgUnitId = __main__.getOrgUnitId(sys.argv[i+1]) i += 2 elif myarg == 'parameters': parameters = sys.argv[i+1].split(',') i += 2 elif myarg == 'skipdates': skips = sys.argv[i+1].split(',') skip_dates = [utils.get_yyyymmdd(d) for d in skips] i += 2 elif myarg == 'skipdaysofweek': skipdaynames = sys.argv[i+1].split(',') dow = [d.lower() for d in calendar.day_abbr] skip_day_numbers = [dow.index(d) for d in skipdaynames if d in dow] i += 2 elif myarg in usergroup_types: if report != 'user': controlflow.invalid_argument_exit(myarg, f'gam usage {report}') entity_type = myarg entity = sys.argv[i+1] users = __main__.getUsersToModify(entity_type, entity) kwargs = [{'userKey': user} for user in users] i += 3 else: controlflow.invalid_argument_exit(sys.argv[i], "gam usage") if not start_date: start_date = datetime.datetime.now() + relativedelta(months=-1) if not end_date: end_date = datetime.datetime.now() if orgUnitId: for i in range(len(kwargs)): kwargs[i-1]['orgUnitID'] = orgUnitId one_day = datetime.timedelta(days=1) usage_on_date = start_date titles.extend(parameters) csvRows = [] vtypes = ['intValue', 'stringValue', 'intValue', 'boolValue', 'datetimeValue'] while usage_on_date <= end_date: use_date = usage_on_date.strftime('%Y-%m-%d') if usage_on_date.weekday() in skip_day_numbers or \ use_date in skip_dates: usage_on_date += one_day continue usage_on_date += one_day try: for kwarg in kwargs: try: usage = gapi.get_all_pages(endpoint, 'get', 'usageReports', throw_reasons=throw_reasons, customerId=customerId, date=use_date, parameters=','.join(parameters), **kwarg) except gapi.errors.GapiBadRequestError: continue for entity in usage: row = {'date': use_date} if 'userEmail' in entity['entity']: row['user'] = entity['entity']['userEmail'] for item in entity['parameters']: if 'name' not in item: continue name = item['name'] if name == 'cros:device_version_distribution': for cros_ver in item['msgValue']: v = cros_ver['version_number'] column_name = f'cros:num_devices_chrome_{v}' if column_name not in titles: titles.append(column_name) row[column_name] = cros_ver['num_devices'] else: for vtype in vtypes: if vtype in item: value = item[vtype] break row[name] = value csvRows.append(row) except gapi.errors.GapiInvalidError: continue display.write_csv_file( csvRows, titles, f'Usage Reports', todrive)
def showUsageParameters(): rep = buildGAPIObject() throw_reasons = [ gapi.errors.ErrorReason.INVALID, gapi.errors.ErrorReason.BAD_REQUEST ] todrive = False if len(sys.argv) == 3: controlflow.missing_argument_exit('user or customer', 'report usageparameters') report = sys.argv[3].lower() titles = ['parameter'] if report == 'customer': endpoint = rep.customerUsageReports() kwargs = {} elif report == 'user': endpoint = rep.userUsageReport() kwargs = {'userKey': __main__._getValueFromOAuth('email')} else: controlflow.expected_argument_exit('usageparameters', ['user', 'customer'], report) customerId = GC_Values[GC_CUSTOMER_ID] if customerId == MY_CUSTOMER: customerId = None tryDate = datetime.date.today().strftime(YYYYMMDD_FORMAT) partial_apps = [] all_parameters = [] one_day = datetime.timedelta(days=1) i = 4 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'todrive': todrive = True i += 1 else: controlflow.invalid_argument_exit(sys.argv[i], "gam report usageparameters") while True: try: response = gapi.call(endpoint, 'get', throw_reasons=throw_reasons, date=tryDate, customerId=customerId, **kwargs) partial_on_thisday = [] for warning in response.get('warnings', []): for data in warning.get('data', []): if data.get('key') == 'application': partial_on_thisday.append(data['value']) if partial_apps: partial_apps = [ app for app in partial_apps if app in partial_on_thisday ] else: partial_apps = partial_on_thisday for parameter in response['usageReports'][0]['parameters']: name = parameter.get('name') if name and name not in all_parameters: all_parameters.append(name) if not partial_apps: break tryDate = (utils.get_yyyymmdd(tryDate, returnDateTime=True) - \ one_day).strftime(YYYYMMDD_FORMAT) except gapi.errors.GapiInvalidError as e: tryDate = _adjust_date(str(e)) all_parameters.sort() csvRows = [] for parameter in all_parameters: csvRows.append({'parameter': parameter}) display.write_csv_file(csvRows, titles, f'{report.capitalize()} Report Usage Parameters', todrive)
def showUsage(): rep = buildGAPIObject() throw_reasons = [ gapi.errors.ErrorReason.INVALID, gapi.errors.ErrorReason.BAD_REQUEST ] todrive = False if len(sys.argv) == 3: controlflow.missing_argument_exit('user or customer', 'report usage') report = sys.argv[3].lower() titles = ['date'] if report == 'customer': endpoint = rep.customerUsageReports() kwargs = [{}] elif report == 'user': endpoint = rep.userUsageReport() kwargs = [{'userKey': 'all'}] titles.append('user') else: controlflow.expected_argument_exit('usage', ['user', 'customer'], report) customerId = GC_Values[GC_CUSTOMER_ID] if customerId == MY_CUSTOMER: customerId = None parameters = [] start_date = end_date = orgUnitId = None skip_day_numbers = [] skip_dates = [] i = 4 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'startdate': start_date = utils.get_yyyymmdd(sys.argv[i + 1], returnDateTime=True) i += 2 elif myarg == 'enddate': end_date = utils.get_yyyymmdd(sys.argv[i + 1], returnDateTime=True) i += 2 elif myarg == 'todrive': todrive = True i += 1 elif myarg in ['fields', 'parameters']: parameters = sys.argv[i + 1].split(',') i += 2 elif myarg == 'skipdates': skips = sys.argv[i + 1].split(',') skip_dates = [utils.get_yyyymmdd(d) for d in skips] i += 2 elif myarg == 'skipdaysofweek': skipdaynames = sys.argv[i + 1].split(',') dow = [d.lower() for d in calendar.day_abbr] skip_day_numbers = [dow.index(d) for d in skipdaynames if d in dow] i += 2 elif report == 'user' and myarg in ['orgunit', 'org', 'ou']: _, orgUnitId = __main__.getOrgUnitId(sys.argv[i + 1]) i += 2 elif report == 'user' and myarg in usergroup_types: users = __main__.getUsersToModify(myarg, sys.argv[i + 1]) kwargs = [{'userKey': user} for user in users] i += 2 else: controlflow.invalid_argument_exit(sys.argv[i], f'gam report usage {report}') if parameters: titles.extend(parameters) parameters = ','.join(parameters) else: parameters = None if not start_date: start_date = datetime.datetime.now() + relativedelta(months=-1) if not end_date: end_date = datetime.datetime.now() if orgUnitId: for kw in kwargs: kw['orgUnitID'] = orgUnitId one_day = datetime.timedelta(days=1) usage_on_date = start_date start_date = usage_on_date.strftime('%Y-%m-%d') csvRows = [] while usage_on_date <= end_date: use_date = usage_on_date.strftime('%Y-%m-%d') if usage_on_date.weekday() in skip_day_numbers or \ use_date in skip_dates: usage_on_date += one_day continue usage_on_date += one_day try: for kwarg in kwargs: try: usage = gapi.get_all_pages(endpoint, 'get', 'usageReports', throw_reasons=throw_reasons, customerId=customerId, date=use_date, parameters=parameters, **kwarg) except gapi.errors.GapiBadRequestError: continue for entity in usage: row = {'date': use_date} if 'userEmail' in entity['entity']: row['user'] = entity['entity']['userEmail'] for item in entity.get('parameters', []): if 'name' not in item: continue name = item['name'] if name == 'cros:device_version_distribution': for cros_ver in item['msgValue']: v = cros_ver['version_number'] column_name = f'cros:num_devices_chrome_{v}' if column_name not in titles: titles.append(column_name) row[column_name] = cros_ver['num_devices'] else: if not name in titles: titles.append(name) for ptype in REPORTS_PARAMETERS_SIMPLE_TYPES: if ptype in item: row[name] = item[ptype] break else: row[name] = '' csvRows.append(row) except gapi.errors.GapiInvalidError as e: display.print_warning(str(e)) break display.write_csv_file( csvRows, titles, f'{report.capitalize()} Usage Report - {start_date}:{use_date}', todrive)